TVM 升级 2023.07
此升级于 2023 年 12 月在主网上启动,详细信息请参考 run。
c7
c7 是存储有关合约执行所需的本地 context 信息的寄存器 (如时间、lt、网络配置等)。
c7 元组从 10 扩展到 14 个元素:
- 10: 存储智能合约本身的
cell
。 - 11:
[integer, maybe_dict]
:传入消息的 TON 值,额外代币。 - 12:
integer
,存储阶段收取的费用。 - 13:
tuple
包含有关先前区块的信息。
10 当前智能合约的代码仅以可执行继续的形式在 TVM 级别呈现,无法转换为cell。这段代码通常用于授权相同类型的 neighbor 合约,例如 Jetton 钱包授权 Jetton 钱包。目前我们需要显式地代码cell存储在存储器中,这使得存储和 init_wrapper 变得更加麻烦。 使用 10 作为代码对于 tvm 的 Everscale 更新兼容。
11 当前,传入消息的值在 TVM 初始化后以堆栈形式呈现,因此如果在执行过程中需要,
则需要将其存储到全局变量或通过本地变量传递(在 funC 级别看起来像所有函数中的额外 msg_value
参数)。通过将其放在 11 元素中,我们将重复合约余额的行为:它既出现在堆栈中,也出现在 c7 中。
12 目前计算存储费用的唯一方法是在先前的交易中存储余额,以某种方式计算 prev 交易中的 gas 用量,然后与当前余额减去消息值进行比较。与此同时,经常希望考虑存储费用。
13 目前没有办法检索先前区块的数据。TON 的一个关键特性是每个结构都是 Merkle 证明友好的cell(树),此外,TVM 也是cell和 Merkle 证明友好的。通过在 TVM context中包含区块信息 ,将能够实现许多不信任的情景:合约 A 可以检查合约 B 上的交易(无需 B 的合作),可以恢复中断的消息链(当恢复合约获取并检查某些事务发生但被还原的证明时),还需要了解主链区块哈希以在链上进行某些验证 fisherman 函数功能。
区块 id 的表示如下:
[ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer ] = BlockId;
[ last_mc_blocks:[BlockId0, BlockId1, ..., BlockId15]
prev_key_block:BlockId ] : PrevBlocksInfo
包括主链的最后 16 个区块的 id(如果主链 seqno 小于 16,则为少于 16 个),以及最后的关键区块。包含有关分片区块的数据可能会导致一些数据可用性问题(由于合并/拆分事件),这并非必需(因为可以使用主链区块来证明任何事件/数 据),因此我们决定不包含。
新的操作码
在选择新操作码的 gas 成本时的经验法则是它不应少于正常成本(从操作码长度计算)且不应超过每个 gas 单位 20 ns。
用于处理新 c7 值的操作码
每个操作码消耗 26 gas,除了 PREVMCBLOCKS
和 PREVKEYBLOCK
(34 gas)。
xxxxxxxxxxxxxxxxxxxxxx Fift 语法 | xxxxxxxxx 堆栈 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 描述 |
---|---|---|
MYCODE | - c | 从 c7 检索智能合约的代码 |
INCOMINGVALUE | - t | 从 c7 检索传入消息的值 |
STORAGEFEES | - i | 从 c7 检索存储阶段费用的值 |
PREVBLOCKSINFOTUPLE | - t | 从 c7 中检索 PrevBlocksInfo: [last_mc_blocks, prev_key_block] |
PREVMCBLOCKS | - t | 仅检索 last_mc_blocks |
PREVKEYBLOCK | - t | 仅检索 prev_key_block |
GLOBALID | - i | 从网络配置的第 19 项检索 global_id |
Gas
xxxxxxxxxxxxxx Fift 语法 | xxxxxxxx 堆栈 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 描述 |
---|---|---|
GASCONSUMED | - g_c | 返回到目前为止 VM 消耗的 gas(包括此指令)。 26 gas |
算术
New variants of the division opcode (A9mscdf
) are added:
d=0
takes one additional integer from stack and adds it to the intermediate value before division/rshift. These operations return both the quotient and the remainder (just like d=3
).
还提供了静默变体(例如 QMULADDDIVMOD
或 QUIET MULADDDIVMOD
)。
如果返回值不适应 257 位整数或除数为零,非静默操作会引发整数溢出异常。静默操作返回 NaN
而不是不适应的值(如果除数为零则返回两个 NaN
)。
Gas 成本等于 10 加上操作码长度:大多数操作码为 26 gas,LSHIFT#
/RSHIFT#
额外加 8,静默额外加 8。
xxxxxxxxxxxxxxxxxxxxxx Fift 语法 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 堆栈 |
---|---|
MULADDDIVMOD | x y w z - q=floor((xy+w)/z) r=(xy+w)-zq |
MULADDDIVMODR | x y w z - q=round((xy+w)/z) r=(xy+w)-zq |
MULADDDIVMODC | x y w z - q=ceil((xy+w)/z) r=(xy+w)-zq |
ADDDIVMOD | x w z - q=floor((x+w)/z) r=(x+w)-zq |
ADDDIVMODR | x w z - q=round((x+w)/z) r=(x+w)-zq |
ADDDIVMODC | x w y - q=ceil((x+w)/z) r=(x+w)-zq |
ADDRSHIFTMOD | x w z - q=floor((x+w)/2^z) r=(x+w)-q*2^z |
ADDRSHIFTMODR | x w z - q=round((x+w)/2^z) r=(x+w)-q*2^z |
ADDRSHIFTMODC | x w z - q=ceil((x+w)/2^z) r=(x+w)-q*2^z |
z ADDRSHIFT#MOD | x w - q=floor((x+w)/2^z) r=(x+w)-q*2^z |
z ADDRSHIFTR#MOD | x w - q=round((x+w)/2^z) r=(x+w)-q*2^z |
z ADDRSHIFTC#MOD | x w - q=ceil((x+w)/2^z) r=(x+w)-q*2^z |
MULADDHIFTMOD | x y w z - q=floor((xy+w)/2^z) r=(xy+w)-q*2^z |
MULADDRSHIFTRMOD | x y w z - q=round((xy+w)/2^z) r=(xy+w)-q*2^z |
MULADDHIFTCMOD | x y w z - q=ceil((xy+w)/2^z) r=(xy+w)-q*2^z |
z MULADDRSHIFT#MOD | x y w - q=floor((xy+w)/2^z) r=(xy+w)-q*2^z |
z MULADDRSHIFTR#MOD | x y w - q=round((xy+w)/2^z) r=(xy+w)-q*2^z |
z MULADDRSHIFTC#MOD | x y w - q=ceil((xy+w)/2^z) r=(xy+w)-q*2^z |
LSHIFTADDDIVMOD | x w z y - q=floor((x*2^y+w)/z) r=(x*2^y+w)-zq |
LSHIFTADDDIVMODR | x w z y - q=round((x*2^y+w)/z) r=(x*2^y+w)-zq |
LSHIFTADDDIVMODC | x w z y - q=ceil((x*2^y+w)/z) r=(x*2^y+w)-zq |
y LSHIFT#ADDDIVMOD | x w z - q=floor((x*2^y+w)/z) r=(x*2^y+w)-zq |
y LSHIFT#ADDDIVMODR | x w z - q=round((x*2^y+w)/z) r=(x*2^y+w)-zq |
y LSHIFT#ADDDIVMODC | x w z - q=ceil((x*2^y+w)/z) r=(x*2^y+w)-zq |
堆栈操作
目前,所有堆栈操作的参数都以 256 为界。
这意味着如果堆栈深度超过 256,就很难管理深堆栈元素。
在大多数情况下,这种限制并没有安全方面的原因,也就是说,限制参数并不是为了防止过于昂贵的操作。
对于某些大规模堆栈操作,如 ROLLREV
(计算时间与参数值成线性关系),气体成本也与参数值成线性关系。
- 现在,
PICK
、ROLL
、ROLLREV
、BLKSWX
、REVX
、DROPX
、XCHGX
、CHKDEPTH
、ONLYTOPX
、ONLYX
的参数不受限制。 - 当参数较大时,
ROLL
,ROLLREV
,REVX
,ONLYTOPX
耗气量更大:额外耗气量为max(arg-255,0)
(参数小于 256 时,耗气量不变,与当前行为一致) - 对于
BLKSWX
,额外费用为max(arg1+arg2-255,0)
(这与当前行为不符,因为当前arg1
和arg2
都限制为 255)。
哈希值
目前,TVM 只提供两种散列操作:计算 cell /片的表示散列和数据的 sha256,但最多只能计算 127 字节(一个 cell 只能容纳这么多数据)。
HASHEXT[A][R]_(HASH)
系列操作被添加:
xxxxxxxxxxxxxxxxxxx Fift 语法 | xxxxxxxxxxxxxxxxxxxxxx 堆栈 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 说明 |
---|---|---|
HASHEXT_(HASH) | s_1 ... s_n n - h | 计算并返回片段(或构建器)s_1...s_n 的连接哈希值。 |
HASHEXTR_(HASH) | s_n ... s_1 n - h | 同理,但参数顺序相反。 |
HASHEXTA_(HASH) | b s_1 ... s_n n - b' | 将生成的哈希值追加到构造函数 b 中,而不是推送到堆栈中。 |
HASHEXTAR_(HASH) | b s_n ... s_1 n - b' | 参数以相反顺序给出,并将哈希值追加到生成器中。 |
仅使用 s_i
的根cell的位。
每个块 s_i
可能包含非整数数量的字节。但所有块的位的和应该是 8 的倍数。注意 TON 使用最高位优先顺序,因此当连接两个具有非整数字节的 slice 时,第一个 slice 的位变为最高位。
Gas 消耗取决于哈希字节数和所选算法。每个块额外消耗 1 gas 单位。
如果未启用 [A]
,则哈希的结果将作为无符号整数返回,如果适应 256 位,否则返回整数的元组。
可用以下算法: