引介 | GasToken:我为何不再担心 gas 价格飙升(下)
摘要: GasToken 不仅有趣,还能鼓励人们更好地理解 EVM,推动对状态维护、合约设计和 gas 市场动态的深入思考
(续前)GST2
free*()
函数调用下列 destroyChildren()
实现:
function destroyChildren(uint256 value) internal {
uint256 tail = s_tail;
// tail points to slot behind the last contract in the queue
for (uint256 i = tail + 1; i <= tail + value; i++) {
mk_contract_address(this, i).call();
}
s_tail = tail + value;
}
此处,我们遍历 child 合约,并调用这些合约中的回调函数。正如 GST2 文档所指出的那样,发行代币时,合约必须找到 child 合约的创建地址(存储这些地址的成本会很高,因此我们会即时计算这些地址)。幸运的是,这是有可能做到的,因为使用 CREATE
生成的合约地址是根据地址/账户已创建的合约数量(nonce)计算得出的,具有确定性。这些合约地址都是在 mk_contract_address
函数中计算得出的,在调用时无需任何参数或值,调用回调函数,然后就像在对应 mint()
函数中硬编码的那样,gas 退款会发送至 parent 合约。
CHI GasToken
历时 3 年,CHI GasToken 终于上线。CHI 由去中心化交易所聚合器 1inch.exchange 开发,与传统的 GasToken 类似,但是铸币效率比后者高出 1%,释放代币的效率比后者高出 10%,而且采用新的 CREATE2
操作码。该操作码可以提前通过确定性方式来创建链上合约地址,主要用于反事实的 Layer-2 解决方案。
CREATE2
操作码采用 4 个堆栈参数:endowment、memory_start、memory_length 和盐值。生成地址等于 keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]
,而非常见的将发送方地址和 nonce 进行哈希计算。由于盐值控制在用户手中,用户可以提前知道地址。
function mint(uint256 value) public {
uint256 offset = totalMinted;
assembly {
mstore(0, 0x746d4946c0e9F43F4Dee607b0eF1fA1c3318585733ff6000526015600bf30000)
for {let i := div(value, 32)} i {i := sub(i, 1)} {
pop(create2(0, 0, 30, add(offset, 0))) pop(create2(0, 0, 30, add(offset, 1)))
pop(create2(0, 0, 30, add(offset, 2))) pop(create2(0, 0, 30, add(offset, 3)))
pop(create2(0, 0, 30, add(offset, 4))) pop(create2(0, 0, 30, add(offset, 5)))
pop(create2(0, 0, 30, add(offset, 6))) pop(create2(0, 0, 30, add(offset, 7)))
pop(create2(0, 0, 30, add(offset, 8))) pop(create2(0, 0, 30, add(offset, 9)))
pop(create2(0, 0, 30, add(offset, 10))) pop(create2(0, 0, 30, add(offset, 11)))
pop(create2(0, 0, 30, add(offset, 12))) pop(create2(0, 0, 30, add(offset, 13)))
pop(create2(0, 0, 30, add(offset, 14))) pop(create2(0, 0, 30, add(offset, 15)))
pop(create2(0, 0, 30, add(offset, 16))) pop(create2(0, 0, 30, add(offset, 17)))
pop(create2(0, 0, 30, add(offset, 18))) pop(create2(0, 0, 30, add(offset, 19)))
pop(create2(0, 0, 30, add(offset, 20))) pop(create2(0, 0, 30, add(offset, 21)))
pop(create2(0, 0, 30, add(offset, 22))) pop(create2(0, 0, 30, add(offset, 23)))
pop(create2(0, 0, 30, add(offset, 24))) pop(create2(0, 0, 30, add(offset, 25)))
pop(create2(0, 0, 30, add(offset, 26))) pop(create2(0, 0, 30, add(offset, 27)))
pop(create2(0, 0, 30, add(offset, 28))) pop(create2(0, 0, 30, add(offset, 29)))
pop(create2(0, 0, 30, add(offset, 30))) pop(create2(0, 0, 30, add(offset, 31)))
offset := add(offset, 32)
}
for {let i := and(value, 0x1F)} i {i := sub(i, 1)} {
pop(create2(0, 0, 30, offset))
offset := add(offset, 1)
}
}
_mint(msg.sender, value);
totalMinted = offset;
}
这里的一般流程是,将固定的 child 合约字节码存储到 memory 中(第 4 行代码),然后使用 for 循环反复调用
CREATE2
,直到计算出对应的值为止。 CREATE2
返回已部署 child 合约的地址,我们不关心这个地址,因此我们只是将这个地址从堆栈中弹出。偏移量计数器被用来计算 child 合约的数量,并将其永久存储在第 33 行代码中。
对应的 free*()
函数调用 _destoryChildren()
:
function _destroyChildren(uint256 value) internal {
assembly {
let i := sload(totalBurned_slot)
let end := add(i, value)
sstore(totalBurned_slot, end)
let data := mload(0x40)
mstore(data, 0xff0000000000004946c0e9F43F4Dee607b0eF1fA1c0000000000000000000000)
mstore(add(data, 53), 0x3c1644c68e5d6cb380c36d1bf847fdbc0c7ac28030025a2fc5e63cce23c16348)
let ptr := add(data, 21)
for { } lt(i, end) { i := add(i, 1) } {
mstore(ptr, i)
pop(call(gas(), keccak256(data, 85), 0, 0, 0, 0, 0))
}
}
为简洁起见,
destroyChildren()
字节码的反汇编是由阅读器来完成的,总的流程与 GST2 类似,但是进行了一些修改来降低 CREATE2
目标地址查找的难度 —— 这就是效率提高 10% 的由来。
为什么要关注 GasToken
2020 年之前,几乎没有人公开关注 GasToken 1、2 或 CHI。然而,到了 2020 年,DeFi 热潮引发了 “gas 大战”,gas 费飙升至 500 GWei 以上,并触发了 Geth 的默认设置内存池溢出 —— 导致以太坊交易丢失!
然而,在这个默默无闻的以太坊小工具上出现的讽刺事件是,当网络拥堵最严重时,GasToken 的价格(以美元计价)也在 Uniswap 等去中心化交易所上达到顶峰。因此,卖出 GasToken 来赚取利润的生意,因为 gas 本身价格的高涨,并不令人轻松;而且,小数额的卖出,很容易错过一段时间内的高点。(注:这绝不是投资建议。)
根据定义,GasToken 当然是最具实用性的代币,因为它直接充当网络的交易池。有些人建议使用 GasToken 来实现一种基于合约的公益品融资。或许这比 Near Protocol 强制规定的智能合约开发者收取基础交易 gas 成本总额的 30%(后者也存在自身的问题,例如,鼓励效率低下的智能合约设计)更好。
非正统 GasToken
DefiSaver 旨在为用户提供更加友好的方式,以便其与不同的 DeFi 协议交互。这一工具通过函数修饰符在合约中使用 GasToken。这个修饰符使用正统的 GST2 合约,目前在几乎所有 DefiSaver 包装的协议函数调用中都使用硬编码的值进行调用。一个有趣的分析是,随着时间的推移,这种方法可以节省多少交易费。Tenderly 等新型以太坊工具凭借其优越的 GasProfiler 和仿真模式使之成为可能。
虽然这种硬编码模式肯定有效,但是经过改进的设计需要依赖当前 gas 价格——这时,chainlink 等信息输入机制就派上了用场。设计上必须谨慎,因为这可能会带来很高的成本(lastestAnswer()
的成本约为 15000 gas)。
其它著名用例/设计有 GasToken 工厂和将 CHI GasToken 纳入 MakerDAO 质押品的提案。
铸造 GasToken
那么,为什么没有更多合约使用 GasToken?状态膨胀(即,节点的存储量大小)的问题越来越严重,或许这就是 GasToken 被视为有害状态操作的原因。就像一些持纯粹主义的比特币持有者拒绝采用 OP_RETURN
比特币脚本操作码来 存储/销毁 比特币区块链上的任意数据的做法,称这会导致不必要的状态膨胀。
状态租赁这一想法似乎已经被放弃,一方面是因为可能会引入过多的复杂性,另一方面是因为无状态客户端的出现和 ETH 2.0 有望引入另一种状态存储架构。虽然可能性很低,但是 ETH 1.0 的矿工可能会抵制状态膨胀,选择审查类似 GasToken 的机制的交易,因为状态膨胀会直接增加运行全节点的成本,尽管增加的成本很少 —— 256 比特的存储插槽的真正成本几乎可以忽略不计。
另一个更加实际的因素是,GasToken 从中长期来看存在操作码重新定价的风险。
没有风险的修改提议
由于伊斯坦布尔硬分叉引入了 EIP 2200,存储操作码已经过大规模重组,不过这些更改涉及特定情况下的 记账/计量方式;SLOAD
的 gas 价格上涨,SSTORE
则没有。
最近,EIP-2929 提议了一些修改。这些修改源自一篇帝国理工学院(Imperial College London)的论文。此前,这篇论文还被用来详细分析操作码的 gas 定价(过低)问题。这个 EIP 提议增加交易首次使用 SLOAD
、 *CALL
、 BALANCE
、 EXT*
和 SELFDESTRUCT
所需的 gas 成本,因为考虑到这些操作码读取的状态量和访问状态所需的时间,它们都存在定价过低的问题。
特别要指出的是,这个 EIP 流程提议增加交易范围内的 addresses_accessed
和 accessed_storage_keys
集合,以便区分冷热状态访问,向冷账户/状态访问收取额外的 2600 gas,并将热状态存储访问的 gas 成本减少至 100 gas。
由于 COLD_SLOAD_COST
是基于 SSTORE_RESET_GAS
收费的,基于存储的 GasToken1 的经济机制就不那么有吸引力了。GasToken 1 似乎不常用,因为它只能在较小的 GasPrice 率范围内节省成本。所以再见了,GasToken1。
对 SELFDESTRUCT
的修改提议不会影响 GST2 或 CHI 和 free*()
部分,因为 gas 退款的接收方 parent 合约已经在 addresses_accessed
集合内。但是,如果该机制采用不同的设计,如,接收退款的地址不在 addresses_accessed
集合内,那就不同了。但是,所有这些都不是断言 GasToken 的经济模型会改变,或是使之不那么 有效/可行。
Gas 在 2020 年发生了什么 ?
Eth 1.x 社区有一个提议是,增加一个记账单位,以便进行计算。这个单位被命名为 oil(石油),与 gas 并行运作(操作码成本和初始限制相同),但是存在以下几点关键区别:
-
如果交易在执行过程中将 oil 耗尽,交易可还原。在 gas 机制中,如果 gas 耗尽,交易只能还原当前帧,并让调用者检查结果。相比之下,如果 oil 耗尽,整个交易都会还原(所有帧)。 -
不同于 gas,调用者合约无法限制被调用者合约可以使用的 oil 数量。 -
交易所退回的以太币数量将基于剩余的 oil 而非 gas 来计算。
玩梗时间
- 要找到一张有趣的燃气表的图片真的太难了 -
(完)
原文链接:
https://medium.com/coinmonks/gastoken-or-how-i-learned-to-stop-worrying-and-love-gas-price-surges-6aaee9fb0ba3
作者: Aodhgan Gleeson
作者:以太坊爱好者;来自链得得内容开放平台“得得号”,本文仅代表作者观点,不代表链得得官方立场凡“得得号”文章,原创性和内容的真实性由投稿人保证,如果稿件因抄袭、作假等行为导致的法律后果,由投稿人本人负责得得号平台发布文章,如有侵权、违规及其他不当言论内容,请广大读者监督,一经证实,平台会立即下线。如遇文章内容问题,请发送至邮箱:linggeqi@chaindd.com
评论(0)
Oh! no
您是否确认要删除该条评论吗?