Сообщения и транзакции
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
TON – это асинхронный блокчейн со сложной структурой, которая сильно отличается от других блокчейнов. В связи с этим у новых разработчиков часто возникают вопросы о низкоуровневом устройстве в TON. В этой статье мы рассмотрим один из таких вопросов, связанный с доставкой сообщений.
Что такое сообщение?
Сообщение – это пакет данных, которым обмениваются участники (пользователи, приложения или смарт-контракты). Обычно оно содержит информацию, указывающую получателю, какое действие следует выполнить, например, обновить хранилище или отправить новое сообщение.


Работа с таким типом обмена данными напоминает запуск спутника в космос. Хотя мы точно знаем, какое сообщение мы создали, после запуска необходимо наблюдение, чтобы определить результат.
Что такое транзакция?
Транзакция в TON состоит из следующего:
- входящее сообщение – инициирует запуск контракта (существуют специальные способы запуска)
- действия контракта, вызванные входящим сообщением, например, обновление хранилища контракта (необязательно)
- исходящие сообщения – создаются и отправляются другим участникам (необязательно).
Технически контракт может быть запущен с помощью специальных функций, таких как Tick-Tock, но эта функция чаще используется для внутренних контрактов ядра TON Blockchain.
Не каждая транзакция приводит к созданию исходящих сообщений или обновлению хранилища контракта – это зависит от действий, заложенных в коде контракта.


