Сообщения и транзакции
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @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.


Это простой случай, когда у нас только два контракта. Как наша система работает в более сложных случаях?