TON链上发送消息与合约进行交互以及对应操作类型的消息格式模板

  • A+
所属分类:TheOpenNetwork(TON)

chatGPT账号

一、说明

TON链上所有的操作都需要与合约进行消息交互,通过程序与FUNC合约进行消息的传递,然后执行消息中包含的操作行为。在FUNC对应的智能合约中不仅需要正确识别对应的操作码op_code还需要有处于对应操作行为的功能。外部程序通过传递消息给FUNC智能合约,其中对应的消息模板要包含目标输入参数。例如,如果需要给指定地址mint jetton则需要在消息中包含mint的目标地址和对应的jetton数量。这些动态参数需要包含在正确的消息模板中。

TON 中的一笔交易包括以下内容:

  • 最初触发合约的传入消息(存在特殊的触发方式)
  • 由传入消息引起的合约行动,例如更新合约的存储(可选)
  • 发送给其他参与者的所生成的传出消息(可选)
  • 技术上,合约可以通过特殊功能如 Tick-Tock 触发,但这个功能更多用于 TON 内部的区块链核心合约。
  • 并非每笔交易都会导致传出消息或对合约存储的更新——这取决于合约代码所定义的操作。

TON链上发送消息与合约进行交互以及对应操作类型的消息格式模板

二、消息类型

幸运的是,TON 的工作方式是任何内部消息都一定会被目标账户接收。消息不会在来源和目的地之间的任何地方丢失。外部消息有点不同,因为它们被接受到区块中是由验证者自行决定的,但是,一旦消息被接受进入传入消息队列,它将被传递。

内部消息:

智能合约通过发送所谓的内部消息来相互交互。当内部消息到达其预定目的地时,会代表目的地账户创建一个普通交易,并按照该账户(智能合约)的代码和持久数据的所指定的去处理内部消息。

外部消息:

外部消息是从外部发送到 TON 区块链中的智能合约,以使它们执行特定操作。

例如,钱包智能合约期望接收包含钱包所有者签名的订单的外部消息(例如,从钱包智能合约发送的内部消息)。当这样的外部消息被钱包智能合约接收时,它首先检查签名,然后接受消息(通过运行 TVM 原语 ACCEPT),然后执行所需的任何操作。

非弹回消息:

几乎所有在智能合约之间发送的内部消息都应该是可弹回的,即应该设置它们的“bounce”位。然后,如果目标智能合约不存在,或者在处理此消息时抛出未处理的异常,消息将被“bounced”,携带原始值的剩余部分(减去所有消息传输和gas费用)。弹回消息的主体将包含32位的0xffffffff,紧接着是原始消息的256位,但是“bounce”标志位被清除,“bounced”标志位被设置。因此,所有智能合约都应检查所有入站消息的“bounced”标志,并且要么默默接受它们(通过立即以exit code 0终止),要么执行一些特殊处理来检测哪个出站查询失败了。弹回消息主体中包含的查询永远不应执行。

三、以下说明了消息的发送方式和消息格式模板:

消息的组成、解析和发送位于TL-B schemas、交易阶段和TVM的交汇处。

事实上,FunC有send_raw_message函数,该函数期望一个序列化消息作为参数。

由于TON是一个功能广泛的系统,支持所有这些功能的消息可能看起来相当复杂。尽管如此,大多数情况下并不使用那么多功能,消息序列化在大多数情况下可以简化为:

  cell msg = begin_cell()
    .store_uint(0x18, 6)
    .store_slice(addr)
    .store_coins(amount)
    .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    .store_slice(message_body)
  .end_cell();

TON链上发送消息与合约进行交互以及对应操作类型的消息格式模板

因此,开发者不用担忧,如果这份文档中的某些内容在第一次阅读时看起来难以理解,没有关系。只需把握总体思路即可。

有时文档中可能会提到**'gram'这个词,但大多是在代码示例中,它只是toncoin**的一个过时名称。

让我们深入了解!

消息布局

我们将从内部消息布局开始。

描述智能合约可以发送的消息的TL-B方案如下:


message$_ {X:Type} info:CommonMsgInfoRelaxed 
  init:(Maybe (Either StateInit ^StateInit))
  body:(Either X ^X) = MessageRelaxed X;

让我们用语言来描述。任何消息的序列化都包括三个字段:info(某种标题,描述来源、目的地和其他元数据)、init(仅在消息初始化时需要的字段)和body(消息有效载荷)。

