Перейти к основному содержимому

Внутренние сообщения

warning

Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.

Общие сведения

Смарт-контракты взаимодействуют друг с другом, отправляя так называемые внутренние сообщения. Когда внутреннее сообщение достигает своего адресата, создается обычная транзакция от имени акккаунта получателя, а внутреннее сообщение обрабатывается в соответствии с кодом и постоянными данными этого аккаунта (смарт-контракт).

к сведению

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

Этот подход приводит к необходимости различать, предназначено ли внутреннее сообщение как "запрос", "ответ" или не требует никакой дополнительной обработки (например, "простой денежный перевод"). Кроме того, при получении ответа должен быть способ определить, какому запросу он соответствует.

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

Внутренняя структура сообщения

Тело сообщения может быть встроено в само сообщение или сохранено в отдельной ячейке, на которую ссылается сообщение, как указано во фрагменте схемы TL-B:

message$_ {X:Type} ... body:(Either X ^X) = Message X;

Принимающий смарт-контракт должен принимать как минимум внутренние сообщения со встроенными телами сообщений (всякий раз, когда они помещаются в ячейку, содержащую сообщение). Если он принимает тела сообщений в отдельных ячейках (используя конструктор right из (Either X ^X)), обработка входящего сообщения не должна зависеть от конкретного варианта встраивания, выбранного для тела сообщения. С другой стороны, совершенно допустимо не поддерживать тела сообщений в отдельных ячейках для более простых запросов и ответов.

Внутреннее тело сообщения

Тело сообщения обычно начинается со следующих полей:

* A 32-bit (big-endian) unsigned integer `op`, identifying the `operation` to be performed, or the `method` of the smart contract to be invoked.
* A 64-bit (big-endian) unsigned integer `query_id`, used in all query-response internal messages to indicate that a response is related to a query (the `query_id` of a response must be equal to the `query_id` of the corresponding query). If `op` is not a query-response method (e.g., it invokes a method that is not expected to send an answer), then `query_id` may be omitted.
* The remainder of the message body is specific for each supported value of `op`.

Простое сообщение с комментарием

Если op равен нулю, то сообщение является "простым сообщением о передаче с комментарием". Комментарий содержится в оставшейся части тела сообщения (без поля query_id, т. е. начиная с пятого байта). Если он не начинается с байта 0xff, комментарий является текстовым; он может быть отображен "как есть" конечному пользователю кошелька (после фильтрации недопустимых и контрольных символов и проверки того, что это допустимая строка UTF-8).

Когда комментарий настолько длинный, что не помещается в ячейку, не вмещающийся конец строки помещается в первую ссылку ячейки. Этот процесс продолжается рекурсивно для описания комментариев, которые не помещаются в две или более ячеек:

root_cell("0x00000000" - 32 bit, "string" up to 123 bytes)
↳1st_ref("string continuation" up to 127 bytes)
↳1st_ref("string continuation" up to 127 bytes)
↳....

Тот же формат используется для комментариев переводов NFT и жетонов.

Например, пользователи могут указать цель простого перевода со своего кошелька на кошелек другого пользователя в этом текстовом поле. С другой стороны, если комментарий начинается с байта 0xff, то остаток представляет собой "двоичный комментарий", который не должен отображаться конечному пользователю в виде текста (только в виде шестнадцатеричного дампа, если необходимо). Предполагаемое использование "двоичных комментариев" заключается, например, в том, чтобы содержать идентификатор покупки для платежей в магазине, который автоматически генерируется и обрабатывается программным обеспечением магазина.

Большинство смарт-контрактов не должны выполнять нетривиальные действия или отклонять входящее сообщение при получении "простого сообщения о переводе". Таким образом, как только op оказывается равным нулю, функция смарт-контракта для обработки входящих внутренних сообщений (обычно называемая recv_internal()) должна немедленно завершиться с нулевым кодом выхода, указывающим на успех (например, путем выдачи исключения 0, если смарт-контрактом не был установлен пользовательский обработчик исключений). Это приведет к тому, что на принимающий аккаунт будет зачислено значение, переданное сообщением, без каких-либо дальнейших последствий.

Сообщения с зашифрованными комментариями

Если op равен 0x2167da4b, то сообщение является "сообщением передачи с зашифрованным комментарием". Это сообщение сериализуется следующим образом:

Ввод:

  • pub_1 и priv_1 - Ed25519 открытый и закрытый ключи отправителя, по 32 байта каждый.
  • pub_2 - Ed25519 открытый ключ получателя, 32 байта.
  • msg - сообщение для шифрования, произвольная строка байтов. len(msg) <= 960.

