From 4eae3cc25cb1646ea43853c1fe6c567f78fb86c6 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 15 Jun 2017 17:05:36 +0200 Subject: [PATCH] core/vm: implemented create2 --- core/state_transition.go | 3 ++- core/vm/evm.go | 8 +++++-- core/vm/instructions.go | 52 +++++++++++++++++++++++++++++++++++++++- core/vm/jump_table.go | 9 +++++++ core/vm/memory_table.go | 4 ++++ core/vm/opcodes.go | 2 ++ 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 0ae9d7fcbf..c6d7a5b68a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -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) diff --git a/core/vm/evm.go b/core/vm/evm.go index 37d502d264..38916cc956 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index db8a940faf..6103314c21 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -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 diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index cdc82e05c3..cdb64afaf5 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -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, diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 9f6533bbce..01e43fbc00 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -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)) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 3ded3e219d..49789a22dd 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -208,6 +208,8 @@ const ( DELEGATECALL STATIC_CALL + CREATE2 OpCode = 0xfb + REVERT = 0xfd SELFDESTRUCT = 0xff )