diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 2e57d725891b5d17380a3ff3de956bc0f8c04819..d110af2c30aeccb30c09e2efe37d0277ea266aaa 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -38,6 +38,10 @@ var ( Name: "trace.nostack", Usage: "Disable stack output in traces", } + TraceDisableReturnDataFlag = cli.BoolFlag{ + Name: "trace.noreturndata", + Usage: "Disable return data output in traces", + } OutputAllocFlag = cli.StringFlag{ Name: "output.alloc", Usage: "Determines where to put the `alloc` of the post-state.\n" + diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index a4908d763ea0efdf90ad244c5c6496311f49364a..079307b97502763194320239d278bf8e974a2b6a 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -83,9 +83,10 @@ func Main(ctx *cli.Context) error { if ctx.Bool(TraceFlag.Name) { // Configure the EVM logger logConfig := &vm.LogConfig{ - DisableStack: ctx.Bool(TraceDisableStackFlag.Name), - DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name), - Debug: true, + DisableStack: ctx.Bool(TraceDisableStackFlag.Name), + DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name), + DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name), + Debug: true, } var prevFile *os.File // This one closes the last file diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 1394dc9ae724726616f1d46a06c209c0a4cb2e20..1bcd4db489efb4f8ce170ce8185d07004630e7c6 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -119,6 +119,14 @@ var ( Name: "nostack", Usage: "disable stack output", } + DisableStorageFlag = cli.BoolFlag{ + Name: "nostorage", + Usage: "disable storage output", + } + DisableReturnDataFlag = cli.BoolFlag{ + Name: "noreturndata", + Usage: "disable return data output", + } EVMInterpreterFlag = cli.StringFlag{ Name: "vm.evm", Usage: "External EVM configuration (default = built-in interpreter)", @@ -135,6 +143,7 @@ var stateTransitionCommand = cli.Command{ t8ntool.TraceFlag, t8ntool.TraceDisableMemoryFlag, t8ntool.TraceDisableStackFlag, + t8ntool.TraceDisableReturnDataFlag, t8ntool.OutputAllocFlag, t8ntool.OutputResultFlag, t8ntool.InputAllocFlag, @@ -170,6 +179,8 @@ func init() { ReceiverFlag, DisableMemoryFlag, DisableStackFlag, + DisableStorageFlag, + DisableReturnDataFlag, EVMInterpreterFlag, } app.Commands = []cli.Command{ diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 0779706763f313f0766a42a27323ecaa30985ab0..a82427eec00e8bde96bb4a80b6ab65505f1307e2 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -111,9 +111,11 @@ func runCmd(ctx *cli.Context) error { glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) log.Root().SetHandler(glogger) logconfig := &vm.LogConfig{ - DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), - Debug: ctx.GlobalBool(DebugFlag.Name), + DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), + DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), + DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name), + Debug: ctx.GlobalBool(DebugFlag.Name), } var ( diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 60eded7a63c212fc45c43df4d9191dfc059f90b6..78bd7a93ea6099c6e4a8e994c6100384bcd73046 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -61,8 +61,10 @@ func stateTestCmd(ctx *cli.Context) error { // Configure the EVM logger config := &vm.LogConfig{ - DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), - DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), + DisableStack: ctx.GlobalBool(DisableStackFlag.Name), + DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), + DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name), } var ( tracer vm.Tracer diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 25166efe724dca2e9242034d3e09aa49e0506cef..1668f797e95dd99fcce010adfceb945ae2627672 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -104,12 +104,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{ } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. -func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { - gas := p.RequiredGas(input) - if contract.UseGas(gas) { - return p.Run(input) +// It returns +// - the returned bytes, +// - the _remaining_ gas, +// - any error that occurred +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { + gasCost := p.RequiredGas(input) + if suppliedGas < gasCost { + return nil, 0, ErrOutOfGas } - return nil, ErrOutOfGas + suppliedGas -= gasCost + output, err := p.Run(input) + return output, suppliedGas, err } // ECRECOVER implemented as a native contract. @@ -199,6 +205,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { type bigModExp struct{} var ( + big0 = big.NewInt(0) big1 = big.NewInt(1) big4 = big.NewInt(4) big8 = big.NewInt(8) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 1957514dc3d05f248df01630daa654559e527a0e..8923047328d40f9c3aa3382d9f30348cc4a64411 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -73,10 +73,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(uint256.Int), p.RequiredGas(in), false /* skipAnalysis */) - t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { - if res, err := RunPrecompiledContract(p, in, contract); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -92,10 +91,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(uint256.Int), p.RequiredGas(in)-1, false /* skipAnalysis */) - t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + gas := p.RequiredGas(in) - 1 + + t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { + _, _, err := RunPrecompiledContract(p, in, gas) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -110,11 +109,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) - contract := NewContract(AccountRef(common.HexToAddress("31337")), - nil, new(uint256.Int), p.RequiredGas(in), false /* skipAnalysis */) - + gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + _, _, err := RunPrecompiledContract(p, in, gas) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -133,8 +130,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { p := allPrecompiles[common.HexToAddress(addr)] in := common.Hex2Bytes(test.Input) reqGas := p.RequiredGas(in) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(uint256.Int), reqGas, false /* skipAnalysis */) var ( res []byte @@ -142,14 +137,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { data = make([]byte, len(in)) ) - bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) { + bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) { bench.ReportAllocs() start := time.Now().Nanosecond() bench.ResetTimer() for i := 0; i < bench.N; i++ { - contract.Gas = reqGas copy(data, in) - res, err = RunPrecompiledContract(p, data, contract) + res, _, err = RunPrecompiledContract(p, data, reqGas) } bench.StopTimer() elapsed := float64(time.Now().Nanosecond() - start) diff --git a/core/vm/evm.go b/core/vm/evm.go index 51e6aa7a7ec93c40dba90c4b554bf9ade78e90c1..5648c2a9070e53aa29a27eb4efc8438e97e7ab90 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -45,23 +45,24 @@ type ( GetHashFunc func(uint64) common.Hash ) +func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { + var precompiles map[common.Address]PrecompiledContract + switch { + case evm.chainRules.IsYoloV1: + precompiles = PrecompiledContractsYoloV1 + case evm.chainRules.IsIstanbul: + precompiles = PrecompiledContractsIstanbul + case evm.chainRules.IsByzantium: + precompiles = PrecompiledContractsByzantium + default: + precompiles = PrecompiledContractsHomestead + } + p, ok := precompiles[addr] + return p, ok +} + // 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, readOnly bool) ([]byte, error) { - if contract.CodeAddr != nil { - precompiles := PrecompiledContractsHomestead - if evm.chainRules.IsByzantium { - precompiles = PrecompiledContractsByzantium - } - if evm.chainRules.IsIstanbul { - precompiles = PrecompiledContractsIstanbul - } - if evm.chainRules.IsYoloV1 { - precompiles = PrecompiledContractsYoloV1 - } - if p := precompiles[*contract.CodeAddr]; p != nil { - return RunPrecompiledContract(p, input, contract) - } - } for _, interpreter := range evm.interpreters { if interpreter.CanRun(contract.Code) { if evm.interpreter != interpreter { @@ -208,22 +209,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance - if !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) { + if value.Sign() != 0 && !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } + p, isPrecompile := evm.precompile(addr) + var ( to = AccountRef(addr) snapshot = evm.IntraBlockState.Snapshot() ) - if !evm.IntraBlockState.Exist(addr) { - precompiles := PrecompiledContractsHomestead - if evm.chainRules.IsByzantium { - precompiles = PrecompiledContractsByzantium - } - if evm.chainRules.IsIstanbul { - precompiles = PrecompiledContractsIstanbul - } - if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 { + if !evm.IntraBlockState.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug { _ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, false, input, gas, value.ToBig()) @@ -237,34 +232,47 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.IntraBlockState.CreateAccount(addr, false) } evm.Transfer(evm.IntraBlockState, 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, evm.vmConfig.SkipAnalysis) - contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.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 if evm.vmConfig.Debug { _ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, false, input, gas, value.ToBig()) - - defer func() { // Lazy evaluation of the parameters - _ = evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, gas-contract.Gas, time.Since(start), err) - }() + defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters + evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) + }(gas, time.Now()) } - ret, err = run(evm, contract, input, false) + if isPrecompile { + ret, gas, err = RunPrecompiledContract(p, input, gas) + } else { + // 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. + code := evm.StateDB.GetCode(addr) + if len(code) == 0 { + ret, err = nil, nil // gas is unchanged + } else { + addrCopy := addr + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := NewContract(caller, AccountRef(addrCopy), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } + } // 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. if err != nil { evm.IntraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas) + gas = 0 } + // TODO: consider clearing up unused snapshots: + //} else { + // evm.StateDB.DiscardSnapshot(snapshot) } - return ret, contract.Gas, err + return ret, gas, err } // CallCode executes the contract associated with the addr with the given input @@ -291,21 +299,26 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, } var ( snapshot = evm.IntraBlockState.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. - contract := NewContract(caller, to, value, gas, evm.vmConfig.SkipAnalysis) - contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr)) - ret, err = run(evm, contract, input, false) + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(p, input, gas) + } else { + addrCopy := addr + // 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(caller.Address()), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.IntraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas) + gas = 0 } } - return ret, contract.Gas, err + return ret, gas, err } // DelegateCall executes the contract associated with the addr with the given input @@ -321,22 +334,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( snapshot = evm.IntraBlockState.Snapshot() - to = AccountRef(caller.Address()) - ) - // Initialise a new contract and make initialise the delegate values - contract := NewContract(caller, to, nil, gas, evm.vmConfig.SkipAnalysis).AsDelegate() - contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr)) - ret, err = run(evm, contract, input, false) + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(p, input, gas) + } else { + addrCopy := addr + // Initialise a new contract and make initialise the delegate values + contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.IntraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas) + gas = 0 } } - return ret, contract.Gas, err + return ret, gas, err } // StaticCall executes the contract associated with the addr with the given input @@ -351,14 +368,12 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( - to = AccountRef(addr) - snapshot = evm.IntraBlockState.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. - contract := NewContract(caller, to, new(uint256.Int), gas, evm.vmConfig.SkipAnalysis) - contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr)) + // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped. + // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced + // after all empty accounts were deleted, so this is not required. However, if we omit this, + // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. + // We could change this, but for now it's left for legacy reasons + var snapshot = evm.StateDB.Snapshot() // 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, @@ -366,17 +381,30 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // future scenarios evm.IntraBlockState.AddBalance(addr, u256.Num0) - // 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, true) + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(p, input, gas) + } else { + // At this point, we use a copy of address. If we don't, the go compiler will + // leak the 'contract' to the outer scope, and make allocation for 'contract' + // even if the actual execution ends on RunPrecompiled above. + addrCopy := addr + // 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(addrCopy), new(big.Int), gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + // 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, true) + gas = contract.Gas + } if err != nil { evm.IntraBlockState.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas) + gas = 0 } } - return ret, contract.Gas, err + return ret, gas, err } type codeAndHash struct { @@ -485,7 +513,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint2 // DESCRIBED: docs/programmers_guide/guide.md#nonce func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.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()) + contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr) } diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 9f3e3e2c5934ce8168ebe230a99d138197bf6b24..e1a79a303e38211b4355bbbbc4518ba2378a72a3 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -24,6 +24,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { MemorySize int `json:"memSize"` Stack []*math.HexOrDecimal256 `json:"stack"` ReturnStack []math.HexOrDecimal64 `json:"returnStack"` + ReturnData []byte `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` @@ -50,6 +51,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.ReturnStack[k] = math.HexOrDecimal64(v) } } + enc.ReturnData = s.ReturnData enc.Storage = s.Storage enc.Depth = s.Depth enc.RefundCounter = s.RefundCounter @@ -70,6 +72,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { MemorySize *int `json:"memSize"` Stack []*math.HexOrDecimal256 `json:"stack"` ReturnStack []math.HexOrDecimal64 `json:"returnStack"` + ReturnData []byte `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` RefundCounter *uint64 `json:"refund"` @@ -104,11 +107,14 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { } } if dec.ReturnStack != nil { - s.ReturnStack = make([]uint64, len(dec.ReturnStack)) + s.ReturnStack = make([]uint32, len(dec.ReturnStack)) for k, v := range dec.ReturnStack { - s.ReturnStack[k] = uint64(v) + s.ReturnStack[k] = uint32(v) } } + if dec.ReturnData != nil { + s.ReturnData = dec.ReturnData + } if dec.Storage != nil { s.Storage = dec.Storage } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d35d640a82a8a5c2fe084a3bdc8c39486b5c16fc..00001deba20cba3ecd1a8da97822d4682fa1a290 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -606,7 +606,7 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ if !callContext.contract.validJumpSubdest(posU64) { return nil, ErrInvalidJump } - callContext.rstack.Push(*pc) + callContext.rstack.push(uint32(*pc)) *pc = posU64 + 1 return nil, nil } @@ -618,7 +618,7 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) // Other than the check that the return stack is not empty, there is no // need to validate the pc from 'returns', since we only ever push valid //values onto it via jumpsub. - *pc = callContext.rstack.Pop() + 1 + *pc = uint64(callContext.rstack.pop()) + 1 return nil, nil } @@ -652,9 +652,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] stackvalue := size callContext.contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig()) + //TODO: use uint256.Int instead of converting with toBig() + var bigVal = big0 + if !value.IsZero() { + bigVal = value.ToBig() + } - stackValue := size + res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only @@ -688,9 +692,16 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ // Apply EIP150 gas -= gas / 64 callContext.contract.UseGas(gas) + // reuse size int for stackvalue + stackvalue := size + //TODO: use uint256.Int instead of converting with toBig() + bigEndowment := big0 + if !endowment.IsZero() { + bigEndowment = endowment.ToBig() + } res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, &endowment, salt.ToBig()) - stackValue := salt + bigEndowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { @@ -720,10 +731,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by // Get the arguments from the memory. args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + var bigVal = big0 + //TODO: use uint256.Int instead of converting with toBig() + // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls), + // but it would make more sense to extend the usage of uint256.Int if !value.IsZero() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal) + if err != nil { temp.Clear() } else { @@ -756,10 +774,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( // Get arguments from the memory. args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + //TODO: use uint256.Int instead of converting with toBig() + var bigVal = big0 if !value.IsZero() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() } else { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 59ab8081b2aae2bdc7259715225521431daa3ee4..53c83a9f8749938ecb4d9c30737e731ec4d28941 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -215,14 +215,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged bool // deferred Tracer should ignore already logged steps res []byte // result of the opcode execution function ) - defer stackPool.Put(locStack) + // Don't move this deferrred function, it's placed before the capturestate-deferred method, + // so that it get's executed _after_: the capturestate needs the stacks before + // they are returned to the pools + defer func() { + returnStack(stack) + returnRStack(returns) + }() contract.Input = input if in.cfg.Debug { defer func() { if err != nil { if !logged { - _ = in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, returns, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) } else { _ = in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, returns, contract, in.evm.depth, err) } @@ -307,7 +313,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } if in.cfg.Debug { - _ = in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, locStack, returns, contract, in.evm.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err) logged = true } @@ -316,7 +322,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // if the operation clears the return data (e.g. it has returning data) // set the last return to the result of the operation. if operation.returns { - in.returnData = res + in.returnData = common.CopyBytes(res) } switch { diff --git a/core/vm/logger.go b/core/vm/logger.go index d31848d40e18eac3491e3878e260a4d75cf998e7..18efafccf8e447d85dd3b160cb061331db4a78e0 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -48,11 +48,12 @@ func (s Storage) Copy() Storage { // LogConfig are the configuration options for structured logger the EVM 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 + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + DisableReturnData bool // disable return data capture + Debug bool // print output during capture end + Limit int // maximum length of output, but zero means unlimited } //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go @@ -67,7 +68,8 @@ type StructLog struct { Memory []byte `json:"memory"` MemorySize int `json:"memSize"` Stack []*big.Int `json:"stack"` - ReturnStack []uint64 `json:"returnStack"` + ReturnStack []uint32 `json:"returnStack"` + ReturnData []byte `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` @@ -104,10 +106,10 @@ func (s *StructLog) ErrorString() string { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { - CaptureStart(depth int, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error - CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *Contract, depth int, err error) error - CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *Contract, depth int, err error) error - CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error + CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error + CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error + CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error CaptureCreate(creator common.Address, creation common.Address) error CaptureAccountRead(account common.Address) error CaptureAccountWrite(account common.Address) error @@ -146,7 +148,7 @@ func (l *StructLogger) CaptureStart(depth int, from common.Address, to common.Ad // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { return errTraceLimitReached @@ -181,9 +183,9 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui stck[i] = new(big.Int).Set(item.ToBig()) } } - var rstack []uint64 + var rstack []uint32 if !l.cfg.DisableStack && rStack != nil { - rstck := make([]uint64, len(rStack.data)) + rstck := make([]uint32, len(rStack.data)) copy(rstck, rStack.data) } // Copy a snapshot of the current storage to a new container @@ -212,10 +214,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui } storage = l.storage[contract.Address()].Copy() } - var rstack []uint64 - if !l.cfg.DisableStack && rStack != nil { - rstck := make([]uint64, len(rStack.Data())) - copy(rstck, rStack.Data()) + var rdata []byte + if !l.cfg.DisableReturnData { + rdata = make([]byte, len(rData)) + copy(rdata, rData) } // create a new snapshot of the EVM. log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.IntraBlockState.GetRefund(), err} @@ -297,6 +299,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) { fmt.Fprintf(writer, "%x: %x\n", h, item) } } + if len(log.ReturnData) > 0 { + fmt.Fprintln(writer, "ReturnData:") + fmt.Fprint(writer, hex.Dump(log.ReturnData)) + } fmt.Fprintln(writer) } } @@ -348,7 +354,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b return nil } -func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *Contract, depth int, err error) error { +func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) if !t.cfg.DisableStack { // format stack diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index f6bfc9a0ed268d157c25107a2ff1fa616f3b8e6c..9826c6433841831331300cc0744beebc405d9d08 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -47,7 +47,7 @@ func (l *JSONLogger) CaptureStart(depth int, from common.Address, to common.Addr } // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *Contract, depth int, err error) error { +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error { log := StructLog{ Pc: pc, Op: op, @@ -71,6 +71,9 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint log.Stack = logstack log.ReturnStack = rStack.data } + if !l.cfg.DisableReturnData { + log.ReturnData = rData + } return l.encoder.Encode(log) } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index be0ee8242210ed0c268bd6e23d32d420e6528b00..4575de8769bf328804bdbd9d560294a192fd4b4f 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -55,13 +55,20 @@ type Config struct { func setDefaults(cfg *Config) { if cfg.ChainConfig == nil { cfg.ChainConfig = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: new(big.Int), - DAOForkBlock: new(big.Int), - DAOForkSupport: false, - EIP150Block: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: new(big.Int), + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP150Hash: common.Hash{}, + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + PetersburgBlock: new(big.Int), + IstanbulBlock: new(big.Int), + MuirGlacierBlock: new(big.Int), + YoloV1Block: nil, } } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index d12186d22329787b3a76dd6b42bda4c0cd415aae..30cb02a4e10ad1398bffed76815970c98d9d8533 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -328,34 +328,6 @@ func TestBlockhash(t *testing.T) { } } -// BenchmarkSimpleLoop test a pretty simple loop which loops -// 1M (1 048 575) times. -// Takes about 200 ms -func BenchmarkSimpleLoop(b *testing.B) { - // 0xfffff = 1048575 loops - code := []byte{ - byte(vm.PUSH3), 0x0f, 0xff, 0xff, - byte(vm.JUMPDEST), // [ count ] - byte(vm.PUSH1), 1, // [count, 1] - byte(vm.SWAP1), // [1, count] - byte(vm.SUB), // [ count -1 ] - byte(vm.DUP1), // [ count -1 , count-1] - byte(vm.PUSH1), 4, // [count-1, count -1, label] - byte(vm.JUMPI), // [ 0 ] - byte(vm.STOP), - } - //tracer := vm.NewJSONLogger(nil, os.Stdout) - //Execute(code, nil, &Config{ - // EVMConfig: vm.Config{ - // Debug: true, - // Tracer: tracer, - // }}) - - for i := 0; i < b.N; i++ { - _, _, _ = Execute(code, nil, nil, 0) - } -} - type stepCounter struct { inner *vm.JSONLogger steps int @@ -365,7 +337,7 @@ func (s *stepCounter) CaptureStart(_ int, from common.Address, to common.Address return nil } -func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *vm.Contract, depth int, err error) error { +func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error { s.steps++ // Enable this for more output //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) @@ -625,3 +597,160 @@ func DisabledTestEipExampleCases(t *testing.T) { "allowed, and causes an error", code) } } + +// benchmarkNonModifyingCode benchmarks code, but if the code modifies the +// state, this should not be used, since it does not reset the state between runs. +func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) { + cfg := new(Config) + setDefaults(cfg) + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + cfg.GasLimit = gas + var ( + destination = common.BytesToAddress([]byte("contract")) + vmenv = NewEnv(cfg) + sender = vm.AccountRef(cfg.Origin) + ) + cfg.State.CreateAccount(destination) + eoa := common.HexToAddress("E0") + { + cfg.State.CreateAccount(eoa) + cfg.State.SetNonce(eoa, 100) + } + reverting := common.HexToAddress("EE") + { + cfg.State.CreateAccount(reverting) + cfg.State.SetCode(reverting, []byte{ + byte(vm.PUSH1), 0x00, + byte(vm.PUSH1), 0x00, + byte(vm.REVERT), + }) + } + + //cfg.State.CreateAccount(cfg.Origin) + // set the receiver's (the executing contract) code for execution. + cfg.State.SetCode(destination, code) + vmenv.Call(sender, destination, nil, gas, cfg.Value) + + b.Run(name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + vmenv.Call(sender, destination, nil, gas, cfg.Value) + } + }) +} + +// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG +// 55 ms +func BenchmarkSimpleLoop(b *testing.B) { + + staticCallIdentity := []byte{ + byte(vm.JUMPDEST), // [ count ] + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.PUSH1), 0x4, // address of identity + byte(vm.GAS), // gas + byte(vm.STATICCALL), + byte(vm.POP), // pop return value + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + callIdentity := []byte{ + byte(vm.JUMPDEST), // [ count ] + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.DUP1), // value + byte(vm.PUSH1), 0x4, // address of identity + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), // pop return value + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + callInexistant := []byte{ + byte(vm.JUMPDEST), // [ count ] + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.DUP1), // value + byte(vm.PUSH1), 0xff, // address of existing contract + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), // pop return value + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + callEOA := []byte{ + byte(vm.JUMPDEST), // [ count ] + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.DUP1), // value + byte(vm.PUSH1), 0xE0, // address of EOA + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), // pop return value + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + loopingCode := []byte{ + byte(vm.JUMPDEST), // [ count ] + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.DUP1), // out insize + byte(vm.DUP1), // in offset + byte(vm.PUSH1), 0x4, // address of identity + byte(vm.GAS), // gas + + byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + calllRevertingContractWithInput := []byte{ + byte(vm.JUMPDEST), // + // push args for the call + byte(vm.PUSH1), 0, // out size + byte(vm.DUP1), // out offset + byte(vm.PUSH1), 0x20, // in size + byte(vm.PUSH1), 0x00, // in offset + byte(vm.PUSH1), 0x00, // value + byte(vm.PUSH1), 0xEE, // address of reverting contract + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), // pop return value + byte(vm.PUSH1), 0, // jumpdestination + byte(vm.JUMP), + } + + //tracer := vm.NewJSONLogger(nil, os.Stdout) + //Execute(loopingCode, nil, &Config{ + // EVMConfig: vm.Config{ + // Debug: true, + // Tracer: tracer, + // }}) + // 100M gas + benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b) + benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b) + benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b) + benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b) + benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b) + benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b) + + //benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b) + //benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b) +} diff --git a/core/vm/stack/stack.go b/core/vm/stack/stack.go index c6a8aaf0f477f09af49af37f1e9f79f8ecdc7baa..c15ef568631e9fd4981f3809dcf9e83314796f4f 100644 --- a/core/vm/stack/stack.go +++ b/core/vm/stack/stack.go @@ -18,10 +18,17 @@ package stack import ( "fmt" + "sync" "github.com/holiman/uint256" ) +var stackPool = sync.Pool{ + New: func() interface{} { + return &Stack{data: make([]uint256.Int, 0, 16)} + }, +} + // 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. @@ -30,6 +37,9 @@ type Stack struct { } func New(n ...int) *Stack { + return stackPool.Get().(*Stack) +} + if len(n) > 0 { return &Stack{data: make([]uint256.Int, 0, 16)} } @@ -96,19 +106,31 @@ func (st *Stack) Print() { fmt.Println("#############") } +var rStackPool = sync.Pool{ + New: func() interface{} { + return &ReturnStack{data: make([]uint32, 0, 10)} + }, +} + // ReturnStack is an object for basic return stack operations. type ReturnStack struct { - data []uint64 + data []uint32 +} + +func newReturnStack() *ReturnStack { + return rStackPool.Get().(*ReturnStack) } func NewReturnStack() *ReturnStack { return &ReturnStack{data: make([]uint64, 0, 1024)} + rStackPool.Put(rs) } func (st *ReturnStack) Push(d uint64) { st.data = append(st.data, d) } +// A uint32 is sufficient as for code below 4.2G func (st *ReturnStack) Pop() (ret uint64) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 0827d793882ffbde6c33283b36344f59093a7dfa..676de898126fc632cb794c5d6d7fbd5c494bf5b8 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -549,7 +549,7 @@ func (jst *Tracer) CaptureStart(depth int, from common.Address, to common.Addres } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rStack *stack.ReturnStack, contract *vm.Contract, depth int, err error) error { +func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error { if jst.err == nil { // Initialize the context if it wasn't done yet if !jst.inited { diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index ca656e76abe58b0d3a7ccd4a3d8304b1178f5afd..4b6d5de009efd8bc5e67201dadeb9fe1be0ceec6 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -171,10 +171,10 @@ func TestHaltBetweenSteps(t *testing.T) { env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) contract := vm.NewContract(&account{}, &account{}, uint256.NewInt(), 0, false /* skipAnalysis */) - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) //nolint:errcheck + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) //nolint:errcheck + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil) if _, err := tracer.GetResult(); err.Error() != timeout.Error() { t.Errorf("Expected timeout error, got %v", err)