Алгоритм шифрования следующий:

  1. Вычислить shared_secret с помощью priv_1 и pub_2.
  2. Пусть salt будет представлением bas64url адреса кошелька отправителя с isBounceable=1 и isTestnetOnly=0.
  3. Выберите байтовую строку prefix длиной от 16 до 31, так что len(prefix+msg) делится на 16. Первый байт prefix равен len(prefix), остальные байты случайны. Пусть data = prefix + msg.
  4. Пусть msg_key будет первыми 16 байтами hmac_sha512(salt, data).
  5. Вычислите x = hmac_sha512(shared_secret, msg_key). Пусть key=x[0:32] и iv=x[32:48].
  6. Зашифруйте data с помощью AES-256 в режиме CBC с key и iv.
  7. Создайте зашифрованный комментарий:
  8. pub_xor = pub_1 ^ pub_2 - 32 байта. Это позволяет каждой стороне расшифровать сообщение, не заглядывая в открытый ключ другой стороны.
  9. msg_key - 16 байт.
  10. Зашифрованные data.
  11. Тело сообщения начинается с 4-байтового тега 0x2167da4b. Затем этот зашифрованный комментарий сохраняется:
  12. Строка байтов делится на сегменты и сохраняется в цепочке ячеек c_1,...,c_k (c_1 является корнем тела). Каждая ячейка (кроме последней) имеет ссылку на следующую.
  13. c_1 содержит до 35 байт (не включая 4-байтовый тег), все остальные ячейки содержат до 127 байт.
  14. Этот формат имеет следующие ограничения: k <= 16, максимальная длина строки 1024.

Тот же формат используется для комментариев при переводе NFT и жетонов, обратите внимание, что следует использовать открытый ключ адреса отправителя и адреса получателя (не адреса jetton-wallet).

к сведению

Learn from examples of the message encryption algorithm:

Простые сообщения о передаче без комментариев

"Простое сообщение о передаче без комментариев" имеет пустое тело (даже без поля op). Вышеизложенные соображения применимы и к таким сообщениям. Обратите внимание, что такие сообщения должны иметь свои тела, встроенные в ячейку сообщения.

Различие между сообщениями запроса и ответа

Мы ожидаем, что сообщения "запроса" будут иметь op с очищенным старшим битом, т. е. в диапазоне 1 .. 2^31-1, а сообщения "ответа" будут иметь op с установленным старшим битом, т. е. в диапазоне 2^31 .. 2^32-1. Если метод не является ни запросом, ни ответом (так что соответствующее тело сообщения не содержит поля query_id), он должен использовать op в диапазоне "запроса" 1 .. 2^31 - 1.

Обработка стандартных сообщений ответа

Существуют некоторые "стандартные" сообщения ответа с op, равным 0xffffffff и 0xfffffffe. В общем случае значения op от 0xffffffff0 до 0xffffffff зарезервированы для таких стандартных ответов.

* `op` = `0xffffffff` means "operation not supported". It is followed by the 64-bit `query_id` extracted from the original query, and the 32-bit `op` of the original query. All but the simplest smart contracts should return this error when they receive a query with an unknown `op` in the range `1 .. 2^31-1`.
* `op` = `0xfffffffe` means "operation not allowed". It is followed by the 64-bit `query_id` of the original query, followed by the 32-bit `op` extracted from the original query.

Обратите внимание, что неизвестные "ответы" (с op в диапазоне 2^31 .. 2^32-1) следует игнорировать (в частности, в ответ на них не следует генерировать ответ с op, равным 0xffffffff), так же как и неожиданные возвращенные сообщения (с установленным флагом "bounced").

Известные коды операций

к сведению

Также op-code, op::code и операционный код

Тип контрактаШестнадцатеричный кодOP::Code
Общий0x00000000Текстовый комментарий
Общий0xffffffffОтклонение
Общий0x2167da4bЗашифрованный комментарий
Общий0xd53276dbИзбыток
Электор0x4e73744bНовая ставка
Электор0xf374484cПодтверждение новой ставки
Электор0x47657424Запрос на восстановление ставки
Электор0x47657424Ответ на восстановление ставки
Кошелек0x0f8a7ea5Перевод жетонов
Кошелек0x235caf52Вызов жетона
Жетон0x178d4519Внутренняя передача жетона
Жетон0x7362d09cУведомление жетона
Жетон0x595f07bcСжигание жетона
Жетон0x7bdd97deУведомление о сжигании жетона
Жетон0xeed236d3Установка статуса жетона
Выпуск жетона0x642b7d07Выпуск жетона
Выпуск жетона0x6501f354Изменение администратора жетона
Выпуск жетона0xfb88e119Запрос администратора жетона
Выпуск жетона0x7431f221Сброс администратора жетона
Выпуск жетона0xcb862902Изменение метаданных жетона
Выпуск жетона0x2508d66aОбновление жетона
Вестинг0xd372158cПополнение
Вестинг0x7258a69bДобавление белого списка
Вестинг0xf258a69bДобавление ответа в белый список
Вестинг0xa7733acdОтправка
Вестинг0xf7733acdОтправка ответа
Dedust0x9c610de3Обмен ExtOut на Dedust
Dedust0xe3a0d482Обмен жетонов на Dedust
Dedust0xea06185dВнутренний обмен Dedust
Dedust0x61ee542dВнешний обмен
Dedust0x72aca8aaОбмен пирами
Dedust0xd55e4686Внутренний депозит ликвидности
Dedust0x40e108d6Депозит ликвидности жетона
Dedust0xb56b9598Ликвидность всех депозитов
Dedust0xad4eb6f5Выплаты из пула
Dedust0x474а86саВыплата
Dedust0xb544f4a4Депозит
Dedust0x3aa870a6Вывод
Dedust0x21cfe02bСоздать хранилище
Dedust0x97d51f2fСоздать непостоянный пул
Dedust0x166cedeeОтмена депозита
StonFi0x25938561Внутренний обмен
StonFi0xf93bb43fЗапрос платежа
StonFi0xfcf9e58fОбеспечение ликвидности
StonFi0xc64370e5Успешный обмен
StonFi0x45078540Ссылка на успешный обмен