部署与确定性地址(CREATE vs CREATE2)

本文详细解释了以太坊智能合约的部署过程,包括部署交易的原理、EVM如何确定合约地址,以及如何使用CREATE和CREATE2预先计算合约地址。文章通过示例展示了如何手动计算合约地址,并解释了CREATE2在预先确定合约地址方面的重要作用。

大部分让人们对链上系统感到困惑的,不是 Solidity 本身,而是合约实际上是如何到达链上的。

一个部署交易中发生了什么?

以太坊如何决定一个合约将存在于哪里?

以及一些团队如何甚至在部署之前就知道一个地址?

这篇文章将逐步分解这个生命周期。

我们将从第一性原理开始:一个部署交易真正是什么(to = nullinit code → runtime code),以及 EVM 如何使用 CREATE 操作码推导出合约的地址。

然后,我们将把它扩展到 CREATE2,这个使地址具有确定性的诀窍,让协议能够以数学精度预先计算出资金池、钱包和工厂的输出。

到最后,你将能够:

1. 从发送者和 nonce 手动计算合约的地址

2. 使用 CREATE2 提前预测地址

3. 了解链上真正存储的是什么(以及为什么构造函数在部署后会消失)

合约部署是如何工作的

当你部署一个智能合约时,你实际上只是发送了一种特殊的交易。与 to 是现有地址的普通转账不同,部署交易设置 to = null,并使用交易的 calldata 携带合约的 init code

init code 有两个任务:

  1. 在部署时运行一次:它执行任何构造函数逻辑,设置初始存储,并处理构造函数参数。
  2. 返回 runtime code:实际的字节码,将永久地存在于合约的地址。

换句话说:

  • Init code = “引导程序”。
  • Runtime code = 留在链上的 "操作系统"。

构造函数参数

当你在 Solidity 中将参数传递给构造函数时,它们会被 ABI 编码,并附加到 init code 中。它们在部署期间被消耗掉,不会保留在合约字节码中。这就是为什么当查询 eth_getCode 时,你不会在 runtime code 中看到它们。

合约存在于哪里

EVM 在部署时确定性地计算出一个合约的地址:

使用 CREATE (默认)

address = keccak256(rlp(sender_address, sender_nonce))[12:]

意思是:取部署账户的地址 + 它的 nonce,对它们进行 RLP 编码,哈希,并使用最后 20 个字节。

大小限制

还有一个上限:合约不能大于 24 KB 的字节码 (EIP-170)。

例子

正如我们所看到的,合约代码与 CREATE opcode 的地址计算无关。让我们使用手动计算来计算部署后的合约地址。

让我们运行 Anvil (正如我们这里学到的)。

anvil

手动计算

让我们选择一个 anvil 的默认地址和私钥,并检查 nonce:

我们可以通过 eth_getTransactionCount 的原生 EVM 调用来检查 nonce,或者使用 forge 的 cast

cast nonce <YOUR-ADDRESS> --rpc-url http://localhost:8545

这将显示该地址的 nonce。现在让我们使用上面的计算公式。让我们进行 RLP 编码。你可以从之前的博客中获取 RLP 编码的代码,或者使用一些在线工具。为了清晰起见,我将使用在线工具。

按回车键或点击查看完整尺寸的图片

rlp(sender_address, sender_nonce)

注意:我这里的 nonce 在响应中是 1,在十六进制中输入你的 nonce。如果是 0,输入 0x。

现在让我们计算下一步的 keccak256,我将再次使用在线工具:

按回车键或点击查看完整尺寸的图片

keccak256(rlp(sender_address, sender_nonce))[12:]

这里的输入是上一张图片的输出,我们取哈希的最后 20 个字节

让我们借助 forge 验证地址,为此我们将运行下一行:

// <YOUR-ADDRESS> - 可以是来自 anvil 默认地址的地址
cast compute-address <YOUR-ADDRESS> --nonce 1
Computed Address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512

我们看到地址是一样的。

使用 CREATE2 进行确定性部署

正常的合约部署 (CREATE) 将新地址与部署者的帐户及其 nonce 绑定在一起。这意味着地址是顺序的,除非你跟踪 nonce,否则你无法提前知道它们。

EIP-1014 (CREATE2) 引入了一种在交易甚至被挖矿之前就以可预测的方式部署合约的方法。这就是为什么人们说 CREATE2 给你 "确定性部署"。](https://miro.medium.com/v2/resize:fit:700/0*ZF8Uesu-T_klnvZ7.png)

我在这里选择了一个随机的 salt:

0x0000000000000000000000000000000000000000000000000000000000000042

让我们用 forge 验证它:

// <YOUR-ADDRESS> - 可以是来自 anvil 默认地址的地址
cast compute-address <YOUR-ADDRESS>\
  --salt 0x0000000000000000000000000000000000000000000000000000000000000042 \
  --init-code-hash 0x9e7ceb5009cf19fc3a77cbead52c79f881b81800108a8931565aa92f9f1f5b64

// Computed Address: 0x93eFaEdEe330e749D9AF79424398204EC04F89F1

正如你所看到的,我们得到了相同的地址。

为什么这很重要

开发者在部署前保证合约地址,从而实现预先出资的钱包、确定性的 DEX 池和可验证的系统合约等功能。

ChatGPT 说:

总结

合约部署不是魔法,而是数学。

一个 to = null 的交易运行一次 init code 以生成链上的 runtime code

使用 CREATE,地址是 keccak256(rlp(sender, nonce))[12:];使用 CREATE2,地址是 keccak256(0xff ++ sender ++ salt ++ keccak256(init_code))[12:]

CREATE2 使地址在部署前可预测,非常适合确定性的资金池、钱包和工厂。

  • 原文链接: medium.com/@andrey_obruc...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Andrey Obruchkov
Andrey Obruchkov
江湖只有他的大名,没有他的介绍。