Розробка децентрализованного VPN-сервісу
Централізований VPN розв'язує одну проблему довіри, створюючи іншу: замість ISP ви довіряєте VPN-провайдеру. NordVPN, ExpressVPN, Surfshark — всі вони ведуть логи (незважаючи на декларації), всі вони підкоряються юрисдикційним запитам, всі вони є єдиною точкою відказу. Децентралізований VPN розподіляє цю довіру по мережі незалежних операторів нод так, щоб ніхто з них не міг відновити повний трафік користувача.
Побудувати це правильно складніше, ніж здається. Існуючі протоколи — Sentinel, Mysterium, Orchid — уже вирішили частину завдань, але мають специфічні архітектурні обмеження. Розуміння цих обмежень критично перед початком розробки.
Протокольний рівень: вибір транспорту
Мережевий транспорт — це фундамент, який неможливо змінити після запуску. Основні варіанти:
WireGuard — сучасний, швидкий (<300 рядків коду в ядрі), вбудований у Linux 5.6+. Проблема для dVPN: WireGuard вимагає знання IP обох сторін заздалегідь, немає native підтримки для roaming та динамічних топологій. Конфігурація peer статична. Для dVPN з динамічним набором exit нод — потрібна обгортка для управління конфігурацією.
OpenVPN — зрілий, але повільний і з великим attack surface. Реальних переваг перед WireGuard для нової розробки немає.
V2Ray / Xray з протоколами VLESS/VMESS — розроблені спеціально для DPI bypass (Deep Packet Inspection). Трафік маскується під HTTPS (XTLS). Незамінні якщо у вимогах — робота в країнах з активною блокуванням (Китай, Іран, Росія). Sentinel використовує V2Ray як один з транспортних шарів.
Mixnet підхід (Nym, Tor-style) — пакети перемішуються через кілька нод, додається затримка й dummy трафік для захисту від traffic analysis. Максимальна анонімність, але висока latency (100–500ms overhead). Для звичайного VPN-use-case overkill, для high-privacy приложень — єдиний правильний вибір.
Для більшості dVPN проектів: WireGuard як транспорт + кастомний control plane для управління peers + опціональний V2Ray слой для DPI-obfuscation.
Архітектура privacy: onion routing vs proxy
Проста proxy-модель
Користувач → Exit Node → Інтернет. Exit нода знає IP користувача й видит трафік (якщо не зашифрований HTTPS). Це модель Mysterium і більшості dVPN першого покоління. Захищає від ISP-слежки, не захищає від malicious exit node.
Multi-hop з onion encryption
Користувач → Guard Node → Relay Node → Exit Node → Інтернет. Кожен слой шифрується окремим ключем (як Tor). Guard видит IP користувача, але не знає exit. Exit видит destination, але не знає користувача. Relay не знає ні того, ні другого.
Реалізація через onion encryption:
Encrypted payload:
[
encrypt(
to: guard_pubkey,
payload: {
next_hop: relay_address,
payload: encrypt(
to: relay_pubkey,
payload: {
next_hop: exit_address,
payload: encrypt(
to: exit_pubkey,
payload: { destination: "example.com:443", data: ... }
)
}
)
}
)
]
Кожна нода расшифровует тільки свій слой, видит тільки next hop, перенаправляє далі. Алгоритм: X25519 для key exchange, ChaCha20-Poly1305 для симметрического шифрування — це вибір WireGuard і Signal Protocol.
Вартість multi-hop: latency зростає лінійно з числом хопів (~30–80ms на хоп у межах одного регіону). Для streaming — максимум 2 хопи, для максимальної приватності — 3.
Смарт-контракти й економіка
Payment channel для micropayments
Користувачі платять за трафік у реальному часі. Транзакція у блокчейні за кожен MB — нежизнеспособно (gas). Рішення: unidirectional payment channels (схема як у Lightning, але простіша для EVM).
contract DVPNChannel {
struct Channel {
address user;
address provider;
uint256 deposit; // заблоковані середства
uint256 settled; // уже виплачено провайдеру
uint256 expiry; // таймаут каналу
bool closed;
}
mapping(bytes32 => Channel) public channels;
event ChannelOpened(bytes32 indexed channelId, address user, address provider, uint256 deposit);
event ChannelClosed(bytes32 indexed channelId, uint256 providerAmount, uint256 userRefund);
// Користувач відкриває канал з депозитом
function openChannel(address provider, uint256 duration) external payable returns (bytes32) {
bytes32 channelId = keccak256(abi.encodePacked(msg.sender, provider, block.timestamp));
channels[channelId] = Channel({
user: msg.sender,
provider: provider,
deposit: msg.value,
settled: 0,
expiry: block.timestamp + duration,
closed: false
});
emit ChannelOpened(channelId, msg.sender, provider, msg.value);
return channelId;
}
// Провайдер закриває канал з підписаним чеком від користувача
function closeChannel(
bytes32 channelId,
uint256 amount, // скільки провайдер заробив
bytes calldata userSig // підпись користувача
) external {
Channel storage ch = channels[channelId];
require(msg.sender == ch.provider, "Only provider");
require(!ch.closed, "Already closed");
require(amount <= ch.deposit, "Exceeds deposit");
// Верифікуємо підпись: користувач підтвердив цей amount
bytes32 hash = keccak256(abi.encodePacked(channelId, amount));
bytes32 ethHash = MessageHashUtils.toEthSignedMessageHash(hash);
address signer = ECDSA.recover(ethHash, userSig);
require(signer == ch.user, "Invalid signature");
ch.closed = true;
ch.settled = amount;
payable(ch.provider).transfer(amount);
payable(ch.user).transfer(ch.deposit - amount);
emit ChannelClosed(channelId, amount, ch.deposit - amount);
}
}
Користувач періодично підписує «чеки» на нарастаючу суму off-chain. Провайдер зберігає останній чек. При закритті — предъявляє останній чек контракту. Це O(1) транзакцій у блокчейні незалежно від обсягу трафіку.
Ризик: користувач може відозвати середства до закриття каналу провайдером. Захист: expiry — провайдер зобов'язаний закрити канал до істечення терміну. Timelock на withdrawal для користувача: не можна вивести середства до expiry або якщо провайдер не ініціював закриття.
Node Registry
On-chain реєстр провайдерів зі stake, метаданими й репутацією:
struct NodeInfo {
address operator;
uint256 stake; // залог
string endpoint; // WireGuard / V2Ray endpoint
bytes32 locationHash; // хеш від країни/регіону (privacy)
uint256 bandwidthCapacity; // Mbps
uint256 totalServed; // суммарний трафік, верифікований контрактом
uint256 uptime; // у basis points (9950 = 99.5%)
NodeStatus status;
}
Endpoint зберігати on-chain небезпечно для провайдерів з чутливих юрисдикцій. Альтернатива: endpoint зберігається в IPFS або у зашифрованому вигляді, ключ расшифровки тільки у авторизованих користувачів.
Bandwidth proof: верифікація без повної довіри
Головна проблема: як доказати, що провайдер реально обслужив трафік? Простих рішень — provider самоотчёт — очевидно неверифіковні.
Proof of Bandwidth через challenge-response. Coordinating нода періодично посилає challenge exit ноді, потребуючи передати дані через встановлений туннель. Latency й throughput вимірюються, результат підписується. Це не ідеальна верифікація, але значно підвищує поріг мошенництва.
Client-side measurement. Клієнтське приложення вимірює реальну швидкість й підписує результат. Провайдер не може предъявити контракту більше, ніж підтвердив клієнт. Проблема: клієнт теж може брехати (collusion), але інцентив для цього немає — клієнт платить більше при накрутці.
Third-party auditor nodes. Спеціалізовані ноди-аудитори, які періодично перевіряють провайдерів і публікують результати on-chain. Sentinel використовує цей підхід.
Client-side реалізація
Клієнтське приложення (desktop/mobile) — критичний компонент. Функції:
Node discovery й selection. Запит до on-chain реєстру → фільтрація по геолокації, ціні, uptime → вибір оптимального провайдера. Кешування списку нод локально, оновлення по таймеру.
WireGuard management. На Linux/macOS — native WireGuard через wg-quick. На Windows — wireguard-windows. На Android/iOS — wireguard-go. Генерація keypair на клієнті, публікація public key провайдеру через зашифрований канал.
Payment channel lifecycle. Відкриття каналу при підключенні, періодичне підписання чеків (наприклад, кожні 10 MB), закриття при відключенні. Все це повинно бути transparent для користувача.
class DVPNClient {
private channel: PaymentChannel | null = null;
private wireguard: WireGuardInterface;
async connect(nodeAddress: string): Promise<void> {
// 1. Відкриваємо payment channel
this.channel = await this.openPaymentChannel(nodeAddress, {
depositAmount: parseEther("0.1"), // depozit
duration: 3600, // 1 час
});
// 2. Отримуємо WireGuard конфіг від провайдера (через зашифрований handshake)
const wgConfig = await this.negotiateWireGuard(nodeAddress, this.channel.id);
// 3. Піднімаємо туннель
await this.wireguard.connect(wgConfig);
// 4. Запускаємо billing loop
this.startBillingLoop();
}
private async startBillingLoop(): Promise<void> {
setInterval(async () => {
const bytesUsed = await this.wireguard.getStats();
const owedAmount = this.calculateOwed(bytesUsed);
const signedVoucher = await this.signVoucher(this.channel!.id, owedAmount);
await this.sendVoucherToProvider(signedVoucher);
}, 30_000); // кожні 30 секунд
}
}
Регуляторні й юридичні аспекти
Exit ноди децентралізованого VPN несуть юридичну відповідальність за трафік, який через них проходить — так же, як звичайні VPN-провайдери. У деяких юрисдикціях це проблема. Sentinel та Mysterium вирішують це через Terms of Service для операторів нод і технічні обмеження на типи трафіку (блокування торрентів та P2P за замовчуванням).
Це не технічне питання, але воно повинно бути вирішено до launch у протокол-рівневій політиці.
Сроки розробки
| Компонент | Срок |
|---|---|
| Протокольне проектування + архітектура | 2–3 тижні |
| Смарт-контракти (channel, registry, staking) | 4–6 тижнів |
| Exit node daemon (WireGuard + billing) | 4–6 тижнів |
| Клієнтське приложення (desktop) | 6–10 тижнів |
| Mobile клієнт (iOS + Android) | 8–12 тижнів |
| Тестування мережі + аудит контрактів | 4–6 тижнів |
MVP з desktop клієнтом й 10–20 тестовими нодами — 4–6 місяців. Production-ready система з mobile підтримкою й достатньою децентралізацією — 8–12 місяців.