MaybeEither和其他类型的表达式意味着以下内容:

  • 当我们有字段info:CommonMsgInfoRelaxed时,意味着CommonMsgInfoRelaxed的序列化直接注入到序列化cell中。
  • 当我们有字段body:(Either X ^X)时,意味着当我们(反)序列化某种类型X时,我们首先放置一个either位,如果X被序列化到同一cell,则为0,如果它被序列化到单独的cell,则为1
  • 当我们有字段init:(Maybe (Either StateInit ^StateInit))时,意味着我们首先放置01,要取决于这个字段是否为空;如果不为空,我们序列化Either StateInit ^StateInit(再次,放置一个either位,如果StateInit被序列化到同一cell则为0,如果被序列化到单独的cell则为1)。

CommonMsgInfoRelaxed的布局如下:

int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
  src:MsgAddress dest:MsgAddressInt 
  value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
  created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;

ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
  created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;

让我们现在专注于int_msg_info。它以1位的前缀0开始,然后有三个1位的标志位,分别表示是否禁用即时超立方路由(目前始终为真)、是否在处理过程中出错时弹回消息,以及消息本身是否是弹回的结果。然后序列化来源和目的地址,接着是消息值和四个与消息转发费用和时间有关的整数。

如果消息是从智能合约发送的,其中一些字段将被重写为正确的值。特别是,验证者将重写bouncedsrcihr_feefwd_feecreated_ltcreated_at。这意味着两件事:首先,另一个智能合约在处理消息时可以信任这些字段(发送者无法伪造来源地址、bounced标志位等);其次,在序列化时我们可以将任何有效值放入这些字段中(无论如何这些值都将被重写)。

消息的直接序列化如下所示:

var msg = begin_cell()
    .store_uint(0, 1) ;; tag
    .store_uint(1, 1) ;; ihr_disabled
    .store_uint(1, 1) ;; allow bounces
    .store_uint(0, 1) ;; not bounced itself
    .store_slice(source)
    .store_slice(destination)
    ;; serialize CurrencyCollection (see below)
    .store_coins(amount)
    .store_dict(extra_currencies)
    .store_coins(0) ;; ihr_fee
    .store_coins(fwd_value) ;; fwd_fee 
    .store_uint(cur_lt(), 64) ;; lt of transaction
    .store_uint(now(), 32) ;; unixtime of transaction
    .store_uint(0,  1) ;; no init-field flag (Maybe)
    .store_uint(0,  1) ;; inplace message body flag (Either)
    .store_slice(msg_body)
  .end_cell();

然而,开发者通常使用快捷方式而不是逐步序列化所有字段。因此,让我们考虑如何使用elector-code中的示例从智能合约发送消息。

() send_message_back(addr, ans_tag, query_id, body, grams, mode) impure inline_ref {
  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
  var msg = begin_cell()
    .store_uint(0x18, 6)
    .store_slice(addr)
    .store_coins(grams)
    .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
    .store_uint(ans_tag, 32)
    .store_uint(query_id, 64);
  if (body >= 0) {
    msg~store_uint(body, 32);
  }
  send_raw_message(msg.end_cell(), mode);
}

然后我们应该序列化值。一般来说,消息值是一个CurrencyCollection对象,其方案如下:

nanograms$_ amount:(VarUInteger 16) = Grams;

extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) 
                 = ExtraCurrencyCollection;

currencies$_ grams:Grams other:ExtraCurrencyCollection 
           = CurrencyCollection;

添加VX或者telegram获取全程线上免费指导

TON链上发送消息与合约进行交互以及对应操作类型的消息格式模板
免责声明

免责声明:

本文不代表知点网立场,且不构成投资建议,请谨慎对待。用户由此造成的损失由用户自行承担,与知点网没有任何关系;

知点网不对网站所发布内容的准确性,真实性等任何方面做任何形式的承诺和保障;

网站内所有涉及到的区块链(衍生)项目,知点网对项目的真实性,准确性等任何方面均不做任何形式的承诺和保障;

网站内所有涉及到的区块链(衍生)项目,知点网不对其构成任何投资建议,用户由此造成的损失由用户自行承担,与知点网没有任何关系;

知点区块链研究院声明:知点区块链研究院内容由知点网发布,部分来源于互联网和行业分析师投稿收录,内容为知点区块链研究院加盟专职分析师独立观点,不代表知点网立场。

本文是全系列中第271 / 278篇:行业技术

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的电报
  • 这是我的电报扫一扫
  • weinxin
chatGPT账号
知点

发表评论

您必须登录才能发表评论!