Разбираемся с IPFS Circuit Relay

Введение в технологию IPFS Circuit Relay.
21.05.2019, ipfs

Уже несколько лет я наблюдаю за проектом IPFS и радуюсь его интенсивному развитию. Наверно приятнее всего, что проект не ушел полностью в разработку Filecoin получив хорошее финансирование. Protocol Labs даже отделили сетевой стек от IPFS, обобщили и назвали его libp2p, ни больше ни меньше библиотека для пиринговой коммуникации. Функциональность у libp2p довольно обширная, но в этой статье я бы хотел рассказать о наиболее интересной для меня, как для фаната mesh-сетей, особенности. Этот модуль вошел в спецификацию под именем relay.

Внимательный пользователь IPFS мог заметить, что в выводе команды ipfs swarm peers все чаще стали появляться записи вида:

/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmZYoCxLeBYxPNcCEVp75HZBsZnfZELiECD1NHywL9F583
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmUyHkuB7uMJWZnMSCvruKwH8UKTb8wsYmSWARRi17NXtF
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmXbaSzhgnYfMo5xHd5s2K9GAXYYe9e1AEL4cTmrAmfikD
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmWiXo5hhiABTaNGLFwA4H4jDFHHassY78vtrfD7FWe9SM
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/Qmf6toae9WsQimsGypgz28SsoreG7Qd71WGdYSYuzQzHiN
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/Qmb8kGDjik1fDmQB2V9TpXyJKB7ZyWD6xLjeue4rUoDdPE
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmXVPXQMDSTUxy4kXTaNG7mUEuNZ3EzC5kz7Tv2c5am5Dz
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmTryiBCgpQ5WzYUP6g5T8ZSo5esQK3cDbZDk1CG6QDFab
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmcLqwzKxAw5b1xZqhdEWC32RMkpqeQnKUPw3AYNJ7cbcU
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmUkqDU6njUCs1uQwQcPqSJ6pQ4n1cceDsskUt5VsjDTvF
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmXM1vgBBjggPHAAgcS7KLUfkfwpouz766ZaGKE8Wpm4Pi
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmS2tD2ztSnoJEKA5EkrzefS24yPgDRWXtiasMJ759k8Em
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmeTDuU562Q6DwTrFeE5nPn7MVAhPmiBqGRrodR8NuZiGA
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmTpazgTQNwD1f3e3yMRqXB7BW9wzeJ3hGfiPqyJhjzWdf
/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmWiTQoWhJZDxikxHEbjmRiQT4FMgzZS98gfPNJ9GWWuZ8

Это довольно странные мульти-адреса так как в них нет традиционных для IP-сетей адреса и порта для связи с пиром, как например, здесь:

/ip4/178.45.40.65/tcp/4001/ipfs/QmbtPSNsRQeecUavxVZTULaPhp7owBgfNdbNY5egVPn9jn

Спецификация утверждает, что это специальный формат адреса, который включает в процедуру соединения с пиром еще одну сторону - Релей.

+-----+    /ip4/.../tcp/.../ws/p2p/QmRelay    +-------+    /ip4/.../tcp/.../p2p/QmTwo       +-----+
|QmOne| <------------------------------------>|QmRelay|<----------------------------------->|QmTwo|
+-----+   (/libp2p/relay/circuit multistream) +-------+ (/libp2p/relay/circuit multistream) +-----+
     ^                                         +-----+                                         ^
     |           /p2p-circuit/QmTwo            |     |                                         |
     +-----------------------------------------+     +-----------------------------------------+

Релей нужен, когда в топологии нашей сети присутствуют два узла (QmOne и QmTwo), прямое соединение между которыми невозможно (например, оба находятся за глухим NAT), а связь установить необходимо. В такой ситуации выбирается специальный узел-посредник, который проксирует соединение от одного узла другому и наоборот.

В relay-модуль так же планируют добавить множественное проксирование (multihop) и автоматический поиск релеев в сети.

Звучит здорово, посмотрим как это работает на практике. Попробуем разобрать Circuit Relay адрес:

/ipfs/QmRdjvsyoNjA2ZfAQBtQ7A2m5NmtSXLgxE55Brn1AUjZ1v/p2p-circuit/ipfs/QmWiTQoWhJZDxikxHEbjmRiQT4FMgzZS98gfPNJ9GWWuZ8
\__________________________________________________/            \__________________________________________________/
         Адрес ноды-посредника (Relay)                                            Адрес целевой ноды

Спецификация так же утверждает, что при подключении достаточно указываеть адрес целевой ноды в таком виде:

/p2p-circuit/ipfs/QmWiTQoWhJZDxikxHEbjmRiQT4FMgzZS98gfPNJ9GWWuZ8

Я буду связываться через команду ipfs ping со своей домашней рабочей станцией. Рабочий ноутбук и домашняя станция находятся за NAT и не имеют прямого подключения. Для начала мне нужен идентификатор ноды на домашней станции, его получим командой ipfs id.

$ ipfs id
{
    "ID": "QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8",
    ...
}

Хорошо, теперь формируем мульти-адрес в Circuit Relay сети:

/p2p-circuit/ipfs/QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8

Наконец пробуем пингануть полученный адрес:

$ ipfs ping /p2p-circuit/ipfs/QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8 
PING QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8.
Pong received: time=641.62 ms
Pong received: time=639.79 ms
Pong received: time=640.72 ms
Pong received: time=572.90 ms
Pong received: time=652.26 ms
Pong received: time=571.97 ms

Поиск релея может занять время, у меня это заняло 15-20 секунд. Так или иначе коннект есть! Соединение через swarm работает так, будто оба узла находятся в одной IP-сети.

$ ipfs swarm connect /p2p-circuit/ipfs/QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8
connect QmTYXdMQjzV3tPEphTRCYw4TQp72u3GnaBLCsfGGW3Aum8 success 

В итоге у нас получилось разобраться как выглядят адреса в Circuit Relay сети и подключиться к ноде не зная ее IP-адреса. Надеюсь читатель, воодушевившись потенциалом связности, который Circuit Relays привносят в IPFS, запустит еще пару нод на ПК, RaspberryPi или микроволновке. Присоединяйтесь к веселому и шумному Рою!