消息 TL-B 方案
本节详细解释消息的 TL-B 方案。
消息 TL-B
TL-B
主要消息 TL-B 方案声明为几个嵌套结构的组合
message$_ {X:Type} info:CommonMsgInfo
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = Message X;
message$_ {X:Type} info:CommonMsgInfoRelaxed
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = MessageRelaxed X;
_ (Message Any) = MessageAny;
这里 Message X
- 是常见消息结构,MessageRelaxed X
是带有 CommonMsgInfoRelaxed 体的附加类型,而 Message Any
是两者的并集。消息结构与 X:Type 统一,换句话说就是一个 Cell。根据 TL-B,如果数据能够适应 1023 位,我们可以将所有数据组合在一个cell中,或者使用带有插入符号 ^
的引用。
序列化的 Message X
通过 FunC 方法 send_raw_message() 放置到动作列表中,然后智能合约执行此动作并发送消息。
显式序列化的定义
根据 TL-B 结构构建有效的二进制数据,我们应该进行序列化,这对每种类型都是递归定义的。这意味着,要序列化 Message X,我们需要知道如何序列化 StateInit
、CommonMsgInfo
等。
我们应该根据递归链接从另一个 TL-B 方案中获取每个嵌套结构,直到顶层结构的序列化是显式的 - 每个位由布尔或类似位的类型(比特,uint,varuint)定义。
目前在常规开发中不使用的结构将在 Type 列中标记为 *
,例如 *Anycast 通常在序列化中被跳过。
message$_
这是整个消息 Message X
的顶层 TL-B 方案:
message$_ {X:Type} info:CommonMsgInfo
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = Message X;
结构 | 类型 | 必需 | 描述 | |
---|---|---|---|---|
message$_ | 构造函数 | 按照构造函数规则定义。空标记 $_ 表示我们不会在开头添加任何位 | ||
info | CommonMsgInfo | 必需 | 详细的消息属性定义目的地及其值。始终放置在消息的根cell中。 | |
init | StateInit | 可选 | 通用结构,用于 TON 中初始化新合约。可以写在cell引用或根cell中。 | |
body | X | 必需 | 消息有效载荷。可以写在cell引用或根cell中。 |
nothing$0 {X:Type} = Maybe X;
just$1 {X:Type} value:X = Maybe X;
left$0 {X:Type} {Y:Type} value:X = Either X Y;
right$1 {X:Type} {Y:Type} value:Y = Either X Y;
回想一下 Maybe
和 Either
的工作方式,我们可以序列化不同的情况:
[CommonMsgInfo][10][StateInit][0][X]
-Message X
在一个cell中
[CommonMsgInfo][11][^StateInit][1][^X]
-Message X
带引用
CommonMsgInfo TL-B
CommonMsgInfo
CommonMsgInfo
是一系列参数的列表,定义了消息在 TON 区块链中的传递方式。
//internal message
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddressInt dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
//external incoming message
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
import_fee:Grams = CommonMsgInfo;
//external outgoing message
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
int_msg_info$0
int_msg_info
是内部消息的一种情况。这意味着它们可以在合约之间发送,且只能在合约之间发送。
用例 - 普通的跨合约消息。
nanograms$_ amount:(VarUInteger 16) = Grams;
//internal message
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddressInt dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
int_msg_info$0 | 构造函数 | 必需 | $0 标记意味着序列化 CommonMsgInfo 以 0 位开始描述内部消息。 |
ihr_disabled | 布尔 | 必需 | 超立方体路由标志位。 |
bounce | 布尔 | 必需 | 如果处理过程中出现错误,消息应该被弹回。如果消息的 flat bounce = 1,它被称为可弹回。 |
bounced | 布尔 | 必需 | 描述消息本身是弹回结果的标志位。 |
src | MsgAddressInt | 必需 | 消息发送者智能合约的地址。 |
dest | MsgAddressInt | 必需 | 消息目的地智能合约的地址。 |
value | CurrencyCollection | 必需 | 描述货币信息的结构,包括消息中转移的总资金。 |
ihr_fee | VarUInteger 16 | 必需 | 超路由交付费用 |
fwd_fee | VarUInteger 16 | 必需 | 验证者指定的转发消息费用 |
created_lt | uint64 | 必需 | 验证者指定的发送消息的逻辑时间。用于对智能合约中的动作进行排序。 |
created_at | uint32 | 必需 | Unix 时间 |
ext_in_msg_info$10
ext_in_msg_info$10
是外部传入消息的一种情况。这意味着这种类型的消息是从合约发送到链下空间的。
用例 - 钱包应用请求钱包合约。
nanograms$_ amount:(VarUInteger 16) = Grams;
//external incoming message
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
import_fee:Grams = CommonMsgInfo;
Structure | Type | Required | Description |
---|---|---|---|
ext_out_msg_info$10 | Constructor | Required | $10 标签意味着,在序列化过程中,以 "10 "位开头的 CommonMsgInfo 描述了一条外部接收的消息。 |
ihr_disabled | Bool | Required | 超级路由标志。(目前始终为真) |
src | MsgAddressExt | Required | 外部发件人的地址。 |
dest | MsgAddressInt | Required | 智能合约信息目的地的地址。 |
import_fee | VarUInteger 16 | Required | 执行和传递信息的费用。 |
ext_out_msg_info$11
ext_out_msg_info$11
是外部发送信息的一种情况。这意味着它们可以从合约发送到链外空间。
用例 - 日志。
//internal message
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
Structure | Type | Required | Description |
---|---|---|---|
ext_out_msg_info$11 | Constructor | Required | $11 标签的意思是,在序列化过程中,以 "11 "位开头的 CommonMsgInfo 描述了一条对外发送的消息。 |
src | MsgAddressInt | Required | Hyper routing flag. v |
dest | MsgAddressExt | Required | TON 中用于初始化新合约的一般结构。可以写入 cell 引用或根 cell 。 |
created_lt | uint64 | Required | 验证器指定的发送信息的逻辑时间。用于智能合约中的订购操作。 |
created_at | uint32 | Required | Unix time |
StateInit TL-B
StateInit 用于将初始数据交付给合约,并在合约部署时使用。
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
code:(Maybe ^Cell) data:(Maybe ^Cell)
library:(HashmapE 256 SimpleLib) = StateInit;
Structure | Type | Required | Description |
---|---|---|---|
split_depth | (## 5) | Optional | 高负载合约的参数,用于定义在不同分片中分割成多个实例的行为。目前 StateInit 不使用该参数。 |
special | TickTock* | Optional | 用于在区块链的每个新区块中调用智能合约。仅在主链中可用。普通用户的合约无需使用它。 |
code | Cell | Optional | 合约的序列化代码。 |
data | Cell | Optional | 合约初始数据。 |
library | HashmapE 256 SimpleLib* | Optional | 当前使用的 StatInit 不含有任何libs |
General detailed explanations for Hashmaps
MsgAddressExt TL-B
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;
MsgAddress
是地址的各种序列化方案。根据发送信息的参与者(链外或智能合约)不同,使用的结构也不同。
addr_none$00
addr_none$00
- 用于定义链外参与者的空地址。这意味着我们可以向没有唯一发件人地址的合约发送外部消息。
addr_none$00 = MsgAddressExt;
Structure | Type | Required | Description |
---|---|---|---|
addr_none$00 | Constructor | Required | $00 标记表示在序列化中,MsgAddressExt 以 "00 "位开始。这意味着整个外部地址为 00 。 |
addr_extern$01
addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;
Structure | Type | Required | Description |
---|---|---|---|
addr_extern$01 | Constructor | Required | $01 标记表示在序列化中,MsgAddressExt 以 "01 "位开始,描述一个外部地址。 |
len | ## 9 | Required | 与 uintN 相同 - 表示无符号 N 位数 |
external_address | (bits len) | Required | 地址是 len 等于前一个 len 的位字符串 |
MsgAddressInt TL-B
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
addr_std$10
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
Structure | Type | Required | Description |
---|---|---|---|
addr_std$10 | Constructor | Required | $10 标签表示,在序列化中,MsgAddressExt 以 "10 "位开始,描述一个外部地址。 |
anycast | Anycast* | Optional | 附加地址数据,目前不用于普通内部报文 |
workchain_id | int8 | Required | 放置目标地址智能合约的工作链。目前总是等于零。 |
address | (bits256) | Required | 智能合约账户 ID 号 |
addr_std$10
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
addr_std$10 | 构造函数 | 必需 | $10 标记意味着序列化 MsgAddressExt 以 10 位开始描述外部地址。 |
anycast | Anycast* | 可选 | 额外的地址数据,目前普通内部消息中未使用 |
workchain_id | int8 | 必需 | 目的地地址的智能合约所在的工作链。目前始终为零。 |
address | (bits256) | 必需 | 智能合约账户 ID 号 |
address | (bits256) | Required | 有效载荷地址(可以是账户 ID) |
addr_var$11
CurrencyCollection
nanograms$_ amount:(VarUInteger 16) = Grams;
currencies$_ grams:Grams other:ExtraCurrencyCollection
= CurrencyCollection;
Structure | Type | Required | Description |
---|---|---|---|
currencies$_ | Constructor | Required | $_ 空标签意味着,在序列化 CurrencyCollection 时,我们不会在开头添加任何位 |
grams | (VarUInteger 16) | Required | Message value in nanoTons |
other | ExtraCurrencyCollection | Optional | ExtraCurrencyCollection 是专为附加货币设计的指令,通常为空。 |
- ExtraCurrencyCollection 复杂类型,通常在信息中写成空 dict
VarUInteger n
var_uint$_ {n:#} len:(#< n) value:(uint (len * 8))
= VarUInteger n;
var_int$_ {n:#} len:(#< n) value:(int (len * 8))
= VarInteger n;
Structure | Type | Required | Description |
---|---|---|---|
var_uint$_ | Constructor | Required | var_uint$_ 空标签意味着,在序列化 CurrencyCollection 时,我们不会在开头添加任何位 |
len | uintN | Required | 下一数值的参数 len 位 |
value | (uint (len * 8)) | Optional | 以 (len * 8) 位写入的整数 uint 值 |
VarUInteger n
常规 func 内部信息
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();
常规函数信息简表
可以跳过总是被验证器覆盖的信息部分(用零位填充)。信息的发件人也会被跳过,序列化为 addr_none$00
。
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();