Skip to content

Latest commit

 

History

History
120 lines (74 loc) · 18 KB

Overlay-Network.md

File metadata and controls

120 lines (74 loc) · 18 KB

Overlay network

Архитектура тона построена таким образом, что в ней могут существовать одновременно и независимо очень много чеинов - они могут быть как приватные, так и публичные. Ноды имеют возможность выбирать, данные каких шардов и чеинов им хранить и обрабатывать. При этом протокол обмена данными остается неизменным засчет универсальности. Этого достичь позволяют такие технологии, как DHT, RLDP и оверлеи. С первыми двумя мы уже знакомы, в этом разделе познакомимся с оверлеями.

Оверлеи отвечают за разделение единой сети на дополнительные суб-сети. Оверлеи могут быть как публичные, к которым может подключиться любой, так и приватные, куда для вступления нужны дополнительные данные, известные только определенному кругу лиц.

Все чеины в тоне, включая мастерчеин, обмениваются данными, используя свой оверлей. Чтобы вступить в него, нужно найти ноды, которые уже состоят в нем, и начать с ними обмен данными. Найти ноды можно с помощью DHT.

Взаимодействие с другими нодами оверлея

Мы уже разбирали пример с поиском нод оверлея в статье про DHT, в разделе Поиск нод, хранящих состояние блокчеина. В этом разделе сконцентрируемся на взаимодействии с ними.

При запросе в DHT, мы получим адреса нод оверлея, у которых сможем узнать адреса других нод этого оверлея c помощью overlay.getRandomPeers. После того, как мы подключимся к достаточному количеству нод, мы сможем получать от них всю информацию о блоках и другие события чеина, а также отправлять им на обработку наши транзакции.

Найдем побольше соседей

Разберем пример с получением нод в оверлее.

Для этого отправим запрос overlay.getRandomPeers, сериализуем TL схему:

overlay.node id:PublicKey overlay:int256 version:int signature:bytes = overlay.Node;
overlay.nodes nodes:(vector overlay.node) = overlay.Nodes;

overlay.getRandomPeers peers:overlay.nodes = overlay.Nodes;

peers - должен содержать известных нам пиров, а в случае если мы хотим принять участие в оверлее, например для обработки броадкастов, также должен содержать наш подписанный адрес, но так как мы пока никого не знаем и хотим "просто спросить", peers.nodes будет пустым массивом.

Запрос overlay.getRandomPeers является своеобразным ping, члены оверлея постоянно шлют его друг другу, обмениваются известными нодами и проверяют доступность друг друга. Для того чтобы быть валидным соседом, нужно поддерживать свой адрес overlay.node в актуальном состоянии, постоянно обновляя поле version текущим unix таймштампом. В случае если ваша версия устареет на более чем 10 минут, вас могут исключить из списка соседей.

Каждый запрос внутри оверлея должен иметь перфикс в виде TL схемы:

overlay.query overlay:int256 = True;

В качестве overlay должен быть айди оверлея - айди ключа от схемы tonNode.ShardPublicOverlayId - тот же, что мы использовали для поиска в DHT.

Нам нужно объединить 2 сериализованные схемы, просто соединив 2 массива байтов, overlay.query будет идти первым, overlay.getRandomPeers - вторым.

Полученный массив мы оборачиваем в схему adnl.message.query и отправляем по ADNL. В ответ мы ждем overlay.nodes - это будет список нод оверлея, к которым мы можем подключиться и, если нужно, повторить тот же запрос уже к ним, пока мы не наберем достаточное количество соединений.

Функциональные запросы

После установки соединения, мы можем обращаться к нодам оверлея с запросами tonNode.*.

Для запросов такого рода используется протокол RLDP. И важно не забыть про префикс overlay.query - он должен использоваться для каждого запроса в оверлее.

В самих запросах нет ничего необычного, они очень похожи на то, что мы делали в статье про ADNL TCP.

Например, в запросе downloadBlockFull используется уже знакомый нам айди блока:

tonNode.downloadBlockFull block:tonNode.blockIdExt = tonNode.DataFull;

Передав его, мы сможем скачать полную информацию о блоке, в ответ получим:

tonNode.dataFull id:tonNode.blockIdExt proof:bytes block:bytes is_link:Bool = tonNode.DataFull;
  или
tonNode.dataFullEmpty = tonNode.DataFull;

В случае наличия, в поле block будут данные в формате TL-B.

Таким образом мы можем получать информацию напрямую от нод.

Broadcasts - распространение информации по сети

В некоторых оверлеях, где состояние сети непостоянно и может изменяться, используется протокол броадкастов. Он отвечает за передачу данных к соседним пирам, каждый пир, получив и обработав броадкаст сообщение, пересылает его своим соседям, которых может быть до пяти. Таким образом, передав изначальное сообщение нескольким пирам, оно распространится по всей сети.

