From f26087d3a60375aa50a0e41e63f1a3727bd6040e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 7 Apr 2018 23:22:58 +0200 Subject: [PATCH] core/vm: precompiles into interpreter, 0-size alloc intpool,prealloc receipts --- core/state_processor.go | 3 ++- core/vm/evm.go | 26 +++++++++++++++----------- core/vm/interpreter.go | 20 ++++++++++++++------ core/vm/intpool.go | 4 ++++ core/vm/stack.go | 4 ++++ 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 4dc58b9dea..c57d429fa3 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -66,13 +66,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg misc.ApplyDAOHardFork(statedb) } // Iterate over and process the individual transactions + receipts = make([]*types.Receipt, len(block.Transactions())) for i, tx := range block.Transactions() { statedb.Prepare(tx.Hash(), block.Hash(), i) receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) if err != nil { return nil, nil, 0, err } - receipts = append(receipts, receipt) + receipts[i] = receipt allLogs = append(allLogs, receipt.Logs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) diff --git a/core/vm/evm.go b/core/vm/evm.go index ea46209742..e94930ae58 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -41,11 +41,7 @@ type ( // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { if contract.CodeAddr != nil { - precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { - precompiles = PrecompiledContractsByzantium - } - if p := precompiles[*contract.CodeAddr]; p != nil { + if p := evm.interpreter.precompiles[*contract.CodeAddr]; p != nil { return RunPrecompiledContract(p, input, contract) } } @@ -155,11 +151,8 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas snapshot = evm.StateDB.Snapshot() ) if !evm.StateDB.Exist(addr) { - precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { - precompiles = PrecompiledContractsByzantium - } - if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + if evm.interpreter.precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + // Calling a non existing account, don't do antything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) @@ -171,10 +164,21 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + code := evm.StateDB.GetCode(addr) + codeHash := evm.StateDB.GetCodeHash(addr) + _, isPrecompile := evm.interpreter.precompiles[addr] + + if !isPrecompile && len(code) == 0 { + // Shortcut execution if there is no code, + // but make sure to set returndata to nil + evm.interpreter.returnData = nil + return nil, gas, nil + } + // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + contract.SetCallCode(&addr, codeHash, code) start := time.Now() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7090e0261f..6dccf2616f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -20,6 +20,7 @@ import ( "fmt" "sync/atomic" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" ) @@ -51,8 +52,9 @@ type Interpreter struct { gasTable params.GasTable intPool *intPool - readOnly bool // Whether to throw on stateful modifications - returnData []byte // Last CALL's return data for subsequent reuse + readOnly bool // Whether to throw on stateful modifications + returnData []byte // Last CALL's return data for subsequent reuse + precompiles map[common.Address]PrecompiledContract } // NewInterpreter returns a new instance of the Interpreter. @@ -73,11 +75,17 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter { } } + precompiles := PrecompiledContractsHomestead + if evm.ChainConfig().IsByzantium(evm.BlockNumber) { + precompiles = PrecompiledContractsByzantium + } + return &Interpreter{ - evm: evm, - cfg: cfg, - gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), - intPool: newIntPool(), + evm: evm, + cfg: cfg, + gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), + intPool: newZerosizeIntPool(), + precompiles: precompiles, } } diff --git a/core/vm/intpool.go b/core/vm/intpool.go index 5dbda18eee..9408a67a74 100644 --- a/core/vm/intpool.go +++ b/core/vm/intpool.go @@ -32,6 +32,10 @@ func newIntPool() *intPool { return &intPool{pool: newstack()} } +func newZerosizeIntPool() *intPool { + return &intPool{pool: newZeroSizeStack()} +} + // get retrieves a big int from the pool, allocating one if the pool is empty. // Note, the returned int's value is arbitrary and will not be zeroed! func (p *intPool) get() *big.Int { diff --git a/core/vm/stack.go b/core/vm/stack.go index 9c10d50ad1..139a54ef9f 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -32,6 +32,10 @@ func newstack() *Stack { return &Stack{data: make([]*big.Int, 0, 1024)} } +func newZeroSizeStack() *Stack { + return &Stack{data: make([]*big.Int, 0)} +} + func (st *Stack) Data() []*big.Int { return st.data }