В асинхронной системе вы не можете получить ответ от целевого смарт-контракта в пределах той же транзакции. Обработка вызова контракта может занять несколько блоков, в зависимости от длины маршрута между источником и местом назначения.
Достижение парадигмы бесконечного шардинга требует полной параллелизации, гарантирующей, что каждая транзакция будет выполняться независимо от других. Поэтому вместо транзакций, которые затрагивают и изменяют состояние многих контрактов одновременно, каждая транзакция в TON выполняется только на одном смарт-контракте, а смарт-контракты обмениваются данными посредством сообщений. Таким образом, смарт-контракты могут взаимодействовать друг с другом только путем вызова их функций с помощью специальных сообщений и последующего получения на них ответа с помощью других сообщений.
Достижение парадигмы бесконечного шардинга требует полной параллелизации, гарантирующей, что каждая транзакция будет выполняться независимо от других. Поэтому вместо транзакций, которые затрагивают и изменя ют состояние многих контрактов одновременно, каждая транзакция в TON выполняется только на одном смарт-контракте, а смарт-контракты обмениваются данными посредством сообщений. Таким образом, смарт-контракты могут взаимодействовать друг с другом только путем вызова их функций с помощью специальных сообщений и последующего получения на них ответа с помощью других сообщений.
Более подробное и точное описание на странице Transaction Layout.
Результат транзакции
Существует код завершения TVM для транзакции, которая находилсь в фазе вычисления, и если значение данного кода не равно 0
или 1
, то произошла ошибка.
Также фаза вычислений TVM может быть пропущена, например, из-за отсутствия средств или статуса состояния.
Для определения успешности транзакции следует использовать tx.description.action.success
&& tx.description.compute_ph.success
:
"transactions": [
{
"description": {
. . . . . . . .
"action": {
"valid": true,
"success": true,
. . . . . . . .
},
. . . . . . . .
"destroyed": false,
"compute_ph": {
"mode": 0,
"type": "vm",
"success": true,
Транзакция может иметь один из трех результатов:
- Success, exit code:
0
или1
– успешно - Fail,
aborted: true
– неуспешно, прервано без выполнения - Fail, exit code,
aborted: true
– неуспешно, прервано.
aborted: true
является полем из Toncenter API, а не полем вывода транзакции
Что такое логическое время?
Строго гарантируется, что транзакция, являющаяся результатом сообщения, будет иметь lt больше, чем lt сообщения. Аналогично lt сообщения, отправленного в некоторой транзакции, строго больше, чем lt транзакции, которая его вызвала. Сообщени я, отправленные с одного аккаунта и транзакции, произошедшие на одном аккаунте, также строго упорядочены.
Строго гарантируется, что транзакция, являющаяся результатом сообщения, будет иметь lt больше, чем lt сообщения. Аналогично lt сообщения, отправленного в некоторой транзакции, строго больше, чем lt транзакции, которая его вызвала. Сообщения, отправленные с одного аккаунта и транзакции, произошедшие на одном аккаунте, также строго упорядочены.


Благодаря lt мы всегда знаем порядок транзакций, полученных и отправленных сообщений для каждого аккаунта.
Более того, если аккаунт A отправил два сообщения аккаунту B, гарантируется, что сообщение с меньшим lt будет обработано раньше:
Если msg1_lt < msg2_lt
=> tx1_lt < tx2_lt
.
Если msg1_lt < msg2_lt
=> tx1_lt < tx2_lt
.


Для каждого блока мы можем определить lt-диапазон, который начинается с первой транзакции и заканчивается lt последнего события в блоке (сообщения или транзакции). Блоки упорядочены так же, как и другие события в TON, поэтому если один блок зависит от другого, он имеет более высокое lt. Дочерний блок в шарде имеет более высокое lt, чем его родитель. lt мастерчейн-блока выше, чем lts каждого вложенного шард-блока, поскольку мастер-блок зависит от вложенных шард-блоков. Каждый шард-блок содержит упорядоченную ссылку на последний мастер-блок (на момент создания шард-блока), поэтому lt шард-блока выше, чем lt мастер-блока на который они ссылаются.
Для каждого блока мы можем определить lt-диапазон, который начинается с первой транзакции и заканчивается lt последнего события в блоке (сообщения или транзакции). Блоки упорядочены так же, как и другие события в TON, поэтому если один блок зависит от другого, он имеет более высокое lt. Дочерний блок в шарде имеет более высокое lt, чем его родитель. lt мастерчейн-блока выше, чем lts каждого вложенного шард-блока, поскольку мастер-блок зависит от вложенных шард-блоков. Каждый шард-блок содержит упорядоченную ссылку на последний мастер-блок (на момент создания шард-блока), поэтому lt шард-блока выше, чем lt мастер-блока на который они ссылаются.
Доставка сообщений
К счастью, TON работает таким образом, что любое внутреннее сообщение будет обязательно получено целевым аккаунтом. Сообщение не может потеряться где-то между источником и получателем. С внешними сообщениями дело обстоит немного иначе, поскольку их принятие в блок происходит по усмотрению валидатора. Однако, как только сообщение будет принято в очередь входящих сообщений, оно будет доставлено.
Порядок доставки
Предположим, что существует два контракта – A и B. A получает внешнее сообщение, которое запускает отправку двух внутренних сообщений контракту B, назовем их сообщения 1 и 2. В этом простом случае мы можем на 100% быть уверены, что сообщение 1 будет обработано B раньше, чем 2 поскольку оно имеет меньшее lt.
Предположим, что существует два контракта – A и B. A получает внешнее сообщение, которое запускает отправку двух внутренних сообщений контракту B, назовем их сообщения 1 и 2. В этом простом случае мы можем на 100% быть уверены, что сообщение 1 будет обработано B раньше, чем 2 поскольку оно имеет меньшее lt.


Это простой случай, когда у нас только два контракта. Как наша система работает в более сложных случаях?
Несколько смарт-контрактов
Для большей наглядности предположим, что наши контракты отправляют обратно сообщения msg1'
и msg2'
после msg1
и msg2
, выполненных контрактами B
и C
. В результате tx2'
и tx1'
будет применено к контракту A
.
Есть две возможные трассировки для этих транзакций,
Для большей наглядности предположим, что наши контракты отправляют обратно сообщения msg1'
и msg2'
после msg1
и msg2
, выполненных контрактами B
и C
. В результате tx2'
и tx1'
будет применено к контракту A
.
Есть две возможные трассировки для этих транзакций,
- Первый возможный порядок –
tx1'_lt < tx2'_lt
:


- Второй возможный порядок –
tx2'_lt < tx1'_lt
:


Тоже самое происходит и в обратном случае, когда два контракта B и C отправляют сообщение одному контракту A. Даже если сообщение B -> A
было отправлено раньше, чем C -> A
, мы не можем знать какое из них будет доставлено первым. Маршрут B -> A
может потребовать большего количества переходов по цепочке шардов.


Возможных сценариев взаимодействия смарт-контрактов может быть множество и в любом сценарии с более чем 2 контрактами порядок доставки сообщений может быть произвольным. Единственная гарантия заключается в том, что сообщения от любого контракта A к любому контракту B будут обрабатываться в порядке их логического времени. Некоторые примеры приведены ниже.






Заключение
Асинхронная структура блокчейна TON создает трудности с гарантией доставки сообщений. Логическое время помогает установить порядок событий и транзакций, но не гарантирует порядок доставки сообщений между несколькими смарт-контрактами из-за различий маршрутов в цепочках шардов. Несмотря на эти сложности, TON обеспечивает внутреннюю доставку сообщений, поддерживая надежность сети. Разработчики должны адаптироваться к этим нюансам, чтобы использовать весь потенциал TON для создания инновационных децентрализованных приложений.