EIP-1014产生可控的智能合约地址

合约地址是怎么产生的

如果对于以太坊智能合约布署不陌生,应该会知道合约地址是依据布署者的地址和他的Nonce 来决定的。假如地址0xa1 在Nonce 为1 的交易中建立了合约,合约地址是0x01,则当他到了其他链而再次使用Nonce 1 建立合约时,就也会得到相同的合约地址0x01。

而在以太坊中,Nonce 是严格递增的,这主要是为了避免双花攻击,因此很显然的,再次布署到同一个地址是不可行的。而在EIP-1014 以前,在合约中建立的子合约也是透过这个方式达成。

EIP-1014 做了什么

前面说到合约中可以建立子合约,通常使用的指令是create,这个指令便会根据主合约的地址与合约本身的Nonce来算出子合约的地址。在EIP-1014中,提出了另外一个指令create2,接受的参数是一个salt和init code(建立合约的Byte Code)。这个指令会透过keccak256哈希,混和参数和主合约的地址来算出子合约地址。

可以留意到,salt和init code都是可控的,主合约地址是固定的,因此就让子合约地址是可控制的(当然不是说想要产生在哪里就在哪里)。在create中,Nonce是渐次增加的,所以无法重复,并且若要控制到特定Nonce需要发起多笔交易。

但是当再次布署到相同地址时,EIP-684说明若该地址Nonce不为零或者存在Byte Code时将直接抛出错误,因此若要布署到同一个位置,就必须利用Self Destruct来抹去Nonce和Byte Code。

为什么要EIP-1014

EIP-1014在起初是基于State Channel的需求,主要是因为某些状况下可能还没有要和合约做互动,但需要先知道合约地址。因此只要是需要先知道地址,但没有要立即使用的合约就十分适合通过这个指令来建立。

例如对于交易所来说,替所有要入金的人建立私钥有管理上的问题,若透过智能合约则相对有弹性,但却不是所有申请地址的人都真的会入金,此时就适合利用这个指令先行取得地址,当真的有入金时再布署合约。

如何使用EIP-1014

除了直接写Opcode以外,Solidity 0.6.2加入了利用create2建立合约的语法。使用方式是new Contract{salt: someByte}(...args),其中new Contract(...args)就是传统的布署方式,而someByte是一个bytes32。

资安疑虑

回到开头,Etherscan 之所以会标记Reinit 则是因为可能有资安的疑虑。回顾地址产生的过程,两个参数都没有限制条件,因此碰撞的可能也是存在的。如果不肖人士先布署一个正常的合约,接着通过Self Destruct 删除后再布署假的合约,就可能让没注意到的使用者上当。

但或许因为碰撞的机会太低,所以并不太构成问题。而若真的要从使用者的角度防范,可以通过检查合约有没有Self Destruct、Delegate Call、Call Code指令(如果没有的话无法删除合约,再次create2时就会被revert)来避免。

2021/4/23 补充

后来看了几个讨论,也想了一下,资安的疑虑应该远不只上面这么简单。
例如一个宣称锁定ERC-20代币的合约,虽然使用者可以检阅程式码,并确认其中解锁functionunlock()必须在uint256 block之后才能执行,并且block是无法变更的,但攻击者可能透过上述方法来消除block状态,进而提前执行unlock。

2021/4/29 再补充

回头看了讨论发现Hakka Finance的陈品老师有提到一个我没想到的点。前面提及create是根据contract address和nonce来算出新的address,而create2可以产生固定的address,因此若create、create2、selfdestruct一起用的话,就可以做到同个地址不同合约了。

举例而言,部署一个包含create2的合约0x0a,并用这个合约建立一个具有Self Destruct的合约钱包0x0b,接着用这个钱包透过create(nonce 1)建立了带有Self Destruct的合约0x0c,随后透过Self Destruct删除0x0b和0x0c,再重新部署合约钱包0x0b,此时0x0b的nonce可以再次使用1,因此虽然与原本的0x0c不同内容,却仍然可以成功发布。

本文链接地址:https://www.wwsww.cn/jishu/8952.html
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。