Протокол броадкастов работает поверх ADNL UDP, используя Custom сообщения в контексте оверлея, и активно применяется в сетях шардчеинов для распространения новых блоков от валидаторов ко всем нодам, а также для распространения external сообщений от нод сети к валидаторам.

Броадкасты делятся на несколько типов:
  • Обычный - c помощью сообщений данного типа по сети распространяются небольшие данные, например, external сообщения от пользователей. Часто не требует специальных разрешений.

  • FEC - Тип броадкаст сообщения, использующийся для распространения больших данных, например, новых блоков. Работает по принципу RLDP, может состоять из большого количества частей, и для упаковки данных используется алгоритм RaptorQ, как и в RLDP, но по завершению получения нам нужно ответить overlay.fec.completed.

  • FEC Short - Аналогично предыдущему, но не несет в себе самих данных, передавая только их хеш, позволяет оптимизировать нагрузку на сеть, так как данные не гоняются туда-сюда повторно. Если пир еще не знает про полученный хеш, он запросит данные у ноды, от которой получил сообщение. На практике его применение пока не встречал.

Права доступа к броадкастингу

Оверлеи могут иметь разные разрешения, инициация броадкаста может быть разрешена только определенному кругу нод, например, валидаторам. Каждому члену сети заранее известен список доверенных публичных ключей, например, в случае шардчеинов - доверенные айди ключей хранятся в конфиге сети, в параметрах 32-37 включительно - это списки прошлых, текущих и следующих валидаторов. Основной список обновляется каждый раунд, и не все ключи конфига могут существовать одновременно.

Каждый участник сети при получении броадкаста валидирует его. Для определения происхождения используются поля src:PublicKey и certificate:overlay.Certificate. Сначала проверяется наличие айди от ключа src в списке доверенных. Если айди есть в списке - ключ считается валидным. Если ключа нет - мы проверяем сертификат certificate.

Для преобразования PublicKey в айди ключа мы берем sha256 хеш от его сериализованной схемы.

Проверка сертификата броадкаста

Сертификат может быть нескольких видов:

  • overlay.certificate - Стандартный тип сертификата, без настроек, разрешает все виды броадкаста, но может ограничивать размер сообщения параметром max_size

  • overlay.certificateV2 - Расширенный тип сертификата, позволяет ограничивать виды броадкаста (запретить FEC) и контролировать уровень доверия (trusted/need check) благодаря наличию параметра flags.

  • overlay.emptyCertificate - Сертификат отсутствует, если первичная проверка броадкаста на ключ не пройдена - броадкаст считается невалидным и игнорируется.

Для проверки сертификата нам нужно сначала проверить кто его выдал, для этого мы читаем поле issued_by:PublicKey сертификата и проверяем, есть ли в списке доверенных такой айди ключа. Если есть, мы проверяем подпись сертификата, сериализуя его по схеме в зависимости от типа, где в качестве node указываем айди ключа, для которого выдан сертификат, т.е айди от src:PublicKey из сообщения броадкаста. Если подпись совпала, сертификат можно считать доверенным и относиться к ключу src как к тому, кто выдал сертификат, но с учетом ограничений, указаных в сертификате.

Проверка подписи броадкаста

После того, как ключ и сертификат прошли проверку, нам нужно проверить подпись самого сообщения броадкаста на соответствие ключу из поля src. Для этого нам сначала нужно вычислить айди броадкаста, им будет являться айди схемы broadcast.Id нужного типа броадкаста. В поле src нам нужно указать айди ключа, посчитанный из ключа src самого броадкаста. Для типа FEC присутствует еще поле type - это айди от FEC типа, поля fec из схемы броадкаста. После того, как мы заполнили структуру, мы берем от нее айди (хеш), это и будет являться айди нашего броадкаста.

Если тип нашего броадкаста - FEC, нам нужно еще посчитать айди его части, сериализовав структуру overlay.broadcastFec.partId, в качестве data_hash нужно использовать sha256 хеш от поля data полученной части броадкаста.

Далее нам нужно сериализовать схему overlay.broadcast.toSign, где в качестве hash передать айди его части (посчитанный выше) в случае FEC, а в случае обычного броадкаста - просто хеш от поля data. В поле date нужно записать время броадкаста, полученное в сообщении в одноименном поле.

После сериализации схемы нам остается только сравнить ее подпись относительно ключа. Если подпись верна, броадкаст можно считать принятым, отправлять соседям и обрабатывать у себя.

Содержимое броадкастов у шардчеинов

В броадкастах шардчеинов передаются новые блоки (как мастерчеина, так и бейзчеина) и экстернал сообщения от пользователей.

[Используемые схемы]

Например, внутри такой структуры как tonNode.blockBroadcast, в поле data содержится сериализованый Cell TL-B схемы блока, обработка которого уже может зависить от конкретной цели.

Обработка броадкастов уже зависит от конкретной цели, но важно помнить, что недостаточно их просто читать, нужно распространять их по соседям для обеспечения стабильности сети.