消息 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 区块链中的传递方式。
//内部消息
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;
//外部传入消息
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
import_fee:Grams = CommonMsgInfo;
//外部传出消息
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;
//内部消息
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;
//外部传入消息
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt
import_fee:Grams = CommonMsgInfo;
| 结构 | 类型 | 必需 | 描述 |
|--------------------|--------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------|
| ext_out_msg_info$10| 构造函数 | 必需 | $10
标记意味着序列化 CommonMsgInfo 以 10
位开始描述外部传入消息。 |
| ihr_disabled | 布尔 | 必需 | 超路由标志位。(目前始终为真) |
| src | MsgAddressExt | 必需 | 消息的外部发送者地址。 |
| dest | MsgAddressInt | 必需 | 消息目的地智能合约的地址。 |
| import_fee | VarUInteger 16 | 必需 | 执行和传递消息的费用。 |
ext_out_msg_info$11
ext_out_msg_info$11
是外部传出消息的一种情况。这意味着它们可以从合约发送到链外空间。
用例 - 日志。
//内部消息
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
ext_out_msg_info$11 | 构造函数 | 必需 | $11 标记意味着序列化 CommonMsgInfo 以 11 位开始描述外部传出消息。 |
src | MsgAddressInt | 必需 | 超路由标志位。 |
dest | MsgAddressExt | 必需 | 用于 TON 中初始化新合约的通用结构。可以写在cell引用或根cell中。 |
created_lt | uint64 | 必需 | 验证者指定的发送消息的逻辑时间。用于对智能合约中的动作进行排序。 |
created_at | uint32 | 必需 | Unix 时间 |
StateInit TL-B
StateInit 用于向合约传递初始数据,并在合约部署中使用。
_ split_depth:(Maybe (## 5)) special:(Maybe TickTock)
code:(Maybe ^Cell) data:(Maybe ^Cell)
library:(HashmapE 256 SimpleLib) = StateInit;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
split_depth | (## 5) | 可选 | 用于高负载合约的参数,定义了在不同分片中分裂为多个实例的行为。目前 StateInit 没有使用它。 |
special | TickTock* | 可选 | 用于在区块链的每个新区块中调用智能合约。仅在主链中可用。普通用户合约没有使用它。 |
code | Cell | 可选 | 合约的序列化代码。 |
data | Cell | 可选 | 合约初始数据。 |
library | HashmapE 256 SimpleLib* | 可选 | 目前使用的 StateInit 没有库 |
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;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
addr_none$00 | 构造函数 | 必需 | $00 标记意味着序列化 MsgAddressExt 以 00 位开始。这意味着整个外部地址是 00 。 |
addr_extern$01
addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;
结构 | 类型 | 必需 | 描述 |
---|
| addr_extern$01
| 构造函数 | 必需 | $01
标记意味着序列化 MsgAddressExt 以 01
位开始描述外部地址。 |
| len | ## 9 | 必需 | 与 uintN 相同 - 表示无符号 N 位数字 |
| external_address | (bits 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;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
addr_std$10 | 构造函数 | 必需 | $10 标记意味着序列化 MsgAddressExt 以 10 位开始描述外部地址。 |
anycast | Anycast* | 可选 | 额外的地址数据,目前普通内部消息中未使用 |
workchain_id | int8 | 必需 | 目的地地址的智能合约所在的工作链。目前始终为零。 |
address | (bits256) | 必需 | 智能合约账户 ID 号 |
addr_var$11
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
addr_var$11 | 构造函数 | 必需 | $11 标记意味着序列化 MsgAddressInt 以 11 位开始描述内部合约地址。 |
anycast | Anycast* | 可选 | 额外的地址数据,目前普通内部消息中未使用 |
addr_len | ## 9 | 必需 | 与 uintN 相同 - 表示无符号 N 位数字 |
workchain_id | int32 | 必需 | 目的地地址的智能合约所在的工作链。目前始终为零。 |
address | (bits256) | 必需 | 有效载荷地址(可以是账户 ID) |
基本使用类型
CurrencyCollection
nanograms$_ amount:(VarUInteger 16) = Grams;
currencies$_ grams:Grams other:ExtraCurrencyCollection
= CurrencyCollection;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
currencies$_ | 构造函数 | 必需 | $_ 空标记意味着序列化 CurrencyCollection 时我们不会在开头添加任何位 |
grams | (VarUInteger 16) | 必需 | 以nanoTons表示的消息价值 |
other | ExtraCurrencyCollection | 可选 | ExtraCurrencyCollection 是一个为附加货币设计的字典,通常为空 |
- ExtraCurrencyCollection 是一种复杂类型,通常在消息中为空字典
VarUInteger n
var_uint$_ {n:#} len:(#< n) value:(uint (len * 8))
= VarUInteger n;
var_int$_ {n:#} len:(#< n) value:(int (len * 8))
= VarInteger n;
结构 | 类型 | 必需 | 描述 |
---|---|---|---|
varuint$ | 构造函数 | 必需 | var_uint$_ 空标 |
记意味着序列化 CurrencyCollection 时我们不会在开头添加任何位 | | len | uintN | 必需 | 下一个值的位长度参数 | | value | (uint (len 8)) | 可选 | 以 (len 8) 位写入的整数值 |
消息示例
常规 func 内部消息
var msg = begin_cell()
.store_uint(0, 1) ;; 标记
.store_uint(1, 1) ;; ihr_disabled
.store_uint(1, 1) ;; 允许弹回
.store_uint(0, 1) ;; 本身不是弹回
.store_slice(source)
.store_slice(destination)
;; 序列化 CurrencyCollection(见下文)
.store_coins(amount)
.store_dict(extra_currencies)
.store_coins(0) ;; ihr_fee
.store_coins(fwd_value) ;; fwd_fee
.store_uint(cur_lt(), 64) ;; 交易的 lt
.store_uint(now(), 32) ;; 交易的 unixtime
.store_uint(0, 1) ;; 没有 init-field 标志位(Maybe)
.store_uint(0, 1) ;; 原位消息体标志位(Either)
.store_slice(msg_body)
.end_cell();
常规 func 消息简短形式
验证者总是覆盖的消息部分可以跳过(填充零位)。此处的消息发送者也被跳过,序列化为 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();