core/vm: implemented create2

This commit is contained in:
Jeffrey Wilcke 2017-06-15 17:05:36 +02:00
parent 797172bfe8
commit 4eae3cc25c
6 changed files with 74 additions and 4 deletions

View file

@ -236,7 +236,8 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
vmerr error
)
if contractCreation {
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
createAddressFn := vm.CreateAddressFn(st.evm.ChainConfig(), st.evm.BlockNumber, sender.Address(), st.state.GetNonce(sender.Address()), st.data)
ret, _, st.gas, vmerr = evm.Create(sender, createAddressFn, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)

View file

@ -290,7 +290,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
}
// 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) {
func (evm *EVM) Create(caller ContractRef, createAddress addressCreationFn, code []byte, gas uint64, value *big.Int) (ret []byte, _ common.Address, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, common.Address{}, gas, nil
}
@ -309,7 +309,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
evm.StateDB.SetNonce(caller.Address(), nonce+1)
snapshot := evm.StateDB.Snapshot()
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
contractAddr := createAddress()
evm.StateDB.CreateAccount(contractAddr)
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
evm.StateDB.SetNonce(contractAddr, 1)
@ -361,3 +363,5 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
// Interpreter returns the EVM interpreter
func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter }
type addressCreationFn func() common.Address

View file

@ -541,6 +541,18 @@ func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac
return nil, nil
}
func CreateAddressFn(chainConfig *params.ChainConfig, blockNumber *big.Int, senderAddress common.Address, nonce uint64, input []byte) func() common.Address {
if chainConfig.IsMetropolis(blockNumber) {
return func() common.Address {
return common.BytesToAddress(crypto.Keccak256(senderAddress.Bytes(), crypto.Keccak256(input))[12:])
}
} else {
return func() common.Address {
return crypto.CreateAddress(senderAddress, nonce)
}
}
}
func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
value = stack.pop()
@ -552,8 +564,46 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
gas -= gas / 64
}
createAddressFn := CreateAddressFn(evm.ChainConfig(), evm.BlockNumber, contract.Address(), evm.StateDB.GetNonce(contract.Address()), input)
contract.UseGas(gas)
_, addr, returnGas, suberr := evm.Create(contract, input, gas, value)
_, addr, returnGas, suberr := evm.Create(contract, createAddressFn, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
stack.push(new(big.Int))
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
stack.push(new(big.Int))
} else {
stack.push(addr.Big())
}
contract.Gas += returnGas
evm.interpreter.intPool.put(value, offset, size)
return nil, nil
}
func opCreate2(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
value = stack.pop()
salt = stack.pop()
offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64())
gas = contract.Gas
)
if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
gas -= gas / 64
}
createAddress := func() common.Address {
return common.BytesToAddress(crypto.Keccak256(contract.Address().Bytes(), salt.Bytes(), crypto.Keccak256(input))[12:])
}
contract.UseGas(gas)
_, addr, returnGas, suberr := evm.Create(contract, createAddress, input, gas, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must

View file

@ -891,6 +891,15 @@ func NewFrontierInstructionSet() [256]operation {
writes: true,
clearsReturndata: true,
},
CREATE2: {
execute: opCreate2,
gasCost: gasCreate,
validateStack: makeStackFunc(4, 1),
memorySize: memoryCreate2,
valid: true,
writes: true,
clearsReturndata: true,
},
CALL: {
execute: opCall,
gasCost: gasCall,

View file

@ -58,6 +58,10 @@ func memoryCreate(stack *Stack) *big.Int {
return calcMemSize(stack.Back(1), stack.Back(2))
}
func memoryCreate2(stack *Stack) *big.Int {
return calcMemSize(stack.Back(2), stack.Back(3))
}
func memoryCall(stack *Stack) *big.Int {
x := calcMemSize(stack.Back(5), stack.Back(6))
y := calcMemSize(stack.Back(3), stack.Back(4))

View file

@ -208,6 +208,8 @@ const (
DELEGATECALL
STATIC_CALL
CREATE2 OpCode = 0xfb
REVERT = 0xfd
SELFDESTRUCT = 0xff
)