前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用以太坊的 CREATE2 操作码

如何使用以太坊的 CREATE2 操作码

作者头像
Tiny熊
发布2022-11-07 10:03:58
8170
发布2022-11-07 10:03:58
举报
文章被收录于专栏:深入浅出区块链技术

本文作者:影无双[1]

2019 年 2 月底,操作码create2被添加到以太坊虚拟机。这段操作码引入了第二种计算新智能合约地址的方法(以前只有CREATE可用)。使用CREATE2当然比最初的CREATE更复杂。不再仅仅写new Token()就行了,而必须要编写汇编代码。

译者注:现在可以不用编写汇编, solidity 0.8 之后, 可以通过指定 slat 来使用 create2 ,例如:new Token{salt: bytes32}()

但是create2有一个重要的属性,在某些特定情况下优点更突出:它不依赖于部署地址的当前状态。这意味着可以确保今天计算的合约地址与一年后相同。这一点很重要,因为在智能合约部署到该地址之前,你可以与该地址交互,并向其发送 ETH。

由于网上相关的案例很少,我想写一篇简单的博客来解释一下:

  1. CREATECREATE2都是怎么工作的
  2. 怎样在智能合约中使用CREATE2
  3. 我怎样用它来完成一个Capture The Ether[2]挑战

CREATE 操作码

这是部署合约默认的操作码,所部署合约的地址是通过哈希计算得来的:

  1. 部署地址
  2. 之前在该地址部署的交易数  — nonce
代码语言:javascript
复制
keccak256(rlp.encode(deployingAddress, nonce))[12:]

CREATE2 操作码

这个操作码本质上是另一种部署智能合约的方法,只是在计算新的合约地址时不一样。它会用到:

  1. 部署地址
  2. 部署合约代码字节码的哈希
  3. 创建者提供的随机的salt (32 字节字符串).
代码语言:javascript
复制
keccak256(0xff ++ deployingAddr ++ salt ++ keccak256(bytecode))[12:]

挑战:创建一个特定地址的合约

作为使用CREATE2的例子,我将完成Capture the Ether 中的模糊身份[3]挑战。要完成挑战中任务,需要创建一个具备下面两个属性的合约:

  1. 有一个会返回bytes32("smarx")name()函数
  2. 在地址里包含字符串badc0de

第一点很容易实现。第二步就是挑战的地方,要完成它,我们需要用到以太坊如何计算合约地址的知识——前面刚刚讨论过!

用 CREATE 来解决?

如果用CREATE操作码来完成这个挑战,我们需要生成许多私钥,对于每个私钥,我们需要计算相应的以太坊地址,例如:用一个0nonce 来计算出合约地址。?

用 CREATE2 来解决?

只需要用一个以太坊地址,我们可以遍历不同的 salt 值,直到找到一个有效值。相比生成数十万的私钥,这看起来是一个不错的选择。

Capture the Ether 创建于 2018 年,CREATE2当然不是这个问题预期的解决方案,但我觉得它是更优的选择。

解决方案

要用CREATE2找到包含badc0de的地址,我们需要:

  1. 要部署的合约的字节码
  2. 部署合约的地址 (用CREATE2的合约)
  3. salt —  我们将通过计算得出.

第一步: 要部署的合约的字节码

第一步是要获取到我们要部署在包含badc0de的地址的合约的字节码。通过这个挑战[4]的合约很简单,如下:

代码语言:javascript
复制
pragma solidity ^0.5.12;
contract BadCodeSmarx is IName {
   function callAuthenticate(address _challenge) public {
      FuzzyIdentityChallenge(_challenge).authenticate();
   }
   function name() external view returns (bytes32) {
      return bytes32("smarx");
   }
}

运行 truffle compile, 就可以在/build/BadCodeSmarx.json找到字节码:

代码语言:javascript
复制
"bytecode": "0x608060405234801561001057600080fd5b506101468061002..."

或者用Remix[5]代替 Truffle 也可以。

第二步: 用 CREATE2 的合约

现在我们可以定义一个简单的合约,给他一个 salt,并且用CREATE2来部署字节码:

代码语言:javascript
复制
contract Deployer {
  bytes contractBytecode = hex"608060405234801561001057600080fd5b5061015d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806306fdde031461003b5780637872ab4914610059575b600080fd5b61004361009d565b6040518082815260200191505060405180910390f35b61009b6004803603602081101561006f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100c5565b005b60007f736d617278000000000000000000000000000000000000000000000000000000905090565b8073ffffffffffffffffffffffffffffffffffffffff1663380c7a676040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561010d57600080fd5b505af1158015610121573d6000803e3d6000fd5b505050505056fea265627a7a72315820fb2fc7a07f0eebf799c680bb1526641d2d905c19393adf340a04e48c9b527de964736f6c634300050c0032";

  function deploy(bytes32 salt) public {
    bytes memory bytecode = contractBytecode;
    address addr;

    assembly {
      addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
    }
  }
}

在新的 solidity 里可以使用:

代码语言:javascript
复制
function deploy(bytes32 _salt) public returns (address) {
        BadCodeSmarx c = new BadCodeSmarx{salt: _salt}();
        return address(c);
    }

在 Solidity 中,create2()有 4 个参数:

  • 1: 发送给新合约的 wei 数(msg.value), 本例中是 0 .
  • 2–3: 字节码在内存中的位置
  • 4: salt —  在第三步计算。我们将它作为参数,可以在计算后再提供

create2()返回创建合约的地址——无论你是否想用它,你都必须在变量中捕获地址。

现在,合约已经准备好了。我将它部署到 Ropsten 测试网: 0xca4dfd86a86c48c5d9c228bedbeb7f218a29c94b[6]. 现在我们知道了将要部署BadCodeSmarx合约的地址,并且我们已经有字节码了,我们需要做的是计算一个可以使得地址包含badc0de的 salt 值。

第三部: 计算 salt

为了找到一个可以让地址包含badc0de的 salt 值,我们需要一个简单的脚本来遍历每一个 salt,并计算出用每个 salt 可生成的地址。

为了确保脚本计算出的地址正确,我用 salt0x00...001部署了一个合约。然后我用这个合约地址来验证脚本是否正确格式化和哈希参数,从而产生与CREATE2在链上相同的地址。

注意,地址创建的公式如下,其中[12:]表示删除前 12 个字节(以便查找地址)。

代码语言:javascript
复制
keccak256(0xff ++ deployingAddr ++ salt ++ keccak256(bytecode))[12:]

下面是我用的脚本,我用了ethereumjs-util包来执行 keccak256 哈希——你可以在Github[7]上找到它。

代码语言:javascript
复制
const eth = require('ethereumjs-util')

// 0xff ++ deployingAddress is fixed:
var string1 = '0xffca4dfd86a86c48c5d9c228bedbeb7f218a29c94b'

// Hash of the bytecode is fixed. Calculated with eth.keccak256():
var string2 = '4670da3f633e838c2746ca61c370ba3dbd257b86b28b78449f4185480e2aba51'

// In each loop, i is the value of the salt we are checking
for (var i = 0; i < 72057594037927936; i++) {
   // 1. Convert i to hex, and it pad to 32 bytes:
   var saltToBytes = i.toString(16).padStart(64, '0')

   // 2. Concatenate this between the other 2 strings
   var concatString = string1.concat(saltToBytes).concat(string2)

   // 3. Hash the resulting string
   var hashed = eth.bufferToHex(eth.keccak256(concatString))

   // 4. Remove leading 0x and 12 bytes
   // 5. Check if the result contains badc0de
   if (hashed.substr(26).includes('badc0de')) {
      console.log(saltToBytes)
      break
   }
}

运行脚本,不到 30 秒,我的 salt 值结果出来了:

代码语言:javascript
复制
0x00000000000000000000000000000000000000000000000000000000005b2bfe

然后我要做的就是执行 Deployer.deploy(0x00...005b2bfe). 看,实例BadCodeSmarx部署在了地址:

0xa905a3922a4ebfbc7d257cecdb1df04a3badc0de

引用:

  1. EIP1014 — Skinny CREATE2[8]
  2. Capture the Ether — Fuzzy Identity[9]
  3. Truffle[10]
  4. ethereumjs-util[11]

原文链接:https://hackernoon.com/using-ethereums-create2-nw2137q7

参考资料

[1]

影无双: https://learnblockchain.cn/people/58

[2]

Capture The Ether: https://capturetheether.com/?ref=hackernoon.com

[3]

Capture the Ether中的模糊身份: https://capturetheether.com/challenges/accounts/fuzzy-identity/?ref=hackernoon.com

[4]

挑战: https://capturetheether.com/challenges/accounts/fuzzy-identity/?ref=hackernoon.com

[5]

Remix: https://remix.ethereum.org/?ref=hackernoon.com

[6]

0xca4dfd86a86c48c5d9c228bedbeb7f218a29c94b: https://ropsten.etherscan.io/address/0xca4dfd86a86c48c5d9c228bedbeb7f218a29c94b?ref=hackernoon.com

[7]

Github: https://github.com/ethereumjs/ethereumjs-util?ref=hackernoon.com

[8]

EIP1014 — Skinny CREATE2: https://eips.ethereum.org/EIPS/eip-1014?ref=hackernoon.com

[9]

Capture the Ether — Fuzzy Identity: https://capturetheether.com/challenges/accounts/fuzzy-identity/?ref=hackernoon.com

[10]

Truffle: https://www.trufflesuite.com/?ref=hackernoon.com

[11]

ethereumjs-util: https://github.com/ethereumjs/ethereumjs-util?ref=hackernoon.com

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 深入浅出区块链技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • CREATE 操作码
  • CREATE2 操作码
  • 挑战:创建一个特定地址的合约
  • 用 CREATE 来解决?
  • 用 CREATE2 来解决?
  • 解决方案
  • 第一步: 要部署的合约的字节码
  • 第二步: 用 CREATE2 的合约
  • 第三部: 计算 salt
  • 引用:
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档