From 2f02ac80430e8a2d3c3541c0f1aeae37c29ef8ab Mon Sep 17 00:00:00 2001 From: AnilChinchawale Date: Fri, 22 Mar 2019 17:39:27 +0530 Subject: [PATCH] EVM Changed added --- cmd/evm/disasm.go | 2 +- cmd/evm/internal/compiler/compiler.go | 2 +- cmd/evm/main.go | 6 - cmd/evm/runner.go | 56 +- cmd/evm/staterunner.go | 12 +- core/vm/analysis.go | 28 - core/vm/analysis_test.go | 26 +- core/vm/common.go | 30 +- core/vm/contract.go | 69 +- core/vm/contracts.go | 2 +- core/vm/errors.go | 2 + core/vm/evm.go | 167 ++- core/vm/gas.go | 9 +- core/vm/gas_table.go | 128 ++- core/vm/gen_structlog.go | 52 +- core/vm/instructions.go | 509 +++++---- core/vm/instructions_test.go | 192 +++- core/vm/interface.go | 4 +- core/vm/interpreter.go | 129 ++- core/vm/intpool.go | 41 +- core/vm/intpool_test.go | 55 + core/vm/jump_table.go | 1375 ++++++++++++++----------- core/vm/logger.go | 42 +- core/vm/logger_json.go | 87 ++ core/vm/logger_test.go | 12 +- core/vm/memory.go | 46 +- core/vm/memory_table.go | 116 ++- core/vm/opcodes.go | 49 +- core/vm/runtime/runtime.go | 14 +- core/vm/runtime/runtime_test.go | 60 +- core/vm/stack.go | 4 +- core/vm/stack_table.go | 32 +- 32 files changed, 2145 insertions(+), 1213 deletions(-) create mode 100644 core/vm/intpool_test.go create mode 100644 core/vm/logger_json.go diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go index 69f611e39b..4a442cf784 100644 --- a/cmd/evm/disasm.go +++ b/cmd/evm/disasm.go @@ -44,7 +44,7 @@ func disasmCmd(ctx *cli.Context) error { return err } - code := strings.TrimSpace(string(in)) + code := strings.TrimSpace(string(in[:])) fmt.Printf("%v\n", code) return asm.PrintDisassembled(code) } diff --git a/cmd/evm/internal/compiler/compiler.go b/cmd/evm/internal/compiler/compiler.go index 54981b6697..753ca62264 100644 --- a/cmd/evm/internal/compiler/compiler.go +++ b/cmd/evm/internal/compiler/compiler.go @@ -25,7 +25,7 @@ import ( func Compile(fn string, src []byte, debug bool) (string, error) { compiler := asm.NewCompiler(debug) - compiler.Feed(asm.Lex(src, debug)) + compiler.Feed(asm.Lex(fn, src, debug)) bin, compileErrors := compiler.Compile() if len(compileErrors) > 0 { diff --git a/cmd/evm/main.go b/cmd/evm/main.go index ebac2047aa..a59cb1fb84 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -110,11 +110,6 @@ var ( Name: "nostack", Usage: "disable stack output", } - EVMInterpreterFlag = cli.StringFlag{ - Name: "vm.evm", - Usage: "External EVM configuration (default = built-in interpreter)", - Value: "", - } ) func init() { @@ -138,7 +133,6 @@ func init() { ReceiverFlag, DisableMemoryFlag, DisableStackFlag, - EVMInterpreterFlag, } app.Commands = []cli.Command{ compileCommand, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index bc5d00cfbe..8a7399840c 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -21,20 +21,20 @@ import ( "encoding/json" "fmt" "io/ioutil" - "math/big" "os" - goruntime "runtime" "runtime/pprof" "time" + goruntime "runtime" + "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" cli "gopkg.in/urfave/cli.v1" @@ -76,20 +76,18 @@ func runCmd(ctx *cli.Context) error { logconfig := &vm.LogConfig{ DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - Debug: ctx.GlobalBool(DebugFlag.Name), } var ( - tracer vm.Tracer - debugLogger *vm.StructLogger - statedb *state.StateDB - chainConfig *params.ChainConfig - sender = common.BytesToAddress([]byte("sender")) - receiver = common.BytesToAddress([]byte("receiver")) - genesisConfig *core.Genesis + tracer vm.Tracer + debugLogger *vm.StructLogger + statedb *state.StateDB + chainConfig *params.ChainConfig + sender = common.StringToAddress("sender") + receiver = common.StringToAddress("receiver") ) if ctx.GlobalBool(MachineFlag.Name) { - tracer = vm.NewJSONLogger(logconfig, os.Stdout) + tracer = NewJSONLogger(logconfig, os.Stdout) } else if ctx.GlobalBool(DebugFlag.Name) { debugLogger = vm.NewStructLogger(logconfig) tracer = debugLogger @@ -98,14 +96,13 @@ func runCmd(ctx *cli.Context) error { } if ctx.GlobalString(GenesisFlag.Name) != "" { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) - genesisConfig = gen - db := rawdb.NewMemoryDatabase() + db, _ := ethdb.NewMemDatabase() genesis := gen.ToBlock(db) statedb, _ = state.New(genesis.Root(), state.NewDatabase(db)) chainConfig = gen.Config } else { - statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) - genesisConfig = new(core.Genesis) + db, _ := ethdb.NewMemDatabase() + statedb, _ = state.New(common.Hash{}, state.NewDatabase(db)) } if ctx.GlobalString(SenderFlag.Name) != "" { sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) @@ -157,23 +154,15 @@ func runCmd(ctx *cli.Context) error { } initialGas := ctx.GlobalUint64(GasFlag.Name) - if genesisConfig.GasLimit != 0 { - initialGas = genesisConfig.GasLimit - } runtimeConfig := runtime.Config{ - Origin: sender, - State: statedb, - GasLimit: initialGas, - GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), - Value: utils.GlobalBig(ctx, ValueFlag.Name), - Difficulty: genesisConfig.Difficulty, - Time: new(big.Int).SetUint64(genesisConfig.Timestamp), - Coinbase: genesisConfig.Coinbase, - BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), + Origin: sender, + State: statedb, + GasLimit: initialGas, + GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), + Value: utils.GlobalBig(ctx, ValueFlag.Name), EVMConfig: vm.Config{ - Tracer: tracer, - Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), - EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name), + Tracer: tracer, + Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), }, } @@ -207,7 +196,6 @@ func runCmd(ctx *cli.Context) error { execTime := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit(true) statedb.IntermediateRoot(true) fmt.Println(string(statedb.Dump())) } @@ -246,7 +234,9 @@ Gas used: %d `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) } - if tracer == nil { + if tracer != nil { + tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err) + } else { fmt.Printf("0x%x\n", ret) if err != nil { fmt.Printf(" error: %v\n", err) diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index b3c69d9b9d..071ea94ad0 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -38,8 +38,6 @@ var stateTestCommand = cli.Command{ ArgsUsage: "", } -// StatetestResult contains the execution status after running a state test, any -// error that might have occurred and a dump of the final state if requested. type StatetestResult struct { Name string `json:"name"` Pass bool `json:"pass"` @@ -68,7 +66,7 @@ func stateTestCmd(ctx *cli.Context) error { ) switch { case ctx.GlobalBool(MachineFlag.Name): - tracer = vm.NewJSONLogger(config, os.Stderr) + tracer = NewJSONLogger(config, os.Stderr) case ctx.GlobalBool(DebugFlag.Name): debugger = vm.NewStructLogger(config) @@ -97,10 +95,6 @@ func stateTestCmd(ctx *cli.Context) error { // Run the test and aggregate the result result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true} state, err := test.Run(st, cfg) - // print state root for evmlab tracing - if ctx.GlobalBool(MachineFlag.Name) && state != nil { - fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false)) - } if err != nil { // Test failed, mark as so and dump any state to aid debugging result.Pass, result.Error = false, err.Error() @@ -109,6 +103,10 @@ func stateTestCmd(ctx *cli.Context) error { result.State = &dump } } + // print state root for evmlab tracing (already committed above, so no need to delete objects again + if ctx.GlobalBool(MachineFlag.Name) && state != nil { + fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false)) + } results = append(results, *result) diff --git a/core/vm/analysis.go b/core/vm/analysis.go index f9c4298d39..0ccf47b979 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -16,34 +16,6 @@ package vm -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// 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]bitvec - -// has checks whether code has a JUMPDEST at dest. -func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool { - // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. - // Don't bother checking for JUMPDEST in that case. - udest := dest.Uint64() - if dest.BitLen() >= 63 || udest >= uint64(len(code)) { - return false - } - - m, analysed := d[codehash] - if !analysed { - m = codeBitmap(code) - d[codehash] = m - } - return OpCode(code[udest]) == JUMPDEST && m.codeSegment(udest) -} - // 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). diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index a64f90ed9c..fd2d744d87 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -16,7 +16,11 @@ package vm -import "testing" +import ( + "testing" + + "github.com/ethereum/go-ethereum/crypto" +) func TestJumpDestAnalysis(t *testing.T) { tests := []struct { @@ -49,5 +53,23 @@ func TestJumpDestAnalysis(t *testing.T) { t.Fatalf("expected %x, got %02x", test.exp, ret[test.which]) } } - +} + +func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { + // 1.4 ms + code := make([]byte, 1200000) + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + codeBitmap(code) + } + bench.StopTimer() +} +func BenchmarkJumpdestHashing_1200k(bench *testing.B) { + // 4 ms + code := make([]byte, 1200000) + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + crypto.Keccak256Hash(code) + } + bench.StopTimer() } diff --git a/core/vm/common.go b/core/vm/common.go index 17de38decb..d592a9410d 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -23,13 +23,31 @@ import ( "github.com/ethereum/go-ethereum/common/math" ) -// calculates the memory size required for a step -func calcMemSize(off, l *big.Int) *big.Int { - if l.Sign() == 0 { - return common.Big0 +// calcMemSize64 calculates the required memory size, and returns +// the size and whether the result overflowed uint64 +func calcMemSize64(off, l *big.Int) (uint64, bool) { + if !l.IsUint64() { + return 0, true } + return calcMemSize64WithUint(off, l.Uint64()) +} - return new(big.Int).Add(off, l) +// calcMemSize64WithUint calculates the required memory size, and returns +// the size and whether the result overflowed uint64 +// Identical to calcMemSize64, but length is a uint64 +func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) { + // if length is zero, memsize is always zero, regardless of offset + if length64 == 0 { + return 0, false + } + // Check that offset doesn't overflow + if !off.IsUint64() { + return 0, true + } + offset64 := off.Uint64() + val := offset64 + length64 + // if value < either of it's parts, then it overflowed + return val, val < offset64 } // getData returns a slice from the data based on the start and size and pads @@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte { // bigUint64 returns the integer casted to a uint64 and returns whether it // overflowed in the process. func bigUint64(v *big.Int) (uint64, bool) { - return v.Uint64(), v.BitLen() > 64 + return v.Uint64(), !v.IsUint64() } // toWordSize returns the ceiled word size required for memory expansion. diff --git a/core/vm/contract.go b/core/vm/contract.go index 66748e8215..20baa6e751 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -40,7 +40,7 @@ type AccountRef common.Address func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } // Contract represents an ethereum contract in the state database. It contains -// the the contract code, calling arguments. Contract implements ContractRef +// the contract code, calling arguments. Contract implements ContractRef type Contract struct { // CallerAddress is the result of the caller which initialised this // contract. However when the "call method" is delegated this value @@ -49,7 +49,8 @@ type Contract struct { caller ContractRef self ContractRef - jumpdests destinations // result of JUMPDEST analysis. + jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. + analysis bitvec // Locally cached result of JUMPDEST analysis Code []byte CodeHash common.Hash @@ -58,21 +59,17 @@ type Contract struct { Gas uint64 value *big.Int - - Args []byte - - DelegateCall bool } // NewContract returns a new contract environment for the execution of EVM. func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { - c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} + c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} if parent, ok := caller.(*Contract); ok { // Reuse JUMPDEST analysis from parent context if available. c.jumpdests = parent.jumpdests } else { - c.jumpdests = make(destinations) + c.jumpdests = make(map[common.Hash]bitvec) } // Gas should be a pointer so it can safely be reduced through the run @@ -84,10 +81,42 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin return c } +func (c *Contract) validJumpdest(dest *big.Int) bool { + udest := dest.Uint64() + // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. + // Don't bother checking for JUMPDEST in that case. + if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) { + return false + } + // Only JUMPDESTs allowed for destinations + if OpCode(c.Code[udest]) != JUMPDEST { + return false + } + // Do we have a contract hash already? + if c.CodeHash != (common.Hash{}) { + // Does parent context have the analysis? + analysis, exist := c.jumpdests[c.CodeHash] + if !exist { + // Do the analysis and save in parent context + // We do not need to store it in c.analysis + analysis = codeBitmap(c.Code) + c.jumpdests[c.CodeHash] = analysis + } + return analysis.codeSegment(udest) + } + // We don't have the code hash, most likely a piece of initcode not already + // in state trie. In that case, we do an analysis, and save it locally, so + // we don't have to recalculate it for every JUMP instruction in the execution + // However, we don't save it within the parent context + if c.analysis == nil { + c.analysis = codeBitmap(c.Code) + } + return c.analysis.codeSegment(udest) +} + // AsDelegate sets the contract to be a delegate call and returns the current // contract (for chaining calls) func (c *Contract) AsDelegate() *Contract { - c.DelegateCall = true // NOTE: caller must, at all times be a contract. It should never happen // that caller is something other than a Contract. parent := c.caller.(*Contract) @@ -138,16 +167,18 @@ func (c *Contract) Value() *big.Int { return c.value } -// SetCode sets the code to the contract -func (self *Contract) SetCode(hash common.Hash, code []byte) { - self.Code = code - self.CodeHash = hash -} - // SetCallCode sets the code of the contract and address of the backing data // object -func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { - self.Code = code - self.CodeHash = hash - self.CodeAddr = addr +func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { + c.Code = code + c.CodeHash = hash + c.CodeAddr = addr +} + +// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash. +// In case hash is not provided, the jumpdest analysis will not be saved to the parent context +func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) { + c.Code = codeAndHash.code + c.CodeHash = codeAndHash.hash + c.CodeAddr = addr } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 237450ea96..20b741f8f1 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -116,7 +116,7 @@ func (c *sha256hash) Run(input []byte) ([]byte, error) { return h[:], nil } -// RIPMED160 implemented as a native contract. +// RIPEMD160 implemented as a native contract. type ripemd160hash struct{} // RequiredGas returns the gas required to execute the pre-compiled contract. diff --git a/core/vm/errors.go b/core/vm/errors.go index b19366be0e..7f88f324ea 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -18,6 +18,7 @@ package vm import "errors" +// List execution errors var ( ErrOutOfGas = errors.New("out of gas") ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas") @@ -25,4 +26,5 @@ var ( ErrTraceLimitReached = errors.New("the number of logs reached the specified limit") ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract address collision") + ErrNoCompatibleInterpreter = errors.New("no compatible interpreter") ) diff --git a/core/vm/evm.go b/core/vm/evm.go index 96676c314a..70e1cd1b87 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -31,15 +31,17 @@ import ( var emptyCodeHash = crypto.Keccak256Hash(nil) type ( + // CanTransferFunc is the signature of a transfer guard function CanTransferFunc func(StateDB, common.Address, *big.Int) bool - TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + // TransferFunc is the signature of a transfer function + TransferFunc func(StateDB, common.Address, common.Address, *big.Int) // GetHashFunc returns the nth block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash ) // 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) { +func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { precompiles := PrecompiledContractsHomestead if evm.ChainConfig().IsByzantium(evm.BlockNumber) { @@ -49,7 +51,20 @@ func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { return RunPrecompiledContract(p, input, contract) } } - return evm.interpreter.Run(contract, input) + for _, interpreter := range evm.interpreters { + if interpreter.CanRun(contract.Code) { + if evm.interpreter != interpreter { + // Ensure that the interpreter pointer is set back + // to its current value upon return. + defer func(i Interpreter) { + evm.interpreter = i + }(evm.interpreter) + evm.interpreter = interpreter + } + return interpreter.Run(contract, input, readOnly) + } + } + return nil, ErrNoCompatibleInterpreter } // Context provides the EVM with auxiliary information. Once provided @@ -101,7 +116,8 @@ type EVM struct { vmConfig Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. - interpreter *Interpreter + interpreters []Interpreter + interpreter Interpreter // abort is used to abort the EVM calling operations // NOTE: must be set atomically abort int32 @@ -115,14 +131,35 @@ type EVM struct { // only ever be used *once*. func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ - Context: ctx, - StateDB: statedb, - vmConfig: vmConfig, - chainConfig: chainConfig, - chainRules: chainConfig.Rules(ctx.BlockNumber), + Context: ctx, + StateDB: statedb, + vmConfig: vmConfig, + chainConfig: chainConfig, + chainRules: chainConfig.Rules(ctx.BlockNumber), + interpreters: make([]Interpreter, 0, 1), } - evm.interpreter = NewInterpreter(evm, vmConfig) + if chainConfig.IsEWASM(ctx.BlockNumber) { + // to be implemented by EVM-C and Wagon PRs. + // if vmConfig.EWASMInterpreter != "" { + // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") + // path := extIntOpts[0] + // options := []string{} + // if len(extIntOpts) > 1 { + // options = extIntOpts[1..] + // } + // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options)) + // } else { + // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig)) + // } + panic("No supported ewasm interpreter yet.") + } + + // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here + // as we always want to have the built-in EVM as the failover option. + evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) + evm.interpreter = evm.interpreters[0] + return evm } @@ -132,6 +169,11 @@ func (evm *EVM) Cancel() { atomic.StoreInt32(&evm.abort, 1) } +// Interpreter returns the current interpreter +func (evm *EVM) Interpreter() Interpreter { + return evm.interpreter +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an @@ -160,17 +202,22 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas precompiles = PrecompiledContractsByzantium } if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + // Calling a non existing account, don't do anything, but ping the tracer + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) + } return nil, gas, nil } evm.StateDB.CreateAccount(addr) } evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) - // 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)) + // Even if the account has no code, we need to continue because it might be a precompile start := time.Now() // Capture the tracer start/end events in debug mode @@ -181,7 +228,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) }() } - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, false) // 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 @@ -220,13 +267,12 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, snapshot = evm.StateDB.Snapshot() to = AccountRef(caller.Address()) ) - // 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. + // 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)) - ret, err = run(evm, contract, input) + ret, err = run(evm, contract, input, false) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -259,7 +305,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, contract, input) + ret, err = run(evm, contract, input, false) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -281,28 +327,26 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - // Make sure the readonly is only set if we aren't in readonly yet - // this makes also sure that the readonly flag isn't removed for - // child calls. - if !evm.interpreter.readOnly { - evm.interpreter.readOnly = true - defer func() { evm.interpreter.readOnly = false }() - } var ( to = AccountRef(addr) snapshot = evm.StateDB.Snapshot() ) - // 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. + // 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, new(big.Int), gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + // We do an AddBalance of zero here, just in order to trigger a touch. + // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, + // but is the correct thing to do and matters on other networks, in tests, and potential + // future scenarios + evm.StateDB.AddBalance(addr, bigZero) + // 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, contract, input) + ret, err = run(evm, contract, input, true) if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -312,9 +356,20 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte return ret, contract.Gas, err } -// 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) { +type codeAndHash struct { + code []byte + hash common.Hash +} +func (c *codeAndHash) Hash() common.Hash { + if c.hash == (common.Hash{}) { + c.hash = crypto.Keccak256Hash(c.code) + } + return c.hash +} + +// create creates a new contract using code as deployment code. +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { @@ -323,39 +378,37 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } - // Ensure there's no existing contract already at the designated address nonce := evm.StateDB.GetNonce(caller.Address()) evm.StateDB.SetNonce(caller.Address(), nonce+1) - contractAddr = crypto.CreateAddress(caller.Address(), nonce) - contractHash := evm.StateDB.GetCodeHash(contractAddr) - if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + // Ensure there's no existing contract already at the designated address + contractHash := evm.StateDB.GetCodeHash(address) + if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state snapshot := evm.StateDB.Snapshot() - evm.StateDB.CreateAccount(contractAddr) + evm.StateDB.CreateAccount(address) if evm.ChainConfig().IsEIP158(evm.BlockNumber) { - evm.StateDB.SetNonce(contractAddr, 1) + evm.StateDB.SetNonce(address, 1) } - evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value) + evm.Transfer(evm.StateDB, caller.Address(), address, value) - // 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, AccountRef(contractAddr), value, gas) - contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) + // 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, AccountRef(address), value, gas) + contract.SetCodeOptionalHash(&address, codeAndHash) if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, contractAddr, gas, nil + return nil, address, gas, nil } if evm.vmConfig.Debug && evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value) + evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) } start := time.Now() - ret, err = run(evm, contract, nil) + ret, err := run(evm, contract, nil, false) // check whether the max code size has been exceeded maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize @@ -366,7 +419,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if err == nil && !maxCodeSizeExceeded { createDataGas := uint64(len(ret)) * params.CreateDataGas if contract.UseGas(createDataGas) { - evm.StateDB.SetCode(contractAddr, ret) + evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas } @@ -388,11 +441,25 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } - return ret, contractAddr, contract.Gas, err + return ret, address, contract.Gas, err + +} + +// 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) +} + +// Create2 creates a new contract using code as deployment code. +// +// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(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 *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + codeAndHash := &codeAndHash{code: code} + contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes()) + return evm.create(caller, codeAndHash, gas, endowment, contractAddr) } // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } - -// Interpreter returns the EVM interpreter -func (evm *EVM) Interpreter() *Interpreter { return evm.interpreter } diff --git a/core/vm/gas.go b/core/vm/gas.go index dd64d5f178..022a84f243 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) +// Gas costs const ( GasQuickStep uint64 = 2 GasFastestStep uint64 = 3 @@ -29,10 +30,6 @@ const ( GasMidStep uint64 = 8 GasSlowStep uint64 = 10 GasExtStep uint64 = 20 - - GasReturn uint64 = 0 - GasStop uint64 = 0 - GasContractByte uint64 = 200 ) // calcGas returns the actual gas cost of the call. @@ -46,11 +43,11 @@ func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big. // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 // is smaller than the requested amount. Therefor we return the new gas instead // of returning an error. - if callCost.BitLen() > 64 || gas < callCost.Uint64() { + if !callCost.IsUint64() || gas < callCost.Uint64() { return gas, nil } } - if callCost.BitLen() > 64 { + if !callCost.IsUint64() { return 0, errGasUintOverflow } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 83adba428e..6400c1324f 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// memoryGasCosts calculates the quadratic gas for memory expansion. It does so +// memoryGasCost calculates the quadratic gas for memory expansion. It does so // only for the memory region that is expanded, not the total memory. func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { @@ -57,12 +57,6 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { return 0, nil } -func constGasFunc(gas uint64) gasFunc { - return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gas, nil - } -} - func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := memoryGasCost(mem, memorySize) if err != nil { @@ -117,24 +111,71 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack * func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( - y, x = stack.Back(1), stack.Back(0) - val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + y, x = stack.Back(1), stack.Back(0) + current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) ) - // This checks for 3 scenario's and calculates gas accordingly - // 1. From a zero-value address to a non-zero value (NEW VALUE) - // 2. From a non-zero value address to a zero-value address (DELETE) - // 3. From a non-zero to a non-zero (CHANGE) - if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { - // 0 => non 0 - return params.SstoreSetGas, nil - } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { - evm.StateDB.AddRefund(params.SstoreRefundGas) - - return params.SstoreClearGas, nil - } else { - // non 0 => non 0 (or 0 => 0) - return params.SstoreResetGas, nil + // The legacy gas metering only takes into consideration the current state + // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) + // OR Constantinople is not active + if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople { + // This checks for 3 scenario's and calculates gas accordingly: + // + // 1. From a zero-value address to a non-zero value (NEW VALUE) + // 2. From a non-zero value address to a zero-value address (DELETE) + // 3. From a non-zero to a non-zero (CHANGE) + switch { + case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 + return params.SstoreSetGas, nil + case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0 + evm.StateDB.AddRefund(params.SstoreRefundGas) + return params.SstoreClearGas, nil + default: // non 0 => non 0 (or 0 => 0) + return params.SstoreResetGas, nil + } } + // The new gas metering is based on net gas costs (EIP-1283): + // + // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + // 2. If current value does not equal new value + // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + // 2.1.1. If original value is 0, 20000 gas is deducted. + // 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter. + // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + // 2.2.1. If original value is not 0 + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + // 2.2.2. If original value equals new value (this storage slot is reset) + // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. + // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + value := common.BigToHash(y) + if current == value { // noop (1) + return params.NetSstoreNoopGas, nil + } + original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + if original == current { + if original == (common.Hash{}) { // create slot (2.1.1) + return params.NetSstoreInitGas, nil + } + if value == (common.Hash{}) { // delete slot (2.1.2b) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) + } + return params.NetSstoreCleanGas, nil // write existing slot (2.1.2) + } + if original != (common.Hash{}) { + if current == (common.Hash{}) { // recreate slot (2.2.1.1) + evm.StateDB.SubRefund(params.NetSstoreClearRefund) + } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) + } + } + if original == value { + if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + evm.StateDB.AddRefund(params.NetSstoreResetClearRefund) + } else { // reset to original existing slot (2.2.2.2) + evm.StateDB.AddRefund(params.NetSstoreResetRefund) + } + } + return params.NetSstoreDirtyGas, nil } func makeGasLog(n uint64) gasFunc { @@ -241,6 +282,10 @@ func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Sta return gas, nil } +func gasExtCodeHash(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.ExtcodeHash, nil +} + func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var overflow bool gas, err := memoryGasCost(mem, memorySize) @@ -289,6 +334,29 @@ func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m return gas, nil } +func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow { + return 0, errGasUintOverflow + } + wordGas, overflow := bigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + + return gas, nil +} + func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return gt.Balance, nil } @@ -308,7 +376,7 @@ func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas overflow bool ) - if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { + if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow { return 0, errGasUintOverflow } return gas, nil @@ -447,15 +515,3 @@ func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stac } return gas, nil } - -func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil -} - -func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil -} - -func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return GasFastestStep, nil -} diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index ade3ca6318..726012e59e 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -13,20 +13,22 @@ import ( var _ = (*structLogMarshaling)(nil) +// MarshalJSON marshals as JSON. func (s StructLog) MarshalJSON() ([]byte, error) { type StructLog struct { - Pc uint64 `json:"pc"` - Op OpCode `json:"op"` - Gas math.HexOrDecimal64 `json:"gas"` - GasCost math.HexOrDecimal64 `json:"gasCost"` - Memory hexutil.Bytes `json:"memory"` - MemorySize int `json:"memSize"` - Stack []*math.HexOrDecimal256 `json:"stack"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth int `json:"depth"` - Err error `json:"-"` - OpName string `json:"opName"` - ErrorString string `json:"error"` + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas math.HexOrDecimal64 `json:"gas"` + GasCost math.HexOrDecimal64 `json:"gasCost"` + Memory hexutil.Bytes `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` + OpName string `json:"opName"` + ErrorString string `json:"error"` } var enc StructLog enc.Pc = s.Pc @@ -43,24 +45,27 @@ func (s StructLog) MarshalJSON() ([]byte, error) { } enc.Storage = s.Storage enc.Depth = s.Depth + enc.RefundCounter = s.RefundCounter enc.Err = s.Err enc.OpName = s.OpName() enc.ErrorString = s.ErrorString() return json.Marshal(&enc) } +// UnmarshalJSON unmarshals from JSON. func (s *StructLog) UnmarshalJSON(input []byte) error { type StructLog struct { - Pc *uint64 `json:"pc"` - Op *OpCode `json:"op"` - Gas *math.HexOrDecimal64 `json:"gas"` - GasCost *math.HexOrDecimal64 `json:"gasCost"` - Memory *hexutil.Bytes `json:"memory"` - MemorySize *int `json:"memSize"` - Stack []*math.HexOrDecimal256 `json:"stack"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth *int `json:"depth"` - Err error `json:"-"` + Pc *uint64 `json:"pc"` + Op *OpCode `json:"op"` + Gas *math.HexOrDecimal64 `json:"gas"` + GasCost *math.HexOrDecimal64 `json:"gasCost"` + Memory *hexutil.Bytes `json:"memory"` + MemorySize *int `json:"memSize"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth *int `json:"depth"` + RefundCounter *uint64 `json:"refund"` + Err error `json:"-"` } var dec StructLog if err := json.Unmarshal(input, &dec); err != nil { @@ -96,6 +101,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { if dec.Depth != nil { s.Depth = *dec.Depth } + if dec.RefundCounter != nil { + s.RefundCounter = *dec.RefundCounter + } if dec.Err != nil { s.Err = dec.Err } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1e494a0eb8..2a062d7e77 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -18,65 +18,64 @@ package vm import ( "errors" - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "golang.org/x/crypto/sha3" ) var ( bigZero = new(big.Int) tt255 = math.BigPow(2, 255) - tt256 = math.BigPow(2, 256) errWriteProtection = errors.New("evm: write protection") errReturnDataOutOfBounds = errors.New("evm: return data out of bounds") errExecutionReverted = errors.New("evm: execution reverted") errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded") + errInvalidJump = errors.New("evm: invalid jump destination") ) -func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() math.U256(y.Add(x, y)) - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opSub(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSub(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() math.U256(y.Sub(x, y)) - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opMul(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMul(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(math.U256(x.Mul(x, y))) - evm.interpreter.intPool.put(y) + interpreter.intPool.put(y) return nil, nil } -func opDiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opDiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() if y.Sign() != 0 { math.U256(y.Div(x, y)) } else { y.SetUint64(0) } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSdiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := math.S256(stack.pop()), math.S256(stack.pop()) - res := evm.interpreter.intPool.getZero() + res := interpreter.intPool.getZero() if y.Sign() == 0 || x.Sign() == 0 { stack.push(res) @@ -89,24 +88,24 @@ func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } stack.push(math.U256(res)) } - evm.interpreter.intPool.put(x, y) + interpreter.intPool.put(x, y) return nil, nil } -func opMod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() if y.Sign() == 0 { stack.push(x.SetUint64(0)) } else { stack.push(math.U256(x.Mod(x, y))) } - evm.interpreter.intPool.put(y) + interpreter.intPool.put(y) return nil, nil } -func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := math.S256(stack.pop()), math.S256(stack.pop()) - res := evm.interpreter.intPool.getZero() + res := interpreter.intPool.getZero() if y.Sign() == 0 { stack.push(res) @@ -119,20 +118,32 @@ func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } stack.push(math.U256(res)) } - evm.interpreter.intPool.put(x, y) + interpreter.intPool.put(x, y) return nil, nil } -func opExp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { base, exponent := stack.pop(), stack.pop() - stack.push(math.Exp(base, exponent)) - - evm.interpreter.intPool.put(base, exponent) - + // some shortcuts + cmpToOne := exponent.Cmp(big1) + if cmpToOne < 0 { // Exponent is zero + // x ^ 0 == 1 + stack.push(base.SetUint64(1)) + } else if base.Sign() == 0 { + // 0 ^ y, if y != 0, == 0 + stack.push(base.SetUint64(0)) + } else if cmpToOne == 0 { // Exponent is one + // x ^ 1 == x + stack.push(base) + } else { + stack.push(math.Exp(base, exponent)) + interpreter.intPool.put(base) + } + interpreter.intPool.put(exponent) return nil, nil } -func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSignExtend(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { bit := uint(back.Uint64()*8 + 7) @@ -148,39 +159,39 @@ func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac stack.push(math.U256(num)) } - evm.interpreter.intPool.put(back) + interpreter.intPool.put(back) return nil, nil } -func opNot(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opNot(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.peek() math.U256(x.Not(x)) return nil, nil } -func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opLt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() if x.Cmp(y) < 0 { y.SetUint64(1) } else { y.SetUint64(0) } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opGt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() if x.Cmp(y) > 0 { y.SetUint64(1) } else { y.SetUint64(0) } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSlt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() xSign := x.Cmp(tt255) @@ -200,11 +211,11 @@ func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac y.SetUint64(0) } } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSgt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() xSign := x.Cmp(tt255) @@ -224,22 +235,22 @@ func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac y.SetUint64(0) } } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opEq(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() if x.Cmp(y) == 0 { y.SetUint64(1) } else { y.SetUint64(0) } - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opIszero(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.peek() if x.Sign() > 0 { x.SetUint64(0) @@ -249,31 +260,31 @@ func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S return nil, nil } -func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAnd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() stack.push(x.And(x, y)) - evm.interpreter.intPool.put(y) + interpreter.intPool.put(y) return nil, nil } -func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opOr(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() y.Or(x, y) - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opXor(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() y.Xor(x, y) - evm.interpreter.intPool.put(x) + interpreter.intPool.put(x) return nil, nil } -func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opByte(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { th, val := stack.pop(), stack.peek() if th.Cmp(common.Big32) < 0 { b := math.Byte(val, 32, int(th.Int64())) @@ -281,11 +292,11 @@ func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } else { val.SetUint64(0) } - evm.interpreter.intPool.put(th) + interpreter.intPool.put(th) return nil, nil } -func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opAddmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(bigZero) > 0 { x.Add(x, y) @@ -294,11 +305,11 @@ func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } else { stack.push(x.SetUint64(0)) } - evm.interpreter.intPool.put(y, z) + interpreter.intPool.put(y, z) return nil, nil } -func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMulmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y, z := stack.pop(), stack.pop(), stack.pop() if z.Cmp(bigZero) > 0 { x.Mul(x, y) @@ -307,17 +318,17 @@ func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S } else { stack.push(x.SetUint64(0)) } - evm.interpreter.intPool.put(y, z) + interpreter.intPool.put(y, z) return nil, nil } // opSHL implements Shift Left // The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the left by arg1 number of bits. -func opSHL(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSHL(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards shift, value := math.U256(stack.pop()), math.U256(stack.peek()) - defer evm.interpreter.intPool.put(shift) // First operand back into the pool + defer interpreter.intPool.put(shift) // First operand back into the pool if shift.Cmp(common.Big256) >= 0 { value.SetUint64(0) @@ -332,10 +343,10 @@ func opSHL(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac // opSHR implements Logical Shift Right // The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. -func opSHR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSHR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards shift, value := math.U256(stack.pop()), math.U256(stack.peek()) - defer evm.interpreter.intPool.put(shift) // First operand back into the pool + defer interpreter.intPool.put(shift) // First operand back into the pool if shift.Cmp(common.Big256) >= 0 { value.SetUint64(0) @@ -350,13 +361,13 @@ func opSHR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac // opSAR implements Arithmetic Shift Right // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. -func opSAR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one shift, value := math.U256(stack.pop()), math.S256(stack.pop()) - defer evm.interpreter.intPool.put(shift) // First operand back into the pool + defer interpreter.intPool.put(shift) // First operand back into the pool if shift.Cmp(common.Big256) >= 0 { - if value.Sign() > 0 { + if value.Sign() >= 0 { value.SetUint64(0) } else { value.SetInt64(-1) @@ -371,57 +382,65 @@ func opSAR(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stac return nil, nil } -func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() data := memory.Get(offset.Int64(), size.Int64()) - hash := crypto.Keccak256(data) - if evm.vmConfig.EnablePreimageRecording { - evm.StateDB.AddPreimage(common.BytesToHash(hash), data) + if interpreter.hasher == nil { + interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) + } else { + interpreter.hasher.Reset() } - stack.push(evm.interpreter.intPool.get().SetBytes(hash)) + interpreter.hasher.Write(data) + interpreter.hasher.Read(interpreter.hasherBuf[:]) - evm.interpreter.intPool.put(offset, size) + evm := interpreter.evm + if evm.vmConfig.EnablePreimageRecording { + evm.StateDB.AddPreimage(interpreter.hasherBuf, data) + } + stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:])) + + interpreter.intPool.put(offset, size) return nil, nil } -func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(contract.Address().Big()) +func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes())) return nil, nil } -func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.Set(evm.StateDB.GetBalance(common.BigToAddress(slot))) + slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot))) return nil, nil } -func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.Origin.Big()) +func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil } -func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(contract.Caller().Big()) +func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes())) return nil, nil } -func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().Set(contract.value)) +func opCallValue(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().Set(contract.value)) return nil, nil } -func opCallDataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetBytes(getDataBig(contract.Input, stack.pop(), big32))) +func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetBytes(getDataBig(contract.Input, stack.pop(), big32))) return nil, nil } -func opCallDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) +func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) return nil, nil } -func opCallDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( memOffset = stack.pop() dataOffset = stack.pop() @@ -429,48 +448,48 @@ func opCallDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st ) memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(contract.Input, dataOffset, length)) - evm.interpreter.intPool.put(memOffset, dataOffset, length) + interpreter.intPool.put(memOffset, dataOffset, length) return nil, nil } -func opReturnDataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetUint64(uint64(len(evm.interpreter.returnData)))) +func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } -func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( memOffset = stack.pop() dataOffset = stack.pop() length = stack.pop() - end = evm.interpreter.intPool.get().Add(dataOffset, length) + end = interpreter.intPool.get().Add(dataOffset, length) ) - defer evm.interpreter.intPool.put(memOffset, dataOffset, length, end) + defer interpreter.intPool.put(memOffset, dataOffset, length, end) - if end.BitLen() > 64 || uint64(len(evm.interpreter.returnData)) < end.Uint64() { + if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() { return nil, errReturnDataOutOfBounds } - memory.Set(memOffset.Uint64(), length.Uint64(), evm.interpreter.returnData[dataOffset.Uint64():end.Uint64()]) + memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()]) return nil, nil } -func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.SetUint64(uint64(evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) return nil, nil } -func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code))) +func opCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + l := interpreter.intPool.get().SetInt64(int64(len(contract.Code))) stack.push(l) return nil, nil } -func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( memOffset = stack.pop() codeOffset = stack.pop() @@ -479,186 +498,221 @@ func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack codeCopy := getDataBig(contract.Code, codeOffset, length) memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - evm.interpreter.intPool.put(memOffset, codeOffset, length) + interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } -func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( addr = common.BigToAddress(stack.pop()) memOffset = stack.pop() codeOffset = stack.pop() length = stack.pop() ) - codeCopy := getDataBig(evm.StateDB.GetCode(addr), codeOffset, length) + codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length) memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - evm.interpreter.intPool.put(memOffset, codeOffset, length) + interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } -func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice)) +// opExtCodeHash returns the code hash of a specified account. +// There are several cases when the function is called, while we can relay everything +// to `state.GetCodeHash` function to ensure the correctness. +// (1) Caller tries to get the code hash of a normal contract account, state +// should return the relative code hash and set it as the result. +// +// (2) Caller tries to get the code hash of a non-existent account, state should +// return common.Hash{} and zero will be set as the result. +// +// (3) Caller tries to get the code hash for an account without contract code, +// state should return emptyCodeHash(0xc5d246...) as the result. +// +// (4) Caller tries to get the code hash of a precompiled account, the result +// should be zero or emptyCodeHash. +// +// It is worth noting that in order to avoid unnecessary create and clean, +// all precompile accounts on mainnet have been transferred 1 wei, so the return +// here should be emptyCodeHash. +// If the precompile account is not transferred any amount on a private or +// customized chain, the return value will be zero. +// +// (5) Caller tries to get the code hash for an account which is marked as suicided +// in the current transaction, the code hash of this account should be returned. +// +// (6) Caller tries to get the code hash for an account which is marked as deleted, +// this account should be regarded as a non-existent account and zero should be returned. +func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + slot := stack.peek() + address := common.BigToAddress(slot) + if interpreter.evm.StateDB.Empty(address) { + slot.SetUint64(0) + } else { + slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) + } return nil, nil } -func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opGasprice(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice)) + return nil, nil +} + +func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { num := stack.pop() - n := evm.interpreter.intPool.get().Sub(evm.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(evm.BlockNumber) < 0 { - stack.push(evm.GetHash(num.Uint64()).Big()) + n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 { + stack.push(interpreter.evm.GetHash(num.Uint64()).Big()) } else { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } - evm.interpreter.intPool.put(num, n) + interpreter.intPool.put(num, n) return nil, nil } -func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.Coinbase.Big()) +func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes())) return nil, nil } -func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(evm.interpreter.intPool.get().Set(evm.Time))) +func opTimestamp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time))) return nil, nil } -func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(evm.interpreter.intPool.get().Set(evm.BlockNumber))) +func opNumber(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber))) return nil, nil } -func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(evm.interpreter.intPool.get().Set(evm.Difficulty))) +func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty))) return nil, nil } -func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(evm.interpreter.intPool.get().SetUint64(evm.GasLimit))) +func opGasLimit(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit))) return nil, nil } -func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - evm.interpreter.intPool.put(stack.pop()) +func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + interpreter.intPool.put(stack.pop()) return nil, nil } -func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset := stack.pop() - val := evm.interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32)) + val := interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32)) stack.push(val) - evm.interpreter.intPool.put(offset) + interpreter.intPool.put(offset) return nil, nil } -func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // pop value of the stack mStart, val := stack.pop(), stack.pop() - memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32)) + memory.Set32(mStart.Uint64(), val) - evm.interpreter.intPool.put(mStart, val) + interpreter.intPool.put(mStart, val) return nil, nil } -func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { off, val := stack.pop().Int64(), stack.pop().Int64() memory.store[off] = byte(val & 0xff) return nil, nil } -func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - loc := common.BigToHash(stack.pop()) - val := evm.StateDB.GetState(contract.Address(), loc).Big() - stack.push(val) +func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + loc := stack.peek() + val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) + loc.SetBytes(val.Bytes()) return nil, nil } -func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) val := stack.pop() - evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) - evm.interpreter.intPool.put(val) + interpreter.intPool.put(val) return nil, nil } -func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos := stack.pop() - if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { - nop := contract.GetOp(pos.Uint64()) - return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + if !contract.validJumpdest(pos) { + return nil, errInvalidJump } *pc = pos.Uint64() - evm.interpreter.intPool.put(pos) + interpreter.intPool.put(pos) return nil, nil } -func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos, cond := stack.pop(), stack.pop() if cond.Sign() != 0 { - if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) { - nop := contract.GetOp(pos.Uint64()) - return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + if !contract.validJumpdest(pos) { + return nil, errInvalidJump } *pc = pos.Uint64() } else { *pc++ } - evm.interpreter.intPool.put(pos, cond) + interpreter.intPool.put(pos, cond) return nil, nil } -func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opJumpdest(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetUint64(*pc)) +func opPc(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetUint64(*pc)) return nil, nil } -func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len()))) +func opMsize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetInt64(int64(memory.Len()))) return nil, nil } -func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas)) +func opGas(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().SetUint64(contract.Gas)) return nil, nil } -func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( value = stack.pop() offset, size = stack.pop(), stack.pop() input = memory.Get(offset.Int64(), size.Int64()) gas = contract.Gas ) - if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + if interpreter.evm.ChainConfig().IsEIP150(interpreter.evm.BlockNumber) { gas -= gas / 64 } contract.UseGas(gas) - res, addr, returnGas, suberr := evm.Create(contract, input, gas, value) + res, addr, returnGas, suberr := interpreter.evm.Create(contract, 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(evm.interpreter.intPool.getZero()) + if interpreter.evm.ChainConfig().IsHomestead(interpreter.evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas { + stack.push(interpreter.intPool.getZero()) } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } else { - stack.push(addr.Big()) + stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) } contract.Gas += returnGas - evm.interpreter.intPool.put(value, offset, size) + interpreter.intPool.put(value, offset, size) if suberr == errExecutionReverted { return res, nil @@ -666,10 +720,38 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S return nil, nil } -func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // Pop gas. The actual gas in in evm.callGasTemp. - evm.interpreter.intPool.put(stack.pop()) - gas := evm.callGasTemp +func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + endowment = stack.pop() + offset, size = stack.pop(), stack.pop() + salt = stack.pop() + input = memory.Get(offset.Int64(), size.Int64()) + gas = contract.Gas + ) + + // Apply EIP150 + gas -= gas / 64 + contract.UseGas(gas) + res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt) + // Push item on the stack based on the returned error. + if suberr != nil { + stack.push(interpreter.intPool.getZero()) + } else { + stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + } + contract.Gas += returnGas + interpreter.intPool.put(endowment, offset, size, salt) + + if suberr == errExecutionReverted { + return res, nil + } + return nil, nil +} + +func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // Pop gas. The actual gas in interpreter.evm.callGasTemp. + interpreter.intPool.put(stack.pop()) + gas := interpreter.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) @@ -680,25 +762,25 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta if value.Sign() != 0 { gas += params.CallStipend } - ret, returnGas, err := evm.Call(contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value) if err != nil { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(interpreter.intPool.get().SetUint64(1)) } if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) + interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } -func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // Pop gas. The actual gas is in evm.callGasTemp. - evm.interpreter.intPool.put(stack.pop()) - gas := evm.callGasTemp +func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.put(stack.pop()) + gas := interpreter.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) @@ -709,96 +791,96 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack if value.Sign() != 0 { gas += params.CallStipend } - ret, returnGas, err := evm.CallCode(contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value) if err != nil { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(interpreter.intPool.get().SetUint64(1)) } if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) + interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } -func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // Pop gas. The actual gas is in evm.callGasTemp. - evm.interpreter.intPool.put(stack.pop()) - gas := evm.callGasTemp +func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.put(stack.pop()) + gas := interpreter.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) + ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas) if err != nil { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(interpreter.intPool.get().SetUint64(1)) } if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) + 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. The actual gas is in evm.callGasTemp. - evm.interpreter.intPool.put(stack.pop()) - gas := evm.callGasTemp +func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.put(stack.pop()) + gas := interpreter.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, toAddr, args, gas) + ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas) if err != nil { - stack.push(evm.interpreter.intPool.getZero()) + stack.push(interpreter.intPool.getZero()) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(interpreter.intPool.get().SetUint64(1)) } if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Gas += returnGas - evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) + interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } -func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opReturn(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := memory.GetPtr(offset.Int64(), size.Int64()) - evm.interpreter.intPool.put(offset, size) + interpreter.intPool.put(offset, size) return ret, nil } -func opRevert(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opRevert(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() ret := memory.GetPtr(offset.Int64(), size.Int64()) - evm.interpreter.intPool.put(offset, size) + interpreter.intPool.put(offset, size) return ret, nil } -func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { +func opStop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return nil, nil } -func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - balance := evm.StateDB.GetBalance(contract.Address()) - evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) +func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + balance := interpreter.evm.StateDB.GetBalance(contract.Address()) + interpreter.evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) - evm.StateDB.Suicide(contract.Address()) + interpreter.evm.StateDB.Suicide(contract.Address()) return nil, nil } @@ -806,7 +888,7 @@ func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * // make log instruction function func makeLog(size int) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { @@ -814,23 +896,38 @@ func makeLog(size int) executionFunc { } d := memory.Get(mStart.Int64(), mSize.Int64()) - evm.StateDB.AddLog(&types.Log{ + interpreter.evm.StateDB.AddLog(&types.Log{ Address: contract.Address(), Topics: topics, Data: d, // This is a non-consensus field, but assigned here because // core/state doesn't know the current block number. - BlockNumber: evm.BlockNumber.Uint64(), + BlockNumber: interpreter.evm.BlockNumber.Uint64(), }) - evm.interpreter.intPool.put(mStart, mSize) + interpreter.intPool.put(mStart, mSize) return nil, nil } } +// opPush1 is a specialized version of pushN +func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + var ( + codeLen = uint64(len(contract.Code)) + integer = interpreter.intPool.get() + ) + *pc += 1 + if *pc < codeLen { + stack.push(integer.SetUint64(uint64(contract.Code[*pc]))) + } else { + stack.push(integer.SetUint64(0)) + } + return nil, nil +} + // make push instruction function func makePush(size uint64, pushByteSize int) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { codeLen := len(contract.Code) startMin := codeLen @@ -843,7 +940,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { endMin = startMin + pushByteSize } - integer := evm.interpreter.intPool.get() + integer := interpreter.intPool.get() stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize))) *pc += size @@ -851,10 +948,10 @@ func makePush(size uint64, pushByteSize int) executionFunc { } } -// make push instruction function +// make dup instruction function func makeDup(size int64) executionFunc { - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.dup(evm.interpreter.intPool, int(size)) + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.dup(interpreter.intPool, int(size)) return nil, nil } } @@ -862,8 +959,8 @@ func makeDup(size int64) executionFunc { // make swap instruction function func makeSwap(size int64) executionFunc { // switch n + 1 otherwise n would be swapped with n - size += 1 - return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + size++ + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { stack.swap(int(size)) return nil, nil } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 0de558612c..8a48d765dd 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -17,10 +17,12 @@ package vm import ( + "bytes" "math/big" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -30,19 +32,23 @@ type twoOperandTest struct { expected string } -func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) { +func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() for i, test := range tests { x := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y)) expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected)) stack.push(x) stack.push(shift) - opFn(&pc, env, nil, nil, stack) + opFn(&pc, evmInterpreter, nil, nil, stack) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual) @@ -50,13 +56,13 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64 // Check pool usage // 1.pool is not allowed to contain anything on the stack // 2.pool is not allowed to contain the same pointers twice - if env.interpreter.intPool.pool.len() > 0 { + if evmInterpreter.intPool.pool.len() > 0 { poolvals := make(map[*big.Int]struct{}) poolvals[actual] = struct{}{} - for env.interpreter.intPool.pool.len() > 0 { - key := env.interpreter.intPool.get() + for evmInterpreter.intPool.pool.len() > 0 { + key := evmInterpreter.intPool.get() if _, exist := poolvals[key]; exist { t.Errorf("Testcase %d, pool contains double-entry", i) } @@ -64,13 +70,18 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64 } } } + poolOfIntPools.put(evmInterpreter.intPool) } func TestByteOp(t *testing.T) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() tests := []struct { v string th uint64 @@ -91,12 +102,13 @@ func TestByteOp(t *testing.T) { th := new(big.Int).SetUint64(test.th) stack.push(val) stack.push(th) - opByte(&pc, env, nil, nil, stack) + opByte(&pc, evmInterpreter, nil, nil, stack) actual := stack.pop() if actual.Cmp(test.expected) != 0 { t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual) } } + poolOfIntPools.put(evmInterpreter.intPool) } func TestSHL(t *testing.T) { @@ -196,11 +208,15 @@ func TestSLT(t *testing.T) { testTwoOperandOp(t, tests, opSlt) } -func opBenchmark(bench *testing.B, op func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) { +func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack = newstack() + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() // convert args byteArgs := make([][]byte, len(args)) for i, arg := range args { @@ -213,9 +229,10 @@ func opBenchmark(bench *testing.B, op func(pc *uint64, evm *EVM, contract *Contr a := new(big.Int).SetBytes(arg) stack.push(a) } - op(&pc, env, nil, nil, stack) + op(&pc, evmInterpreter, nil, nil, stack) stack.pop() } + poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpAdd64(b *testing.B) { @@ -425,3 +442,148 @@ func BenchmarkOpIsZero(b *testing.B) { x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" opBenchmark(b, opIszero, x) } + +func TestOpMstore(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() + mem.Resize(64) + pc := uint64(0) + v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" + stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) + opMstore(&pc, evmInterpreter, nil, mem, stack) + if got := common.Bytes2Hex(mem.Get(0, 32)); got != v { + t.Fatalf("Mstore fail, got %v, expected %v", got, v) + } + stack.pushN(big.NewInt(0x1), big.NewInt(0)) + opMstore(&pc, evmInterpreter, nil, mem, stack) + if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { + t.Fatalf("Mstore failed to overwrite previous value") + } + poolOfIntPools.put(evmInterpreter.intPool) +} + +func BenchmarkOpMstore(bench *testing.B) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() + mem.Resize(64) + pc := uint64(0) + memStart := big.NewInt(0) + value := big.NewInt(0x1337) + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + stack.pushN(value, memStart) + opMstore(&pc, evmInterpreter, nil, mem, stack) + } + poolOfIntPools.put(evmInterpreter.intPool) +} + +func BenchmarkOpSHA3(bench *testing.B) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + env.interpreter = evmInterpreter + evmInterpreter.intPool = poolOfIntPools.get() + mem.Resize(32) + pc := uint64(0) + start := big.NewInt(0) + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + stack.pushN(big.NewInt(32), start) + opSha3(&pc, evmInterpreter, nil, mem, stack) + } + poolOfIntPools.put(evmInterpreter.intPool) +} + +func TestCreate2Addreses(t *testing.T) { + type testcase struct { + origin string + salt string + code string + expected string + } + + for i, tt := range []testcase{ + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x00", + expected: "0x4d1a2e2bb4f88f0250f26ffff098b0b30b26bf38", + }, + { + origin: "0xdeadbeef00000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x00", + expected: "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3", + }, + { + origin: "0xdeadbeef00000000000000000000000000000000", + salt: "0xfeed000000000000000000000000000000000000", + code: "0x00", + expected: "0xD04116cDd17beBE565EB2422F2497E06cC1C9833", + }, + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0xdeadbeef", + expected: "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e", + }, + { + origin: "0x00000000000000000000000000000000deadbeef", + salt: "0xcafebabe", + code: "0xdeadbeef", + expected: "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7", + }, + { + origin: "0x00000000000000000000000000000000deadbeef", + salt: "0xcafebabe", + code: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + expected: "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C", + }, + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x", + expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0", + }, + } { + + origin := common.BytesToAddress(common.FromHex(tt.origin)) + salt := common.BytesToHash(common.FromHex(tt.salt)) + code := common.FromHex(tt.code) + codeHash := crypto.Keccak256(code) + address := crypto.CreateAddress2(origin, salt, codeHash) + /* + stack := newstack() + // salt, but we don't need that for this test + stack.push(big.NewInt(int64(len(code)))) //size + stack.push(big.NewInt(0)) // memstart + stack.push(big.NewInt(0)) // value + gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) + fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) + */ + expected := common.BytesToAddress(common.FromHex(tt.expected)) + if !bytes.Equal(expected.Bytes(), address.Bytes()) { + t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String()) + } + + } +} diff --git a/core/vm/interface.go b/core/vm/interface.go index 1ef91cf1d6..fc15082f18 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -40,8 +40,10 @@ type StateDB interface { GetCodeSize(common.Address) int AddRefund(uint64) + SubRefund(uint64) GetRefund() uint64 + GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) @@ -64,7 +66,7 @@ type StateDB interface { ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) } -// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM +// CallContext provides a basic interface for the EVM calling conventions. The EVM // depends on this context being implemented for doing subcalls and initialising new EVM contracts. type CallContext interface { // Call another contract diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7090e0261f..4176653700 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -18,8 +18,10 @@ package vm import ( "fmt" + "hash" "sync/atomic" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" ) @@ -39,24 +41,60 @@ type Config struct { // may be left uninitialised and will be set to the default // table. JumpTable [256]operation + + // Type of the EWASM interpreter + EWASMInterpreter string + // Type of the EVM interpreter + EVMInterpreter string } // Interpreter is used to run Ethereum based contracts and will utilise the // passed environment to query external sources for state information. // The Interpreter will run the byte code VM based on the passed // configuration. -type Interpreter struct { +type Interpreter interface { + // Run loops and evaluates the contract's code with the given input data and returns + // the return byte-slice and an error if one occurred. + Run(contract *Contract, input []byte, static bool) ([]byte, error) + // CanRun tells if the contract, passed as an argument, can be + // run by the current interpreter. This is meant so that the + // caller can do something like: + // + // ```golang + // for _, interpreter := range interpreters { + // if interpreter.CanRun(contract.code) { + // interpreter.Run(contract.code, input) + // } + // } + // ``` + CanRun([]byte) bool +} + +// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports +// Read to get a variable amount of data from the hash state. Read is faster than Sum +// because it doesn't copy the internal state, but also modifies the internal state. +type keccakState interface { + hash.Hash + Read([]byte) (int, error) +} + +// EVMInterpreter represents an EVM interpreter +type EVMInterpreter struct { evm *EVM cfg Config gasTable params.GasTable - intPool *intPool + + intPool *intPool + + hasher keccakState // Keccak256 hasher instance shared across opcodes + hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes readOnly bool // Whether to throw on stateful modifications returnData []byte // Last CALL's return data for subsequent reuse } -// NewInterpreter returns a new instance of the Interpreter. -func NewInterpreter(evm *EVM, cfg Config) *Interpreter { +// NewEVMInterpreter returns a new instance of the Interpreter. +func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // We use the STOP instruction whether to see // the jump table was initialised. If it was not // we'll set the default jump table. @@ -73,41 +111,39 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter { } } - return &Interpreter{ + return &EVMInterpreter{ evm: evm, cfg: cfg, gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), - intPool: newIntPool(), } } -func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { - if in.evm.chainRules.IsByzantium { - if in.readOnly { - // If the interpreter is operating in readonly mode, make sure no - // state-modifying operation is performed. The 3rd stack item - // for a call operation is the value. Transferring value from one - // account to the others means the state is modified and should also - // return with an error. - if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) { - return errWriteProtection - } - } - } - return nil -} - // Run loops and evaluates the contract's code with the given input data and returns // 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 except for // errExecutionReverted which means revert-and-keep-gas-left. -func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { +func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { + if in.intPool == nil { + in.intPool = poolOfIntPools.get() + defer func() { + poolOfIntPools.put(in.intPool) + in.intPool = nil + }() + } + // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() + // Make sure the readOnly is only set if we aren't in readOnly yet. + // This makes also sure that the readOnly flag isn't removed for child calls. + if readOnly && !in.readOnly { + in.readOnly = true + defer func() { in.readOnly = false }() + } + // Reset the previous call's return data. It's unimportant to preserve the old buffer // as every returning call will return new data anyway. in.returnData = nil @@ -130,9 +166,13 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er 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 + res []byte // result of the opcode execution function ) contract.Input = input + // Reclaim the stack as an int pool when the execution stops + defer func() { in.intPool.put(stack.data...) }() + if in.cfg.Debug { defer func() { if err != nil { @@ -161,19 +201,35 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er if !operation.valid { return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) } - if err := operation.validateStack(stack); err != nil { - return nil, err + // Validate stack + if sLen := stack.len(); sLen < operation.minStack { + return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack) + } else if sLen > operation.maxStack { + return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack) } // If the operation is valid, enforce and write restrictions - if err := in.enforceRestrictions(op, operation, stack); err != nil { - return nil, err + if in.readOnly && in.evm.chainRules.IsByzantium { + // If the interpreter is operating in readonly mode, make sure no + // state-modifying operation is performed. The 3rd stack item + // for a call operation is the value. Transferring value from one + // account to the others means the state is modified and should also + // return with an error. + if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) { + return nil, errWriteProtection + } + } + // Static portion of gas + if !contract.UseGas(operation.constantGas) { + return nil, ErrOutOfGas } var memorySize uint64 // calculate the new memory size and expand the memory to fit // the operation + // Memory check needs to be done prior to evaluating the dynamic gas portion, + // to detect calculation overflows if operation.memorySize != nil { - memSize, overflow := bigUint64(operation.memorySize(stack)) + memSize, overflow := operation.memorySize(stack) if overflow { return nil, errGasUintOverflow } @@ -183,11 +239,14 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er return nil, errGasUintOverflow } } + // Dynamic portion of gas // consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method can get the proper cost - cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) - if err != nil || !contract.UseGas(cost) { - return nil, ErrOutOfGas + if operation.dynamicGas != nil { + cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize) + if err != nil || !contract.UseGas(cost) { + return nil, ErrOutOfGas + } } if memorySize > 0 { mem.Resize(memorySize) @@ -199,7 +258,7 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er } // execute the operation - res, err := operation.execute(&pc, in.evm, contract, mem, stack) + res, err = operation.execute(&pc, in, contract, mem, stack) // verifyPool is a build flag. Pool verification makes sure the integrity // of the integer pool by comparing values to a default value. if verifyPool { @@ -224,3 +283,9 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er } return nil, nil } + +// CanRun tells if the contract, passed as an argument, can be +// run by the current interpreter. +func (in *EVMInterpreter) CanRun(code []byte) bool { + return true +} diff --git a/core/vm/intpool.go b/core/vm/intpool.go index 5dbda18eee..917a78d560 100644 --- a/core/vm/intpool.go +++ b/core/vm/intpool.go @@ -16,7 +16,10 @@ package vm -import "math/big" +import ( + "math/big" + "sync" +) var checkVal = big.NewInt(-42) @@ -65,3 +68,39 @@ func (p *intPool) put(is ...*big.Int) { p.pool.push(i) } } + +// The intPool pool's default capacity +const poolDefaultCap = 25 + +// intPoolPool manages a pool of intPools. +type intPoolPool struct { + pools []*intPool + lock sync.Mutex +} + +var poolOfIntPools = &intPoolPool{ + pools: make([]*intPool, 0, poolDefaultCap), +} + +// get is looking for an available pool to return. +func (ipp *intPoolPool) get() *intPool { + ipp.lock.Lock() + defer ipp.lock.Unlock() + + if len(poolOfIntPools.pools) > 0 { + ip := ipp.pools[len(ipp.pools)-1] + ipp.pools = ipp.pools[:len(ipp.pools)-1] + return ip + } + return newIntPool() +} + +// put a pool that has been allocated with get. +func (ipp *intPoolPool) put(ip *intPool) { + ipp.lock.Lock() + defer ipp.lock.Unlock() + + if len(ipp.pools) < cap(ipp.pools) { + ipp.pools = append(ipp.pools, ip) + } +} diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go new file mode 100644 index 0000000000..6c0d00f3ce --- /dev/null +++ b/core/vm/intpool_test.go @@ -0,0 +1,55 @@ +// Copyright 2018 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 . + +package vm + +import ( + "testing" +) + +func TestIntPoolPoolGet(t *testing.T) { + poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) + + nip := poolOfIntPools.get() + if nip == nil { + t.Fatalf("Invalid pool allocation") + } +} + +func TestIntPoolPoolPut(t *testing.T) { + poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) + + nip := poolOfIntPools.get() + if len(poolOfIntPools.pools) != 0 { + t.Fatalf("Pool got added to list when none should have been") + } + + poolOfIntPools.put(nip) + if len(poolOfIntPools.pools) == 0 { + t.Fatalf("Pool did not get added to list when one should have been") + } +} + +func TestIntPoolPoolReUse(t *testing.T) { + poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) + nip := poolOfIntPools.get() + poolOfIntPools.put(nip) + poolOfIntPools.get() + + if len(poolOfIntPools.pools) != 0 { + t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0) + } +} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 3389941353..425436f9e5 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -18,27 +18,30 @@ package vm import ( "errors" - "math/big" "github.com/ethereum/go-ethereum/params" ) type ( - executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) - gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 - stackValidationFunc func(*Stack) error - memorySizeFunc func(*Stack) *big.Int + executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) + gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + // memorySizeFunc returns the required size, and whether the operation overflowed a uint64 + memorySizeFunc func(*Stack) (size uint64, overflow bool) ) var errGasUintOverflow = errors.New("gas uint64 overflow") type operation struct { - // op is the operation function - execute executionFunc - // gasCost is the gas function and returns the gas required for execution - gasCost gasFunc - // validateStack validates the stack (size) for the operation - validateStack stackValidationFunc + // execute is the operation function + execute executionFunc + constantGas uint64 + dynamicGas gasFunc + // minStack tells how many stack items are required + minStack int + // maxStack specifies the max length the stack can have for this operation + // to not overflow the stack. + maxStack int + // memorySize returns the memory size required for the operation memorySize memorySizeFunc @@ -51,901 +54,1055 @@ type operation struct { } var ( - frontierInstructionSet = NewFrontierInstructionSet() - homesteadInstructionSet = NewHomesteadInstructionSet() - byzantiumInstructionSet = NewByzantiumInstructionSet() - constantinopleInstructionSet = NewConstantinopleInstructionSet() + frontierInstructionSet = newFrontierInstructionSet() + homesteadInstructionSet = newHomesteadInstructionSet() + byzantiumInstructionSet = newByzantiumInstructionSet() + constantinopleInstructionSet = newConstantinopleInstructionSet() ) // NewConstantinopleInstructionSet returns the frontier, homestead // byzantium and contantinople instructions. -func NewConstantinopleInstructionSet() [256]operation { +func newConstantinopleInstructionSet() [256]operation { // instructions that can be executed during the byzantium phase. - instructionSet := NewByzantiumInstructionSet() + instructionSet := newByzantiumInstructionSet() instructionSet[SHL] = operation{ - execute: opSHL, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSHL, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, } instructionSet[SHR] = operation{ - execute: opSHR, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSHR, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, } instructionSet[SAR] = operation{ - execute: opSAR, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSAR, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, + } + instructionSet[EXTCODEHASH] = operation{ + execute: opExtCodeHash, + dynamicGas: gasExtCodeHash, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, + } + instructionSet[CREATE2] = operation{ + execute: opCreate2, + dynamicGas: gasCreate2, + minStack: minStack(4, 1), + maxStack: maxStack(4, 1), + memorySize: memoryCreate2, + valid: true, + writes: true, + returns: true, } return instructionSet } // NewByzantiumInstructionSet returns the frontier, homestead and // byzantium instructions. -func NewByzantiumInstructionSet() [256]operation { +func newByzantiumInstructionSet() [256]operation { // instructions that can be executed during the homestead phase. - instructionSet := NewHomesteadInstructionSet() + instructionSet := newHomesteadInstructionSet() instructionSet[STATICCALL] = operation{ - execute: opStaticCall, - gasCost: gasStaticCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryStaticCall, - valid: true, - returns: true, + execute: opStaticCall, + dynamicGas: gasStaticCall, + minStack: minStack(6, 1), + maxStack: maxStack(6, 1), + memorySize: memoryStaticCall, + valid: true, + returns: true, } instructionSet[RETURNDATASIZE] = operation{ - execute: opReturnDataSize, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opReturnDataSize, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, } instructionSet[RETURNDATACOPY] = operation{ - execute: opReturnDataCopy, - gasCost: gasReturnDataCopy, - validateStack: makeStackFunc(3, 0), - memorySize: memoryReturnDataCopy, - valid: true, + execute: opReturnDataCopy, + dynamicGas: gasReturnDataCopy, + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryReturnDataCopy, + valid: true, } instructionSet[REVERT] = operation{ - execute: opRevert, - gasCost: gasRevert, - validateStack: makeStackFunc(2, 0), - memorySize: memoryRevert, - valid: true, - reverts: true, - returns: true, + execute: opRevert, + dynamicGas: gasRevert, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryRevert, + valid: true, + reverts: true, + returns: true, } return instructionSet } // NewHomesteadInstructionSet returns the frontier and homestead // instructions that can be executed during the homestead phase. -func NewHomesteadInstructionSet() [256]operation { - instructionSet := NewFrontierInstructionSet() +func newHomesteadInstructionSet() [256]operation { + instructionSet := newFrontierInstructionSet() instructionSet[DELEGATECALL] = operation{ - execute: opDelegateCall, - gasCost: gasDelegateCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryDelegateCall, - valid: true, - returns: true, + execute: opDelegateCall, + dynamicGas: gasDelegateCall, + minStack: minStack(6, 1), + maxStack: maxStack(6, 1), + memorySize: memoryDelegateCall, + valid: true, + returns: true, } return instructionSet } // NewFrontierInstructionSet returns the frontier instructions // that can be executed during the frontier phase. -func NewFrontierInstructionSet() [256]operation { +func newFrontierInstructionSet() [256]operation { return [256]operation{ STOP: { - execute: opStop, - gasCost: constGasFunc(0), - validateStack: makeStackFunc(0, 0), - halts: true, - valid: true, + execute: opStop, + constantGas: 0, + minStack: minStack(0, 0), + maxStack: maxStack(0, 0), + halts: true, + valid: true, }, ADD: { - execute: opAdd, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opAdd, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, MUL: { - execute: opMul, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opMul, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SUB: { - execute: opSub, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSub, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, DIV: { - execute: opDiv, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opDiv, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SDIV: { - execute: opSdiv, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSdiv, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, MOD: { - execute: opMod, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opMod, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SMOD: { - execute: opSmod, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSmod, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, ADDMOD: { - execute: opAddmod, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(3, 1), - valid: true, + execute: opAddmod, + constantGas: GasMidStep, + minStack: minStack(3, 1), + maxStack: maxStack(3, 1), + valid: true, }, MULMOD: { - execute: opMulmod, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(3, 1), - valid: true, + execute: opMulmod, + constantGas: GasMidStep, + minStack: minStack(3, 1), + maxStack: maxStack(3, 1), + valid: true, }, EXP: { - execute: opExp, - gasCost: gasExp, - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opExp, + dynamicGas: gasExp, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SIGNEXTEND: { - execute: opSignExtend, - gasCost: constGasFunc(GasFastStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSignExtend, + constantGas: GasFastStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, LT: { - execute: opLt, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opLt, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, GT: { - execute: opGt, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opGt, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SLT: { - execute: opSlt, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSlt, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SGT: { - execute: opSgt, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opSgt, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, EQ: { - execute: opEq, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opEq, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, ISZERO: { - execute: opIszero, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opIszero, + constantGas: GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, AND: { - execute: opAnd, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opAnd, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, XOR: { - execute: opXor, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opXor, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, OR: { - execute: opOr, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opOr, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, NOT: { - execute: opNot, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opNot, + constantGas: GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, BYTE: { - execute: opByte, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(2, 1), - valid: true, + execute: opByte, + constantGas: GasFastestStep, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + valid: true, }, SHA3: { - execute: opSha3, - gasCost: gasSha3, - validateStack: makeStackFunc(2, 1), - memorySize: memorySha3, - valid: true, + execute: opSha3, + dynamicGas: gasSha3, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + memorySize: memorySha3, + valid: true, }, ADDRESS: { - execute: opAddress, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opAddress, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, BALANCE: { - execute: opBalance, - gasCost: gasBalance, - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opBalance, + dynamicGas: gasBalance, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, ORIGIN: { - execute: opOrigin, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opOrigin, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, CALLER: { - execute: opCaller, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opCaller, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, CALLVALUE: { - execute: opCallValue, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opCallValue, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, CALLDATALOAD: { - execute: opCallDataLoad, - gasCost: constGasFunc(GasFastestStep), - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opCallDataLoad, + constantGas: GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, CALLDATASIZE: { - execute: opCallDataSize, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opCallDataSize, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, CALLDATACOPY: { - execute: opCallDataCopy, - gasCost: gasCallDataCopy, - validateStack: makeStackFunc(3, 0), - memorySize: memoryCallDataCopy, - valid: true, + execute: opCallDataCopy, + dynamicGas: gasCallDataCopy, + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryCallDataCopy, + valid: true, }, CODESIZE: { - execute: opCodeSize, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opCodeSize, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, CODECOPY: { - execute: opCodeCopy, - gasCost: gasCodeCopy, - validateStack: makeStackFunc(3, 0), - memorySize: memoryCodeCopy, - valid: true, + execute: opCodeCopy, + dynamicGas: gasCodeCopy, + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryCodeCopy, + valid: true, }, GASPRICE: { - execute: opGasprice, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opGasprice, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, EXTCODESIZE: { - execute: opExtCodeSize, - gasCost: gasExtCodeSize, - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opExtCodeSize, + dynamicGas: gasExtCodeSize, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, EXTCODECOPY: { - execute: opExtCodeCopy, - gasCost: gasExtCodeCopy, - validateStack: makeStackFunc(4, 0), - memorySize: memoryExtCodeCopy, - valid: true, + execute: opExtCodeCopy, + dynamicGas: gasExtCodeCopy, + minStack: minStack(4, 0), + maxStack: maxStack(4, 0), + memorySize: memoryExtCodeCopy, + valid: true, }, BLOCKHASH: { - execute: opBlockhash, - gasCost: constGasFunc(GasExtStep), - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opBlockhash, + constantGas: GasExtStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, COINBASE: { - execute: opCoinbase, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opCoinbase, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, TIMESTAMP: { - execute: opTimestamp, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opTimestamp, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, NUMBER: { - execute: opNumber, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opNumber, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, DIFFICULTY: { - execute: opDifficulty, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opDifficulty, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, GASLIMIT: { - execute: opGasLimit, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opGasLimit, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, POP: { - execute: opPop, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(1, 0), - valid: true, + execute: opPop, + constantGas: GasQuickStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + valid: true, }, MLOAD: { - execute: opMload, - gasCost: gasMLoad, - validateStack: makeStackFunc(1, 1), - memorySize: memoryMLoad, - valid: true, + execute: opMload, + dynamicGas: gasMLoad, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + memorySize: memoryMLoad, + valid: true, }, MSTORE: { - execute: opMstore, - gasCost: gasMStore, - validateStack: makeStackFunc(2, 0), - memorySize: memoryMStore, - valid: true, + execute: opMstore, + dynamicGas: gasMStore, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryMStore, + valid: true, }, MSTORE8: { - execute: opMstore8, - gasCost: gasMStore8, - memorySize: memoryMStore8, - validateStack: makeStackFunc(2, 0), + execute: opMstore8, + dynamicGas: gasMStore8, + memorySize: memoryMStore8, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), valid: true, }, SLOAD: { - execute: opSload, - gasCost: gasSLoad, - validateStack: makeStackFunc(1, 1), - valid: true, + execute: opSload, + dynamicGas: gasSLoad, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + valid: true, }, SSTORE: { - execute: opSstore, - gasCost: gasSStore, - validateStack: makeStackFunc(2, 0), - valid: true, - writes: true, + execute: opSstore, + dynamicGas: gasSStore, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + valid: true, + writes: true, }, JUMP: { - execute: opJump, - gasCost: constGasFunc(GasMidStep), - validateStack: makeStackFunc(1, 0), - jumps: true, - valid: true, + execute: opJump, + constantGas: GasMidStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + jumps: true, + valid: true, }, JUMPI: { - execute: opJumpi, - gasCost: constGasFunc(GasSlowStep), - validateStack: makeStackFunc(2, 0), - jumps: true, - valid: true, + execute: opJumpi, + constantGas: GasSlowStep, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + jumps: true, + valid: true, }, PC: { - execute: opPc, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opPc, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, MSIZE: { - execute: opMsize, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opMsize, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, GAS: { - execute: opGas, - gasCost: constGasFunc(GasQuickStep), - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opGas, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, JUMPDEST: { - execute: opJumpdest, - gasCost: constGasFunc(params.JumpdestGas), - validateStack: makeStackFunc(0, 0), - valid: true, + execute: opJumpdest, + constantGas: params.JumpdestGas, + minStack: minStack(0, 0), + maxStack: maxStack(0, 0), + valid: true, }, PUSH1: { - execute: makePush(1, 1), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: opPush1, + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH2: { - execute: makePush(2, 2), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(2, 2), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH3: { - execute: makePush(3, 3), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(3, 3), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH4: { - execute: makePush(4, 4), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(4, 4), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH5: { - execute: makePush(5, 5), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(5, 5), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH6: { - execute: makePush(6, 6), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(6, 6), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH7: { - execute: makePush(7, 7), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(7, 7), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH8: { - execute: makePush(8, 8), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(8, 8), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH9: { - execute: makePush(9, 9), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(9, 9), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH10: { - execute: makePush(10, 10), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(10, 10), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH11: { - execute: makePush(11, 11), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(11, 11), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH12: { - execute: makePush(12, 12), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(12, 12), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH13: { - execute: makePush(13, 13), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(13, 13), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH14: { - execute: makePush(14, 14), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(14, 14), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH15: { - execute: makePush(15, 15), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(15, 15), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH16: { - execute: makePush(16, 16), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(16, 16), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH17: { - execute: makePush(17, 17), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(17, 17), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH18: { - execute: makePush(18, 18), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(18, 18), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH19: { - execute: makePush(19, 19), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(19, 19), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH20: { - execute: makePush(20, 20), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(20, 20), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH21: { - execute: makePush(21, 21), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(21, 21), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH22: { - execute: makePush(22, 22), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(22, 22), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH23: { - execute: makePush(23, 23), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(23, 23), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH24: { - execute: makePush(24, 24), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(24, 24), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH25: { - execute: makePush(25, 25), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(25, 25), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH26: { - execute: makePush(26, 26), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(26, 26), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH27: { - execute: makePush(27, 27), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(27, 27), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH28: { - execute: makePush(28, 28), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(28, 28), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH29: { - execute: makePush(29, 29), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(29, 29), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH30: { - execute: makePush(30, 30), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(30, 30), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH31: { - execute: makePush(31, 31), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(31, 31), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, PUSH32: { - execute: makePush(32, 32), - gasCost: gasPush, - validateStack: makeStackFunc(0, 1), - valid: true, + execute: makePush(32, 32), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, }, DUP1: { - execute: makeDup(1), - gasCost: gasDup, - validateStack: makeDupStackFunc(1), - valid: true, + execute: makeDup(1), + constantGas: GasFastestStep, + minStack: minDupStack(1), + maxStack: maxDupStack(1), + valid: true, }, DUP2: { - execute: makeDup(2), - gasCost: gasDup, - validateStack: makeDupStackFunc(2), - valid: true, + execute: makeDup(2), + constantGas: GasFastestStep, + minStack: minDupStack(2), + maxStack: maxDupStack(2), + valid: true, }, DUP3: { - execute: makeDup(3), - gasCost: gasDup, - validateStack: makeDupStackFunc(3), - valid: true, + execute: makeDup(3), + constantGas: GasFastestStep, + minStack: minDupStack(3), + maxStack: maxDupStack(3), + valid: true, }, DUP4: { - execute: makeDup(4), - gasCost: gasDup, - validateStack: makeDupStackFunc(4), - valid: true, + execute: makeDup(4), + constantGas: GasFastestStep, + minStack: minDupStack(4), + maxStack: maxDupStack(4), + valid: true, }, DUP5: { - execute: makeDup(5), - gasCost: gasDup, - validateStack: makeDupStackFunc(5), - valid: true, + execute: makeDup(5), + constantGas: GasFastestStep, + minStack: minDupStack(5), + maxStack: maxDupStack(5), + valid: true, }, DUP6: { - execute: makeDup(6), - gasCost: gasDup, - validateStack: makeDupStackFunc(6), - valid: true, + execute: makeDup(6), + constantGas: GasFastestStep, + minStack: minDupStack(6), + maxStack: maxDupStack(6), + valid: true, }, DUP7: { - execute: makeDup(7), - gasCost: gasDup, - validateStack: makeDupStackFunc(7), - valid: true, + execute: makeDup(7), + constantGas: GasFastestStep, + minStack: minDupStack(7), + maxStack: maxDupStack(7), + valid: true, }, DUP8: { - execute: makeDup(8), - gasCost: gasDup, - validateStack: makeDupStackFunc(8), - valid: true, + execute: makeDup(8), + constantGas: GasFastestStep, + minStack: minDupStack(8), + maxStack: maxDupStack(8), + valid: true, }, DUP9: { - execute: makeDup(9), - gasCost: gasDup, - validateStack: makeDupStackFunc(9), - valid: true, + execute: makeDup(9), + constantGas: GasFastestStep, + minStack: minDupStack(9), + maxStack: maxDupStack(9), + valid: true, }, DUP10: { - execute: makeDup(10), - gasCost: gasDup, - validateStack: makeDupStackFunc(10), - valid: true, + execute: makeDup(10), + constantGas: GasFastestStep, + minStack: minDupStack(10), + maxStack: maxDupStack(10), + valid: true, }, DUP11: { - execute: makeDup(11), - gasCost: gasDup, - validateStack: makeDupStackFunc(11), - valid: true, + execute: makeDup(11), + constantGas: GasFastestStep, + minStack: minDupStack(11), + maxStack: maxDupStack(11), + valid: true, }, DUP12: { - execute: makeDup(12), - gasCost: gasDup, - validateStack: makeDupStackFunc(12), - valid: true, + execute: makeDup(12), + constantGas: GasFastestStep, + minStack: minDupStack(12), + maxStack: maxDupStack(12), + valid: true, }, DUP13: { - execute: makeDup(13), - gasCost: gasDup, - validateStack: makeDupStackFunc(13), - valid: true, + execute: makeDup(13), + constantGas: GasFastestStep, + minStack: minDupStack(13), + maxStack: maxDupStack(13), + valid: true, }, DUP14: { - execute: makeDup(14), - gasCost: gasDup, - validateStack: makeDupStackFunc(14), - valid: true, + execute: makeDup(14), + constantGas: GasFastestStep, + minStack: minDupStack(14), + maxStack: maxDupStack(14), + valid: true, }, DUP15: { - execute: makeDup(15), - gasCost: gasDup, - validateStack: makeDupStackFunc(15), - valid: true, + execute: makeDup(15), + constantGas: GasFastestStep, + minStack: minDupStack(15), + maxStack: maxDupStack(15), + valid: true, }, DUP16: { - execute: makeDup(16), - gasCost: gasDup, - validateStack: makeDupStackFunc(16), - valid: true, + execute: makeDup(16), + constantGas: GasFastestStep, + minStack: minDupStack(16), + maxStack: maxDupStack(16), + valid: true, }, SWAP1: { - execute: makeSwap(1), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(2), - valid: true, + execute: makeSwap(1), + constantGas: GasFastestStep, + minStack: minSwapStack(2), + maxStack: maxSwapStack(2), + valid: true, }, SWAP2: { - execute: makeSwap(2), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(3), - valid: true, + execute: makeSwap(2), + constantGas: GasFastestStep, + minStack: minSwapStack(3), + maxStack: maxSwapStack(3), + valid: true, }, SWAP3: { - execute: makeSwap(3), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(4), - valid: true, + execute: makeSwap(3), + constantGas: GasFastestStep, + minStack: minSwapStack(4), + maxStack: maxSwapStack(4), + valid: true, }, SWAP4: { - execute: makeSwap(4), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(5), - valid: true, + execute: makeSwap(4), + constantGas: GasFastestStep, + minStack: minSwapStack(5), + maxStack: maxSwapStack(5), + valid: true, }, SWAP5: { - execute: makeSwap(5), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(6), - valid: true, + execute: makeSwap(5), + constantGas: GasFastestStep, + minStack: minSwapStack(6), + maxStack: maxSwapStack(6), + valid: true, }, SWAP6: { - execute: makeSwap(6), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(7), - valid: true, + execute: makeSwap(6), + constantGas: GasFastestStep, + minStack: minSwapStack(7), + maxStack: maxSwapStack(7), + valid: true, }, SWAP7: { - execute: makeSwap(7), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(8), - valid: true, + execute: makeSwap(7), + constantGas: GasFastestStep, + minStack: minSwapStack(8), + maxStack: maxSwapStack(8), + valid: true, }, SWAP8: { - execute: makeSwap(8), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(9), - valid: true, + execute: makeSwap(8), + constantGas: GasFastestStep, + minStack: minSwapStack(9), + maxStack: maxSwapStack(9), + valid: true, }, SWAP9: { - execute: makeSwap(9), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(10), - valid: true, + execute: makeSwap(9), + constantGas: GasFastestStep, + minStack: minSwapStack(10), + maxStack: maxSwapStack(10), + valid: true, }, SWAP10: { - execute: makeSwap(10), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(11), - valid: true, + execute: makeSwap(10), + constantGas: GasFastestStep, + minStack: minSwapStack(11), + maxStack: maxSwapStack(11), + valid: true, }, SWAP11: { - execute: makeSwap(11), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(12), - valid: true, + execute: makeSwap(11), + constantGas: GasFastestStep, + minStack: minSwapStack(12), + maxStack: maxSwapStack(12), + valid: true, }, SWAP12: { - execute: makeSwap(12), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(13), - valid: true, + execute: makeSwap(12), + constantGas: GasFastestStep, + minStack: minSwapStack(13), + maxStack: maxSwapStack(13), + valid: true, }, SWAP13: { - execute: makeSwap(13), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(14), - valid: true, + execute: makeSwap(13), + constantGas: GasFastestStep, + minStack: minSwapStack(14), + maxStack: maxSwapStack(14), + valid: true, }, SWAP14: { - execute: makeSwap(14), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(15), - valid: true, + execute: makeSwap(14), + constantGas: GasFastestStep, + minStack: minSwapStack(15), + maxStack: maxSwapStack(15), + valid: true, }, SWAP15: { - execute: makeSwap(15), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(16), - valid: true, + execute: makeSwap(15), + constantGas: GasFastestStep, + minStack: minSwapStack(16), + maxStack: maxSwapStack(16), + valid: true, }, SWAP16: { - execute: makeSwap(16), - gasCost: gasSwap, - validateStack: makeSwapStackFunc(17), - valid: true, + execute: makeSwap(16), + constantGas: GasFastestStep, + minStack: minSwapStack(17), + maxStack: maxSwapStack(17), + valid: true, }, LOG0: { - execute: makeLog(0), - gasCost: makeGasLog(0), - validateStack: makeStackFunc(2, 0), - memorySize: memoryLog, - valid: true, - writes: true, + execute: makeLog(0), + dynamicGas: makeGasLog(0), + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryLog, + valid: true, + writes: true, }, LOG1: { - execute: makeLog(1), - gasCost: makeGasLog(1), - validateStack: makeStackFunc(3, 0), - memorySize: memoryLog, - valid: true, - writes: true, + execute: makeLog(1), + dynamicGas: makeGasLog(1), + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryLog, + valid: true, + writes: true, }, LOG2: { - execute: makeLog(2), - gasCost: makeGasLog(2), - validateStack: makeStackFunc(4, 0), - memorySize: memoryLog, - valid: true, - writes: true, + execute: makeLog(2), + dynamicGas: makeGasLog(2), + minStack: minStack(4, 0), + maxStack: maxStack(4, 0), + memorySize: memoryLog, + valid: true, + writes: true, }, LOG3: { - execute: makeLog(3), - gasCost: makeGasLog(3), - validateStack: makeStackFunc(5, 0), - memorySize: memoryLog, - valid: true, - writes: true, + execute: makeLog(3), + dynamicGas: makeGasLog(3), + minStack: minStack(5, 0), + maxStack: maxStack(5, 0), + memorySize: memoryLog, + valid: true, + writes: true, }, LOG4: { - execute: makeLog(4), - gasCost: makeGasLog(4), - validateStack: makeStackFunc(6, 0), - memorySize: memoryLog, - valid: true, - writes: true, + execute: makeLog(4), + dynamicGas: makeGasLog(4), + minStack: minStack(6, 0), + maxStack: maxStack(6, 0), + memorySize: memoryLog, + valid: true, + writes: true, }, CREATE: { - execute: opCreate, - gasCost: gasCreate, - validateStack: makeStackFunc(3, 1), - memorySize: memoryCreate, - valid: true, - writes: true, - returns: true, + execute: opCreate, + dynamicGas: gasCreate, + minStack: minStack(3, 1), + maxStack: maxStack(3, 1), + memorySize: memoryCreate, + valid: true, + writes: true, + returns: true, }, CALL: { - execute: opCall, - gasCost: gasCall, - validateStack: makeStackFunc(7, 1), - memorySize: memoryCall, - valid: true, - returns: true, + execute: opCall, + dynamicGas: gasCall, + minStack: minStack(7, 1), + maxStack: maxStack(7, 1), + memorySize: memoryCall, + valid: true, + returns: true, }, CALLCODE: { - execute: opCallCode, - gasCost: gasCallCode, - validateStack: makeStackFunc(7, 1), - memorySize: memoryCall, - valid: true, - returns: true, + execute: opCallCode, + dynamicGas: gasCallCode, + minStack: minStack(7, 1), + maxStack: maxStack(7, 1), + memorySize: memoryCall, + valid: true, + returns: true, }, RETURN: { - execute: opReturn, - gasCost: gasReturn, - validateStack: makeStackFunc(2, 0), - memorySize: memoryReturn, - halts: true, - valid: true, + execute: opReturn, + dynamicGas: gasReturn, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + memorySize: memoryReturn, + halts: true, + valid: true, }, SELFDESTRUCT: { - execute: opSuicide, - gasCost: gasSuicide, - validateStack: makeStackFunc(1, 0), - halts: true, - valid: true, - writes: true, + execute: opSuicide, + dynamicGas: gasSuicide, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + halts: true, + valid: true, + writes: true, }, } } diff --git a/core/vm/logger.go b/core/vm/logger.go index 4c820d8b53..1733bf2700 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -29,11 +29,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) +// Storage represents a contract's storage. type Storage map[common.Hash]common.Hash -func (self Storage) Copy() Storage { +// Copy duplicates the current storage. +func (s Storage) Copy() Storage { cpy := make(Storage) - for key, value := range self { + for key, value := range s { cpy[key] = value } @@ -45,6 +47,7 @@ type LogConfig struct { DisableMemory bool // disable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture + Debug bool // print output during capture end Limit int // maximum length of output, but zero means unlimited } @@ -53,16 +56,17 @@ type LogConfig struct { // StructLog is emitted to the EVM each cycle and lists information about the current internal state // prior to the execution of the statement. type StructLog struct { - Pc uint64 `json:"pc"` - Op OpCode `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` - MemorySize int `json:"memSize"` - Stack []*big.Int `json:"stack"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth int `json:"depth"` - Err error `json:"-"` + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*big.Int `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` } // overrides for gencodec @@ -75,10 +79,12 @@ type structLogMarshaling struct { ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON } +// OpName formats the operand name in a human-readable format. func (s *StructLog) OpName() string { return s.Op.String() } +// ErrorString formats the log's error as a string. func (s *StructLog) ErrorString() string { if s.Err != nil { return s.Err.Error() @@ -123,6 +129,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { return logger } +// CaptureStart implements the Tracer interface to initialize the tracing operation. func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { return nil } @@ -171,19 +178,28 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui 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} + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err} l.logs = append(l.logs, log) return nil } +// CaptureFault implements the Tracer interface to trace an execution fault +// while running an opcode. func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { return nil } +// CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { l.output = output l.err = err + if l.cfg.Debug { + fmt.Printf("0x%x\n", output) + if err != nil { + fmt.Printf(" error: %v\n", err) + } + } return nil } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go new file mode 100644 index 0000000000..ff379a4efd --- /dev/null +++ b/core/vm/logger_json.go @@ -0,0 +1,87 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package vm + +import ( + "encoding/json" + "io" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +type JSONLogger struct { + encoder *json.Encoder + cfg *LogConfig +} + +// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects +// into the provided stream. +func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { + l := &JSONLogger{json.NewEncoder(writer), cfg} + if l.cfg == nil { + l.cfg = &LogConfig{} + } + return l +} + +func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { + return nil +} + +// CaptureState outputs state information on the logger. +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + log := StructLog{ + Pc: pc, + Op: op, + Gas: gas, + GasCost: cost, + MemorySize: memory.Len(), + Storage: nil, + Depth: depth, + RefundCounter: env.StateDB.GetRefund(), + Err: err, + } + if !l.cfg.DisableMemory { + log.Memory = memory.Data() + } + if !l.cfg.DisableStack { + log.Stack = stack.Data() + } + return l.encoder.Encode(log) +} + +// CaptureFault outputs state information on the logger. +func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + return nil +} + +// CaptureEnd is triggered at end of execution. +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { + type endLog struct { + Output string `json:"output"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time time.Duration `json:"time"` + Err string `json:"error,omitempty"` + } + if err != nil { + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + } + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) +} diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 28830c445c..2ea7535a79 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" ) @@ -41,14 +42,15 @@ func (d *dummyContractRef) SetBalance(*big.Int) {} func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } -type dummyStateDB struct { - NoopStateDB - ref *dummyContractRef +type dummyStatedb struct { + state.StateDB } +func (*dummyStatedb) GetRefund() uint64 { return 1337 } + func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) mem = NewMemory() stack = newstack() @@ -56,9 +58,7 @@ func TestStoreCapture(t *testing.T) { ) stack.push(big.NewInt(1)) stack.push(big.NewInt(0)) - var index common.Hash - logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) 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()])) diff --git a/core/vm/memory.go b/core/vm/memory.go index 99a84d2271..7e6f0eb940 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -16,7 +16,12 @@ package vm -import "fmt" +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common/math" +) // Memory implements a simple memory model for the ethereum virtual machine. type Memory struct { @@ -24,25 +29,39 @@ type Memory struct { lastGasCost uint64 } +// NewMemory returns a new memory model. func NewMemory() *Memory { return &Memory{} } // Set sets offset + size to value func (m *Memory) Set(offset, size uint64, value []byte) { - // length of store may never be less than offset + size. - // The store should be resized PRIOR to setting the memory - if size > uint64(len(m.store)) { - panic("INVALID memory: store empty") - } - // It's possible the offset is greater than 0 and size equals 0. This is because // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP) if size > 0 { + // length of store may never be less than offset + size. + // The store should be resized PRIOR to setting the memory + if offset+size > uint64(len(m.store)) { + panic("invalid memory: store empty") + } copy(m.store[offset:offset+size], value) } } +// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to +// 32 bytes. +func (m *Memory) Set32(offset uint64, val *big.Int) { + // length of store may never be less than offset + size. + // The store should be resized PRIOR to setting the memory + if offset+32 > uint64(len(m.store)) { + panic("invalid memory: store empty") + } + // Zero the memory area + copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + // Fill in relevant bits + math.ReadBits(val, m.store[offset:offset+32]) +} + // Resize resizes the memory to size func (m *Memory) Resize(size uint64) { if uint64(m.Len()) < size { @@ -51,14 +70,14 @@ func (m *Memory) Resize(size uint64) { } // Get returns offset + size as a new slice -func (self *Memory) Get(offset, size int64) (cpy []byte) { +func (m *Memory) Get(offset, size int64) (cpy []byte) { if size == 0 { return nil } - if len(self.store) > int(offset) { + if len(m.store) > int(offset) { cpy = make([]byte, size) - copy(cpy, self.store[offset:offset+size]) + copy(cpy, m.store[offset:offset+size]) return } @@ -67,13 +86,13 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) { } // GetPtr returns the offset + size -func (self *Memory) GetPtr(offset, size int64) []byte { +func (m *Memory) GetPtr(offset, size int64) []byte { if size == 0 { return nil } - if len(self.store) > int(offset) { - return self.store[offset : offset+size] + if len(m.store) > int(offset) { + return m.store[offset : offset+size] } return nil @@ -89,6 +108,7 @@ func (m *Memory) Data() []byte { return m.store } +// Print dumps the content of the memory. func (m *Memory) Print() { fmt.Printf("### mem %d bytes ###\n", len(m.store)) if len(m.store) > 0 { diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index bec0235bcc..4fcb41442c 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -16,84 +16,98 @@ package vm -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common/math" -) - -func memorySha3(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(1)) +func memorySha3(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(1)) } -func memoryCallDataCopy(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(2)) +func memoryCallDataCopy(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(2)) } -func memoryReturnDataCopy(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(2)) +func memoryReturnDataCopy(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(2)) } -func memoryCodeCopy(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(2)) +func memoryCodeCopy(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(2)) } -func memoryExtCodeCopy(stack *Stack) *big.Int { - return calcMemSize(stack.Back(1), stack.Back(3)) +func memoryExtCodeCopy(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(1), stack.Back(3)) } -func memoryMLoad(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), big.NewInt(32)) +func memoryMLoad(stack *Stack) (uint64, bool) { + return calcMemSize64WithUint(stack.Back(0), 32) } -func memoryMStore8(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), big.NewInt(1)) +func memoryMStore8(stack *Stack) (uint64, bool) { + return calcMemSize64WithUint(stack.Back(0), 1) } -func memoryMStore(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), big.NewInt(32)) +func memoryMStore(stack *Stack) (uint64, bool) { + return calcMemSize64WithUint(stack.Back(0), 32) } -func memoryCreate(stack *Stack) *big.Int { - return calcMemSize(stack.Back(1), stack.Back(2)) +func memoryCreate(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(1), stack.Back(2)) } -func memoryCall(stack *Stack) *big.Int { - x := calcMemSize(stack.Back(5), stack.Back(6)) - y := calcMemSize(stack.Back(3), stack.Back(4)) - - return math.BigMax(x, y) +func memoryCreate2(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(1), stack.Back(2)) } -func memoryCallCode(stack *Stack) *big.Int { - x := calcMemSize(stack.Back(5), stack.Back(6)) - y := calcMemSize(stack.Back(3), stack.Back(4)) - - return math.BigMax(x, y) +func memoryCall(stack *Stack) (uint64, bool) { + x, overflow := calcMemSize64(stack.Back(5), stack.Back(6)) + if overflow { + return 0, true + } + y, overflow := calcMemSize64(stack.Back(3), stack.Back(4)) + if overflow { + return 0, true + } + if x > y { + return x, false + } + return y, false } -func memoryDelegateCall(stack *Stack) *big.Int { - x := calcMemSize(stack.Back(4), stack.Back(5)) - y := calcMemSize(stack.Back(2), stack.Back(3)) - - return math.BigMax(x, y) +func memoryDelegateCall(stack *Stack) (uint64, bool) { + x, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) + if overflow { + return 0, true + } + y, overflow := calcMemSize64(stack.Back(2), stack.Back(3)) + if overflow { + return 0, true + } + if x > y { + return x, false + } + return y, false } -func memoryStaticCall(stack *Stack) *big.Int { - x := calcMemSize(stack.Back(4), stack.Back(5)) - y := calcMemSize(stack.Back(2), stack.Back(3)) - - return math.BigMax(x, y) +func memoryStaticCall(stack *Stack) (uint64, bool) { + x, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) + if overflow { + return 0, true + } + y, overflow := calcMemSize64(stack.Back(2), stack.Back(3)) + if overflow { + return 0, true + } + if x > y { + return x, false + } + return y, false } -func memoryReturn(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(1)) +func memoryReturn(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(1)) } -func memoryRevert(stack *Stack) *big.Int { - return calcMemSize(stack.Back(0), stack.Back(1)) +func memoryRevert(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(1)) } -func memoryLog(stack *Stack) *big.Int { - mSize, mStart := stack.Back(1), stack.Back(0) - return calcMemSize(mStart, mSize) +func memoryLog(stack *Stack) (uint64, bool) { + return calcMemSize64(stack.Back(0), stack.Back(1)) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 7fe55b72f6..4349ffd295 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -23,6 +23,7 @@ import ( // OpCode is an EVM opcode type OpCode byte +// IsPush specifies if an opcode is a PUSH opcode. func (op OpCode) IsPush() bool { switch op { case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: @@ -31,12 +32,13 @@ func (op OpCode) IsPush() bool { return false } +// IsStaticJump specifies if an opcode is JUMP. func (op OpCode) IsStaticJump() bool { return op == JUMP } +// 0x0 range - arithmetic ops. const ( - // 0x0 range - arithmetic ops STOP OpCode = iota ADD MUL @@ -51,6 +53,7 @@ const ( SIGNEXTEND ) +// 0x10 range - comparison ops. const ( LT OpCode = iota + 0x10 GT @@ -70,8 +73,8 @@ const ( SHA3 = 0x20 ) +// 0x30 range - closure state. const ( - // 0x30 range - closure state ADDRESS OpCode = 0x30 + iota BALANCE ORIGIN @@ -87,10 +90,11 @@ const ( EXTCODECOPY RETURNDATASIZE RETURNDATACOPY + EXTCODEHASH ) +// 0x40 range - block operations. const ( - // 0x40 range - block operations BLOCKHASH OpCode = 0x40 + iota COINBASE TIMESTAMP @@ -99,8 +103,8 @@ const ( GASLIMIT ) +// 0x50 range - 'storage' and execution. const ( - // 0x50 range - 'storage' and execution POP OpCode = 0x50 + iota MLOAD MSTORE @@ -115,8 +119,8 @@ const ( JUMPDEST ) +// 0x60 range. const ( - // 0x60 range PUSH1 OpCode = 0x60 + iota PUSH2 PUSH3 @@ -183,6 +187,7 @@ const ( SWAP16 ) +// 0xa0 range - logging ops. const ( LOG0 OpCode = 0xa0 + iota LOG1 @@ -191,29 +196,30 @@ const ( LOG4 ) -// unofficial opcodes used for parsing +// unofficial opcodes used for parsing. const ( PUSH OpCode = 0xb0 + iota DUP SWAP ) +// 0xf0 range - closures. const ( - // 0xf0 range - closures CREATE OpCode = 0xf0 + iota CALL CALLCODE RETURN DELEGATECALL + CREATE2 STATICCALL = 0xfa REVERT = 0xfd SELFDESTRUCT = 0xff ) -// Since the opcodes aren't all in order we can't use a regular slice +// Since the opcodes aren't all in order we can't use a regular slice. var opCodeToString = map[OpCode]string{ - // 0x0 range - arithmetic ops + // 0x0 range - arithmetic ops. STOP: "STOP", ADD: "ADD", MUL: "MUL", @@ -232,7 +238,7 @@ var opCodeToString = map[OpCode]string{ ISZERO: "ISZERO", SIGNEXTEND: "SIGNEXTEND", - // 0x10 range - bit ops + // 0x10 range - bit ops. AND: "AND", OR: "OR", XOR: "XOR", @@ -243,10 +249,10 @@ var opCodeToString = map[OpCode]string{ ADDMOD: "ADDMOD", MULMOD: "MULMOD", - // 0x20 range - crypto + // 0x20 range - crypto. SHA3: "SHA3", - // 0x30 range - closure state + // 0x30 range - closure state. ADDRESS: "ADDRESS", BALANCE: "BALANCE", ORIGIN: "ORIGIN", @@ -262,8 +268,9 @@ var opCodeToString = map[OpCode]string{ EXTCODECOPY: "EXTCODECOPY", RETURNDATASIZE: "RETURNDATASIZE", RETURNDATACOPY: "RETURNDATACOPY", + EXTCODEHASH: "EXTCODEHASH", - // 0x40 range - block operations + // 0x40 range - block operations. BLOCKHASH: "BLOCKHASH", COINBASE: "COINBASE", TIMESTAMP: "TIMESTAMP", @@ -271,7 +278,7 @@ var opCodeToString = map[OpCode]string{ DIFFICULTY: "DIFFICULTY", GASLIMIT: "GASLIMIT", - // 0x50 range - 'storage' and execution + // 0x50 range - 'storage' and execution. POP: "POP", //DUP: "DUP", //SWAP: "SWAP", @@ -287,7 +294,7 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", - // 0x60 range - push + // 0x60 range - push. PUSH1: "PUSH1", PUSH2: "PUSH2", PUSH3: "PUSH3", @@ -360,12 +367,13 @@ var opCodeToString = map[OpCode]string{ LOG3: "LOG3", LOG4: "LOG4", - // 0xf0 range + // 0xf0 range. CREATE: "CREATE", CALL: "CALL", RETURN: "RETURN", CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", + CREATE2: "CREATE2", STATICCALL: "STATICCALL", REVERT: "REVERT", SELFDESTRUCT: "SELFDESTRUCT", @@ -375,10 +383,10 @@ var opCodeToString = map[OpCode]string{ SWAP: "SWAP", } -func (o OpCode) String() string { - str := opCodeToString[o] +func (op OpCode) String() string { + str := opCodeToString[op] if len(str) == 0 { - return fmt.Sprintf("Missing opcode 0x%x", int(o)) + return fmt.Sprintf("Missing opcode 0x%x", int(op)) } return str @@ -429,6 +437,7 @@ var stringToOp = map[string]OpCode{ "EXTCODECOPY": EXTCODECOPY, "RETURNDATASIZE": RETURNDATASIZE, "RETURNDATACOPY": RETURNDATACOPY, + "EXTCODEHASH": EXTCODEHASH, "BLOCKHASH": BLOCKHASH, "COINBASE": COINBASE, "TIMESTAMP": TIMESTAMP, @@ -517,6 +526,7 @@ var stringToOp = map[string]OpCode{ "LOG3": LOG3, "LOG4": LOG4, "CREATE": CREATE, + "CREATE2": CREATE2, "CALL": CALL, "RETURN": RETURN, "CALLCODE": CALLCODE, @@ -524,6 +534,7 @@ var stringToOp = map[string]OpCode{ "SELFDESTRUCT": SELFDESTRUCT, } +// StringToOp finds the opcode whose name is stored in `str`. func StringToOp(str string) OpCode { return stringToOp[str] } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 1e9ed7ae2d..db1f6f3822 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -22,10 +22,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) @@ -52,7 +52,7 @@ type Config struct { func setDefaults(cfg *Config) { if cfg.ChainConfig == nil { cfg.ChainConfig = ¶ms.ChainConfig{ - ChainId: big.NewInt(1), + ChainID: big.NewInt(1), HomesteadBlock: new(big.Int), DAOForkBlock: new(big.Int), DAOForkSupport: false, @@ -99,11 +99,10 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { setDefaults(cfg) if cfg.State == nil { - db, _ := ethdb.NewMemDatabase() - cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db)) + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) } var ( - address = common.StringToAddress("contract") + address = common.BytesToAddress([]byte("contract")) vmenv = NewEnv(cfg) sender = vm.AccountRef(cfg.Origin) ) @@ -113,7 +112,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // Call the code with the given configuration. ret, _, err := vmenv.Call( sender, - common.StringToAddress("contract"), + common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, cfg.Value, @@ -130,8 +129,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { setDefaults(cfg) if cfg.State == nil { - db, _ := ethdb.NewMemDatabase() - cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(db)) + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) } var ( vmenv = NewEnv(cfg) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 2c4dc50265..15f545ddca 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -23,9 +23,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" ) func TestDefaults(t *testing.T) { @@ -94,8 +95,7 @@ func TestExecute(t *testing.T) { } func TestCall(t *testing.T) { - db, _ := ethdb.NewMemDatabase() - state, _ := state.New(common.Hash{}, state.NewDatabase(db)) + state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) address := common.HexToAddress("0x0a") state.SetCode(address, []byte{ byte(vm.PUSH1), 10, @@ -149,3 +149,57 @@ func BenchmarkCall(b *testing.B) { } } } +func benchmarkEVM_Create(bench *testing.B, code string) { + var ( + statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase())) + sender = common.BytesToAddress([]byte("sender")) + receiver = common.BytesToAddress([]byte("receiver")) + ) + + statedb.CreateAccount(sender) + statedb.SetCode(receiver, common.FromHex(code)) + runtimeConfig := Config{ + Origin: sender, + State: statedb, + GasLimit: 10000000, + Difficulty: big.NewInt(0x200000), + Time: new(big.Int).SetUint64(0), + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(1), + ChainConfig: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + }, + EVMConfig: vm.Config{}, + } + // Warm up the intpools and stuff + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + Call(receiver, []byte{}, &runtimeConfig) + } + bench.StopTimer() +} + +func BenchmarkEVM_CREATE_500(bench *testing.B) { + // initcode size 500K, repeatedly calls CREATE and then modifies the mem contents + benchmarkEVM_Create(bench, "5b6207a120600080f0600152600056") +} +func BenchmarkEVM_CREATE2_500(bench *testing.B) { + // initcode size 500K, repeatedly calls CREATE2 and then modifies the mem contents + benchmarkEVM_Create(bench, "5b586207a120600080f5600152600056") +} +func BenchmarkEVM_CREATE_1200(bench *testing.B) { + // initcode size 1200K, repeatedly calls CREATE and then modifies the mem contents + benchmarkEVM_Create(bench, "5b62124f80600080f0600152600056") +} +func BenchmarkEVM_CREATE2_1200(bench *testing.B) { + // initcode size 1200K, repeatedly calls CREATE2 and then modifies the mem contents + benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056") +} diff --git a/core/vm/stack.go b/core/vm/stack.go index 9c10d50ad1..4c1b9e8037 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -21,7 +21,7 @@ import ( "math/big" ) -// stack is an object for basic stack operations. Items popped to the stack are +// Stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly // initialised objects. type Stack struct { @@ -32,6 +32,7 @@ func newstack() *Stack { return &Stack{data: make([]*big.Int, 0, 1024)} } +// Data returns the underlying big.Int array. func (st *Stack) Data() []*big.Int { return st.data } @@ -80,6 +81,7 @@ func (st *Stack) require(n int) error { return nil } +// Print dumps the content of the stack func (st *Stack) Print() { fmt.Println("### stack ###") if len(st.data) > 0 { diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go index a4b1cfcd89..10c12901af 100644 --- a/core/vm/stack_table.go +++ b/core/vm/stack_table.go @@ -17,28 +17,26 @@ package vm import ( - "fmt" - "github.com/ethereum/go-ethereum/params" ) -func makeStackFunc(pop, push int) stackValidationFunc { - return func(stack *Stack) error { - if err := stack.require(pop); err != nil { - return err - } - - if stack.len()+push-pop > int(params.StackLimit) { - return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit) - } - return nil - } +func minSwapStack(n int) int { + return minStack(n, n) +} +func maxSwapStack(n int) int { + return maxStack(n, n) } -func makeDupStackFunc(n int) stackValidationFunc { - return makeStackFunc(n, n+1) +func minDupStack(n int) int { + return minStack(n, n+1) +} +func maxDupStack(n int) int { + return maxStack(n, n+1) } -func makeSwapStackFunc(n int) stackValidationFunc { - return makeStackFunc(n, n) +func maxStack(pop, push int) int { + return int(params.StackLimit) + pop - push +} +func minStack(pops, push int) int { + return pops }