diff options
Diffstat (limited to 'core/vm')
-rw-r--r-- | core/vm/analysis.go | 57 | ||||
-rw-r--r-- | core/vm/analysis_test.go | 53 | ||||
-rw-r--r-- | core/vm/evm.go | 18 | ||||
-rw-r--r-- | core/vm/gas_table.go | 46 | ||||
-rw-r--r-- | core/vm/instructions.go | 76 | ||||
-rw-r--r-- | core/vm/interpreter.go | 43 | ||||
-rw-r--r-- | core/vm/jump_table.go | 4 | ||||
-rw-r--r-- | core/vm/logger.go | 26 | ||||
-rw-r--r-- | core/vm/logger_test.go | 24 |
9 files changed, 170 insertions, 177 deletions
diff --git a/core/vm/analysis.go b/core/vm/analysis.go index d5f048d1d..f9c4298d3 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -25,7 +25,7 @@ import ( // destinations stores one map per contract (keyed by hash of code). // The maps contain an entry for each location of a JUMPDEST // instruction. -type destinations map[common.Hash][]byte +type destinations map[common.Hash]bitvec // has checks whether code has a JUMPDEST at dest. func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool { @@ -38,24 +38,53 @@ func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool m, analysed := d[codehash] if !analysed { - m = jumpdests(code) + m = codeBitmap(code) d[codehash] = m } - return (m[udest/8] & (1 << (udest % 8))) != 0 + return OpCode(code[udest]) == JUMPDEST && m.codeSegment(udest) } -// jumpdests creates a map that contains an entry for each -// PC location that is a JUMPDEST instruction. -func jumpdests(code []byte) []byte { - m := make([]byte, len(code)/8+1) - for pc := uint64(0); pc < uint64(len(code)); pc++ { +// bitvec is a bit vector which maps bytes in a program. +// An unset bit means the byte is an opcode, a set bit means +// it's data (i.e. argument of PUSHxx). +type bitvec []byte + +func (bits *bitvec) set(pos uint64) { + (*bits)[pos/8] |= 0x80 >> (pos % 8) +} +func (bits *bitvec) set8(pos uint64) { + (*bits)[pos/8] |= 0xFF >> (pos % 8) + (*bits)[pos/8+1] |= ^(0xFF >> (pos % 8)) +} + +// codeSegment checks if the position is in a code segment. +func (bits *bitvec) codeSegment(pos uint64) bool { + return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0 +} + +// codeBitmap collects data locations in code. +func codeBitmap(code []byte) bitvec { + // The bitmap is 4 bytes longer than necessary, in case the code + // ends with a PUSH32, the algorithm will push zeroes onto the + // bitvector outside the bounds of the actual code. + bits := make(bitvec, len(code)/8+1+4) + for pc := uint64(0); pc < uint64(len(code)); { op := OpCode(code[pc]) - if op == JUMPDEST { - m[pc/8] |= 1 << (pc % 8) - } else if op >= PUSH1 && op <= PUSH32 { - a := uint64(op) - uint64(PUSH1) + 1 - pc += a + + if op >= PUSH1 && op <= PUSH32 { + numbits := op - PUSH1 + 1 + pc++ + for ; numbits >= 8; numbits -= 8 { + bits.set8(pc) // 8 + pc += 8 + } + for ; numbits > 0; numbits-- { + bits.set(pc) + pc++ + } + } else { + pc++ } } - return m + return bits } diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go new file mode 100644 index 000000000..a64f90ed9 --- /dev/null +++ b/core/vm/analysis_test.go @@ -0,0 +1,53 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package vm + +import "testing" + +func TestJumpDestAnalysis(t *testing.T) { + tests := []struct { + code []byte + exp byte + which int + }{ + {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0}, + {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0}, + {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0}, + {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1}, + {[]byte{byte(PUSH32)}, 0x7F, 0}, + {[]byte{byte(PUSH32)}, 0xFF, 1}, + {[]byte{byte(PUSH32)}, 0xFF, 2}, + } + for _, test := range tests { + ret := codeBitmap(test.code) + if ret[test.which] != test.exp { + t.Fatalf("expected %x, got %02x", test.exp, ret[test.which]) + } + } + +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 093c7d4c1..cbb5a03ce 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -38,7 +38,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, snapshot int, contract *Contract, input []byte) ([]byte, error) { +func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { if contract.CodeAddr != nil { precompiles := PrecompiledContractsHomestead if evm.ChainConfig().IsByzantium(evm.BlockNumber) { @@ -48,7 +48,7 @@ func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, erro return RunPrecompiledContract(p, input, contract) } } - return evm.interpreter.Run(snapshot, contract, input) + return evm.interpreter.Run(contract, input) } // Context provides the EVM with auxiliary information. Once provided @@ -104,6 +104,10 @@ type EVM struct { // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 + // callGasTemp holds the gas available for the current call. This is needed because the + // available gas is calculated in gasCall* according to the 63/64 rule and later + // applied in opCall*. + callGasTemp uint64 } // NewEVM retutrns a new EVM . The returned EVM is not thread safe and should @@ -167,7 +171,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = run(evm, snapshot, contract, input) + ret, err = run(evm, contract, input) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. @@ -211,7 +215,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = run(evm, snapshot, contract, input) + ret, err = run(evm, contract, input) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -244,7 +248,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by contract := NewContract(caller, to, nil, gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = run(evm, snapshot, contract, input) + ret, err = run(evm, contract, input) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -287,7 +291,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. - ret, err = run(evm, snapshot, contract, input) + ret, err = run(evm, contract, input) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -334,7 +338,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, contractAddr, gas, nil } - ret, err = run(evm, snapshot, contract, nil) + ret, err = run(evm, contract, nil) // check whether the max code size has been exceeded maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize // if the contract creation ran successfully and no errors were returned diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 0d8e295a5..ff109af57 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -342,19 +342,11 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. This information is otherwise lost due to the dependency on *current* - // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -374,19 +366,11 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. This information is otherwise lost due to the dependency on *current* - // available gas. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -436,18 +420,11 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil @@ -463,18 +440,11 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac return 0, errGasUintOverflow } - cg, err := callGas(gt, contract.Gas, gas, stack.Back(0)) + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err } - // Replace the stack item with the new gas calculation. This means that - // either the original item is left on the stack or the item is replaced by: - // (availableGas - gas) * 63 / 64 - // We replace the stack item so that it's available when the opCall instruction is - // called. - stack.data[stack.len()-1] = new(big.Int).SetUint64(cg) - - if gas, overflow = math.SafeAdd(gas, cg); overflow { + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, errGasUintOverflow } return gas, nil diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b6d6e22c4..1d1585fca 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -603,24 +603,20 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() - // pop gas and value of the stack. - addr, value := stack.pop(), stack.pop() + // Pop gas. The actual gas in in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) value = math.U256(value) - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // Get the arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend } - ret, returnGas, err := evm.Call(contract, address, args, gas, value) + ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value) if err != nil { stack.push(new(big.Int)) } else { @@ -636,25 +632,20 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas := stack.pop().Uint64() - // pop gas and value of the stack. - addr, value := stack.pop(), stack.pop() + // Pop gas. The actual gas is in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) value = math.U256(value) - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend } - - ret, returnGas, err := evm.CallCode(contract, address, args, gas, value) + ret, returnGas, err := evm.CallCode(contract, toAddr, args, gas, value) if err != nil { stack.push(new(big.Int)) } else { @@ -670,9 +661,13 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack } func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - - toAddr := common.BigToAddress(to) + // Pop gas. The actual gas is in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas) @@ -682,30 +677,25 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st stack.push(big.NewInt(1)) } if err == nil || err == errExecutionReverted { - memory.Set(outOffset.Uint64(), outSize.Uint64(), ret) + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) + evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // pop gas - gas := stack.pop().Uint64() - // pop address - addr := stack.pop() - // pop input size and offset - inOffset, inSize := stack.pop(), stack.pop() - // pop return size and offset - retOffset, retSize := stack.pop(), stack.pop() - - address := common.BigToAddress(addr) - - // Get the arguments from the memory + // Pop gas. The actual gas is in evm.callGasTemp. + evm.interpreter.intPool.put(stack.pop()) + gas := evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, returnGas, err := evm.StaticCall(contract, address, args, gas) + ret, returnGas, err := evm.StaticCall(contract, toAddr, args, gas) if err != nil { stack.push(new(big.Int)) } else { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 94b922c79..455f970dd 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -107,9 +107,9 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack // the return byte-slice and an error if one occurred. // // It's important to note that any errors returned by the interpreter should be -// considered a revert-and-consume-all-gas operation. No error specific checks -// should be handled to reduce complexity and errors further down the in. -func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) { +// considered a revert-and-consume-all-gas operation except for +// errExecutionReverted which means revert-and-keep-gas-left. +func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() @@ -138,16 +138,15 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret pc = uint64(0) // program counter cost uint64 // copies used by tracer - stackCopy = newstack() // stackCopy needed for Tracer since stack is mutated by 63/64 gas rule - pcCopy uint64 // needed for the deferred Tracer + pcCopy uint64 // needed for the deferred Tracer gasCopy uint64 // for Tracer to log gas remaining before execution - logged bool // deferred Tracer should ignore already logged steps + logged bool // deferred Tracer should ignore already logged steps ) contract.Input = input defer func() { if err != nil && !logged && in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) } }() @@ -156,35 +155,25 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret // the execution of one of the operations or until the done flag is set by the // parent context. for atomic.LoadInt32(&in.evm.abort) == 0 { - // Get the memory location of pc - op = contract.GetOp(pc) - if in.cfg.Debug { - logged = false - pcCopy = uint64(pc) - gasCopy = uint64(contract.Gas) - stackCopy = newstack() - for _, val := range stack.data { - stackCopy.push(val) - } + // Capture pre-execution values for tracing. + logged, pcCopy, gasCopy = false, pc, contract.Gas } - // get the operation from the jump table matching the opcode + // Get the operation from the jump table and validate the stack to ensure there are + // enough stack items available to perform the operation. + op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] - if err := in.enforceRestrictions(op, operation, stack); err != nil { - return nil, err - } - - // if the op is invalid abort the process and return an error if !operation.valid { return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) } - - // validate the stack and make sure there enough stack items available - // to perform the operation if err := operation.validateStack(stack); err != nil { return nil, err } + // If the operation is valid, enforce and write restrictions + if err := in.enforceRestrictions(op, operation, stack); err != nil { + return nil, err + } var memorySize uint64 // calculate the new memory size and expand the memory to fit @@ -214,7 +203,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret } if in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stackCopy, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) logged = true } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 9ef192fdf..a1c5ad9c6 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -42,12 +42,12 @@ type operation struct { // memorySize returns the memory size required for the operation memorySize memorySizeFunc - halts bool // indicates whether the operation shoult halt further execution + halts bool // indicates whether the operation should halt further execution jumps bool // indicates whether the program counter should not increment writes bool // determines whether this a state modifying operation valid bool // indication whether the retrieved operation is valid and known reverts bool // determines whether the operation reverts state (implicitly halts) - returns bool // determines whether the opertions sets the return data content + returns bool // determines whether the operations sets the return data content } var ( diff --git a/core/vm/logger.go b/core/vm/logger.go index 623c0d563..75309da92 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -45,7 +45,6 @@ type LogConfig struct { DisableMemory bool // disable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture - FullStorage bool // show full storage (slow) Limit int // maximum length of output, but zero means unlimited } @@ -136,14 +135,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ) l.changedValues[contract.Address()][address] = value } - // copy a snapstot of the current memory state to a new buffer + // Copy a snapstot of the current memory state to a new buffer var mem []byte if !l.cfg.DisableMemory { mem = make([]byte, len(memory.Data())) copy(mem, memory.Data()) } - - // copy a snapshot of the current stack state to a new buffer + // Copy a snapshot of the current stack state to a new buffer var stck []*big.Int if !l.cfg.DisableStack { stck = make([]*big.Int, len(stack.Data())) @@ -151,26 +149,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui stck[i] = new(big.Int).Set(item) } } - - // Copy the storage based on the settings specified in the log config. If full storage - // is disabled (default) we can use the simple Storage.Copy method, otherwise we use - // the state object to query for all values (slow process). + // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage { - if l.cfg.FullStorage { - storage = make(Storage) - // Get the contract account and loop over each storage entry. This may involve looping over - // the trie and is a very expensive process. - - env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool { - storage[key] = value - // Return true, indicating we'd like to continue. - return true - }) - } else { - // copy a snapshot of the current storage to a new container. - storage = l.changedValues[contract.Address()].Copy() - } + storage = l.changedValues[contract.Address()].Copy() } // create a new snaptshot of the EVM. log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err} diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index b6fa31132..915f7177e 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -63,32 +63,8 @@ func TestStoreCapture(t *testing.T) { if len(logger.changedValues[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) } - exp := common.BigToHash(big.NewInt(1)) if logger.changedValues[contract.Address()][index] != exp { t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index]) } } - -func TestStorageCapture(t *testing.T) { - t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it") - var ( - ref = &dummyContractRef{} - contract = NewContract(ref, ref, new(big.Int), 0) - env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) - logger = NewStructLogger(nil) - mem = NewMemory() - stack = newstack() - ) - - logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) - if ref.calledForEach { - t.Error("didn't expect for each to be called") - } - - logger = NewStructLogger(&LogConfig{FullStorage: true}) - logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) - if !ref.calledForEach { - t.Error("expected for each to be called") - } -} |