1.1 create
在合约中部署合约可以使用create字节码,该字节码会通过部署合约的合约地址,nonce值来计算新合约的地址,计算公式为:keccak256(rlp.encode(address, nonce))[12:]
在go-ethereum中的vm/evm.go中有该字节码的实现:// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
// CreateAddress creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Keccak256(data)[12:])
}
在这个公式中,address和nonce都不能被控制,这意味着无法控制新合约的地址。
1.2 create2
create2是在君士坦丁堡硬分叉中新加入的一个字节码,该字节码不再使用nonce,并且加入了可被控制的参数salt和init code。计算公式如下:keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]
address为部署合约的工厂合约的地址,salt是一个32bytes长的混淆值,init_code是合约的创建字节码。// Create2 creates a new contract using code as deployment code.
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}
// CreateAddress2 creates an ethereum address given the address bytes, initial
// contract code hash and a salt.
func CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
return common.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
}
合约的创建字节码部署合约的字节码,但是可以通过solidiity中的creationCode获取:bytecodes = type(Test).creationCode;
现在create2中的参数都已经能被控制,理论上只要爆破salt,就可以获取想要的合约地址。