diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go
deleted file mode 100644
index 1b401a5cae..0000000000
--- a/cmd/evm/json_logger.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 main
-
-import (
- "encoding/json"
- "io"
- "math/big"
- "time"
-
- "github.com/tomochain/tomochain/common"
- "github.com/tomochain/tomochain/common/math"
- "github.com/tomochain/tomochain/core/vm"
-)
-
-type JSONLogger struct {
- encoder *json.Encoder
- cfg *vm.LogConfig
-}
-
-func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
- return &JSONLogger{json.NewEncoder(writer), cfg}
-}
-
-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 *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
- log := vm.StructLog{
- Pc: pc,
- Op: op,
- Gas: gas,
- GasCost: cost,
- MemorySize: memory.Len(),
- Storage: nil,
- Depth: depth,
- 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 *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.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/cmd/evm/runner.go b/cmd/evm/runner.go
index 5d3b242898..3a19a79e32 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -20,21 +20,21 @@ import (
"bytes"
"encoding/json"
"fmt"
- "github.com/tomochain/tomochain/core/rawdb"
"io/ioutil"
"os"
+ goruntime "runtime"
"runtime/pprof"
"time"
- goruntime "runtime"
-
"github.com/tomochain/tomochain/cmd/evm/internal/compiler"
"github.com/tomochain/tomochain/cmd/utils"
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/core"
+ "github.com/tomochain/tomochain/core/rawdb"
"github.com/tomochain/tomochain/core/state"
"github.com/tomochain/tomochain/core/vm"
"github.com/tomochain/tomochain/core/vm/runtime"
+ "github.com/tomochain/tomochain/eth/tracers/logger"
"github.com/tomochain/tomochain/log"
"github.com/tomochain/tomochain/params"
cli "gopkg.in/urfave/cli.v1"
@@ -73,26 +73,26 @@ func runCmd(ctx *cli.Context) error {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
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),
+ logconfig := &logger.Config{
+ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
+ DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
}
var (
- tracer vm.Tracer
- debugLogger *vm.StructLogger
+ tracer vm.EVMLogger
+ debugLogger *logger.StructLogger
statedb *state.StateDB
chainConfig *params.ChainConfig
sender = common.StringToAddress("sender")
receiver = common.StringToAddress("receiver")
)
if ctx.GlobalBool(MachineFlag.Name) {
- tracer = NewJSONLogger(logconfig, os.Stdout)
+ tracer = logger.NewJSONLogger(logconfig, os.Stdout)
} else if ctx.GlobalBool(DebugFlag.Name) {
- debugLogger = vm.NewStructLogger(logconfig)
+ debugLogger = logger.NewStructLogger(logconfig)
tracer = debugLogger
} else {
- debugLogger = vm.NewStructLogger(logconfig)
+ debugLogger = logger.NewStructLogger(logconfig)
}
if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
@@ -216,10 +216,10 @@ func runCmd(ctx *cli.Context) error {
if ctx.GlobalBool(DebugFlag.Name) {
if debugLogger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
- vm.WriteTrace(os.Stderr, debugLogger.StructLogs())
+ logger.WriteTrace(os.Stderr, debugLogger.StructLogs())
}
fmt.Fprintln(os.Stderr, "#### LOGS ####")
- vm.WriteLogs(os.Stderr, statedb.Logs())
+ logger.WriteLogs(os.Stderr, statedb.Logs())
}
if ctx.GlobalBool(StatDumpFlag.Name) {
@@ -235,7 +235,7 @@ Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
}
if tracer != nil {
- tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err)
+ tracer.CaptureEnd(ret, initialGas-leftOverGas, err)
} else {
fmt.Printf("0x%x\n", ret)
if err != nil {
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 5499be6962..53e7a6e7e5 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -25,6 +25,7 @@ import (
"github.com/tomochain/tomochain/core/state"
"github.com/tomochain/tomochain/core/vm"
+ "github.com/tomochain/tomochain/eth/tracers/logger"
"github.com/tomochain/tomochain/log"
"github.com/tomochain/tomochain/tests"
@@ -56,24 +57,24 @@ func stateTestCmd(ctx *cli.Context) error {
log.Root().SetHandler(glogger)
// Configure the EVM logger
- config := &vm.LogConfig{
- DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
- DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
+ config := &logger.Config{
+ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
+ DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
}
var (
- tracer vm.Tracer
- debugger *vm.StructLogger
+ tracer vm.EVMLogger
+ debugger *logger.StructLogger
)
switch {
case ctx.GlobalBool(MachineFlag.Name):
- tracer = NewJSONLogger(config, os.Stderr)
+ tracer = logger.NewJSONLogger(config, os.Stderr)
case ctx.GlobalBool(DebugFlag.Name):
- debugger = vm.NewStructLogger(config)
+ debugger = logger.NewStructLogger(config)
tracer = debugger
default:
- debugger = vm.NewStructLogger(config)
+ debugger = logger.NewStructLogger(config)
}
// Load the test content from the input file
src, err := ioutil.ReadFile(ctx.Args().First())
@@ -114,7 +115,7 @@ func stateTestCmd(ctx *cli.Context) error {
if ctx.GlobalBool(DebugFlag.Name) {
if debugger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
- vm.WriteTrace(os.Stderr, debugger.StructLogs())
+ logger.WriteTrace(os.Stderr, debugger.StructLogs())
}
}
}
diff --git a/core/state_transition.go b/core/state_transition.go
index 9a2b079249..62717e090c 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -42,8 +42,10 @@ The state transitioning model does all all the necessary work to work out a vali
3) Create a new state object if the recipient is \0*32
4) Value transfer
== If contract creation ==
- 4a) Attempt to run transaction data
- 4b) If valid, use result as code for the new state object
+
+ 4a) Attempt to run transaction data
+ 4b) If valid, use result as code for the new state object
+
== end ==
5) Run Script section
6) Derive new state root
@@ -219,6 +221,12 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
if err = st.preCheck(); err != nil {
return
}
+
+ if tracer := st.evm.Config.Tracer; tracer != nil {
+ tracer.CaptureTxStart(st.initialGas)
+ // TODO(trinhdn): defer the CaptureTxEnd with remaining gas
+ }
+
msg := st.msg
sender := st.from() // err checked in preCheck
diff --git a/core/vm/eips.go b/core/vm/eips.go
index be00512c84..8bb62efebd 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -59,9 +59,9 @@ func enable1884(jt *JumpTable) {
}
}
-func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
- callContext.stack.push(balance)
+func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.Contract.Address()))
+ callContext.Stack.push(balance)
return nil, nil
}
@@ -79,9 +79,9 @@ func enable1344(jt *JumpTable) {
}
// opChainID implements CHAINID opcode
-func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainId)
- callContext.stack.push(chainId)
+ callContext.Stack.push(chainId)
return nil, nil
}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 4bd7ff77a1..8c91395ab9 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -17,15 +17,14 @@
package vm
import (
- "github.com/tomochain/tomochain/tomox/tradingstate"
"errors"
- "github.com/tomochain/tomochain/params"
"math/big"
"sync/atomic"
- "time"
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/crypto"
+ "github.com/tomochain/tomochain/params"
+ "github.com/tomochain/tomochain/tomox/tradingstate"
)
// emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -132,7 +131,7 @@ type EVM struct {
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
- vmConfig Config
+ Config Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreters []Interpreter
@@ -150,16 +149,16 @@ type EVM struct {
// only ever be used *once*.
func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
- Context: ctx,
- StateDB: statedb,
+ Context: ctx,
+ StateDB: statedb,
tradingStateDB: tradingStateDB,
- vmConfig: vmConfig,
- chainConfig: chainConfig,
- chainRules: chainConfig.Rules(ctx.BlockNumber),
- interpreters: make([]Interpreter, 0, 1),
+ Config: vmConfig,
+ chainConfig: chainConfig,
+ chainRules: chainConfig.Rules(ctx.BlockNumber),
+ interpreters: make([]Interpreter, 0, 1),
}
- // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
+ // Config.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]
@@ -188,7 +187,7 @@ func (evm *EVM) Interpreter() Interpreter {
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
- if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ if evm.Config.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
@@ -202,6 +201,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
+ debug = evm.Config.Tracer != nil
)
if !evm.StateDB.Exist(addr) {
precompiles := PrecompiledContractsHomestead
@@ -215,9 +215,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
if precompiles[addr] == nil && evm.chainRules.IsEIP158 && 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)
+ if debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureEnd(ret, 0, nil)
+ } else {
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureExit(ret, 0, nil)
+ }
}
return nil, gas, nil
}
@@ -229,17 +234,22 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
- // 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.depth == 0 {
- evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
-
- defer func() { // Lazy evaluation of the parameters
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
- }()
+ if debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ defer func(startGas uint64) { // Lazy evaluation of the parameters
+ evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
+ }(gas)
+ } else {
+ // Handle tracer events for entering and exiting a call frame
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
}
+
ret, err = run(evm, contract, input, false)
// When an error was returned by the EVM or when setting the creation code
@@ -262,7 +272,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
- if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ if evm.Config.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
@@ -280,6 +290,15 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)
+
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Tracer != nil {
+ evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
// 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)
@@ -301,7 +320,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
- if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ if evm.Config.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
@@ -312,6 +331,19 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)
+
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Tracer != nil {
+ // NOTE: caller must, at all times be a contract. It should never happen
+ // that caller is something other than a Contract.
+ parent := caller.(*Contract)
+ // DELEGATECALL inherits value from parent call
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
@@ -331,7 +363,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
- if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ if evm.Config.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
@@ -355,6 +387,13 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
evm.StateDB.AddBalance(addr, bigZero)
}
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Tracer != nil {
+ evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(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
@@ -382,7 +421,7 @@ func (c *codeAndHash) Hash() common.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) {
+func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@@ -412,14 +451,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)
- if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ if evm.Config.NoRecursion && evm.depth > 0 {
return nil, address, gas, nil
}
- if evm.vmConfig.Debug && evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
+ if evm.Config.Tracer != nil {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ } else {
+ evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
+ }
}
- start := time.Now()
ret, err := run(evm, contract, nil, false)
@@ -451,8 +493,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if maxCodeSizeExceeded && err == nil {
err = ErrMaxCodeSizeExceeded
}
- if evm.vmConfig.Debug && evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ if evm.Config.Tracer != nil {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
+ } else {
+ evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
+ }
}
return ret, address, contract.Gas, err
@@ -461,7 +507,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// 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)
+ return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
// Create2 creates a new contract using code as deployment code.
@@ -471,7 +517,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
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)
+ return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}
// ChainConfig returns the environment's chain configuration
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 16f3685852..a9beb882b7 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -31,33 +31,33 @@ var (
tt255 = math.BigPow(2, 255)
)
-func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
math.U256(y.Add(x, y))
interpreter.intPool.putOne(x)
return nil, nil
}
-func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
math.U256(y.Sub(x, y))
interpreter.intPool.putOne(x)
return nil, nil
}
-func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(math.U256(x.Mul(x, y)))
+func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.pop()
+ callContext.Stack.push(math.U256(x.Mul(x, y)))
interpreter.intPool.putOne(y)
return nil, nil
}
-func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
if y.Sign() != 0 {
math.U256(y.Div(x, y))
} else {
@@ -67,12 +67,12 @@ func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil
}
-func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
+func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := math.S256(callContext.Stack.pop()), math.S256(callContext.Stack.pop())
res := interpreter.intPool.getZero()
if y.Sign() == 0 || x.Sign() == 0 {
- callContext.stack.push(res)
+ callContext.Stack.push(res)
} else {
if x.Sign() != y.Sign() {
res.Div(x.Abs(x), y.Abs(y))
@@ -80,29 +80,29 @@ func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
} else {
res.Div(x.Abs(x), y.Abs(y))
}
- callContext.stack.push(math.U256(res))
+ callContext.Stack.push(math.U256(res))
}
interpreter.intPool.put(x, y)
return nil, nil
}
-func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
+func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.pop()
if y.Sign() == 0 {
- callContext.stack.push(x.SetUint64(0))
+ callContext.Stack.push(x.SetUint64(0))
} else {
- callContext.stack.push(math.U256(x.Mod(x, y)))
+ callContext.Stack.push(math.U256(x.Mod(x, y)))
}
interpreter.intPool.putOne(y)
return nil, nil
}
-func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
+func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := math.S256(callContext.Stack.pop()), math.S256(callContext.Stack.pop())
res := interpreter.intPool.getZero()
if y.Sign() == 0 {
- callContext.stack.push(res)
+ callContext.Stack.push(res)
} else {
if x.Sign() < 0 {
res.Mod(x.Abs(x), y.Abs(y))
@@ -110,38 +110,38 @@ func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
} else {
res.Mod(x.Abs(x), y.Abs(y))
}
- callContext.stack.push(math.U256(res))
+ callContext.Stack.push(math.U256(res))
}
interpreter.intPool.put(x, y)
return nil, nil
}
-func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- base, exponent := callContext.stack.pop(), callContext.stack.pop()
+func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ base, exponent := callContext.Stack.pop(), callContext.Stack.pop()
// some shortcuts
cmpToOne := exponent.Cmp(big1)
if cmpToOne < 0 { // Exponent is zero
// x ^ 0 == 1
- callContext.stack.push(base.SetUint64(1))
+ callContext.Stack.push(base.SetUint64(1))
} else if base.Sign() == 0 {
// 0 ^ y, if y != 0, == 0
- callContext.stack.push(base.SetUint64(0))
+ callContext.Stack.push(base.SetUint64(0))
} else if cmpToOne == 0 { // Exponent is one
// x ^ 1 == x
- callContext.stack.push(base)
+ callContext.Stack.push(base)
} else {
- callContext.stack.push(math.Exp(base, exponent))
+ callContext.Stack.push(math.Exp(base, exponent))
interpreter.intPool.putOne(base)
}
interpreter.intPool.putOne(exponent)
return nil, nil
}
-func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- back := callContext.stack.pop()
+func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ back := callContext.Stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
- num := callContext.stack.pop()
+ num := callContext.Stack.pop()
mask := back.Lsh(common.Big1, bit)
mask.Sub(mask, common.Big1)
if num.Bit(int(bit)) > 0 {
@@ -150,21 +150,21 @@ func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
num.And(num, mask)
}
- callContext.stack.push(math.U256(num))
+ callContext.Stack.push(math.U256(num))
}
interpreter.intPool.putOne(back)
return nil, nil
}
-func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x := callContext.stack.peek()
+func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x := callContext.Stack.peek()
math.U256(x.Not(x))
return nil, nil
}
-func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
if x.Cmp(y) < 0 {
y.SetUint64(1)
} else {
@@ -174,8 +174,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil
}
-func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
if x.Cmp(y) > 0 {
y.SetUint64(1)
} else {
@@ -185,8 +185,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil
}
-func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
xSign := x.Cmp(tt255)
ySign := y.Cmp(tt255)
@@ -209,8 +209,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil
}
-func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
xSign := x.Cmp(tt255)
ySign := y.Cmp(tt255)
@@ -233,8 +233,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil
}
-func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
if x.Cmp(y) == 0 {
y.SetUint64(1)
} else {
@@ -244,8 +244,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil
}
-func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x := callContext.stack.peek()
+func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x := callContext.Stack.peek()
if x.Sign() > 0 {
x.SetUint64(0)
} else {
@@ -254,32 +254,32 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
return nil, nil
}
-func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(x.And(x, y))
+func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.pop()
+ callContext.Stack.push(x.And(x, y))
interpreter.intPool.putOne(y)
return nil, nil
}
-func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
y.Or(x, y)
interpreter.intPool.putOne(x)
return nil, nil
}
-func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.peek()
+func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y := callContext.Stack.pop(), callContext.Stack.peek()
y.Xor(x, y)
interpreter.intPool.putOne(x)
return nil, nil
}
-func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- th, val := callContext.stack.pop(), callContext.stack.peek()
+func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ th, val := callContext.Stack.pop(), callContext.Stack.peek()
if th.Cmp(common.Big32) < 0 {
b := math.Byte(val, 32, int(th.Int64()))
val.SetUint64(uint64(b))
@@ -290,27 +290,27 @@ func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
return nil, nil
}
-func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y, z := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
if z.Cmp(bigZero) > 0 {
x.Add(x, y)
x.Mod(x, z)
- callContext.stack.push(math.U256(x))
+ callContext.Stack.push(math.U256(x))
} else {
- callContext.stack.push(x.SetUint64(0))
+ callContext.Stack.push(x.SetUint64(0))
}
interpreter.intPool.put(y, z)
return nil, nil
}
-func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ x, y, z := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
if z.Cmp(bigZero) > 0 {
x.Mul(x, y)
x.Mod(x, z)
- callContext.stack.push(math.U256(x))
+ callContext.Stack.push(math.U256(x))
} else {
- callContext.stack.push(x.SetUint64(0))
+ callContext.Stack.push(x.SetUint64(0))
}
interpreter.intPool.put(y, z)
return nil, nil
@@ -319,9 +319,9 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// 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, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]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(callContext.stack.pop()), math.U256(callContext.stack.peek())
+ shift, value := math.U256(callContext.Stack.pop()), math.U256(callContext.Stack.peek())
defer interpreter.intPool.putOne(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 {
@@ -337,9 +337,9 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// 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, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]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(callContext.stack.pop()), math.U256(callContext.stack.peek())
+ shift, value := math.U256(callContext.Stack.pop()), math.U256(callContext.Stack.peek())
defer interpreter.intPool.putOne(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 {
@@ -355,9 +355,9 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// 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, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
- shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop())
+ shift, value := math.U256(callContext.Stack.pop()), math.S256(callContext.Stack.pop())
defer interpreter.intPool.putOne(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 {
@@ -366,19 +366,19 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
} else {
value.SetInt64(-1)
}
- callContext.stack.push(math.U256(value))
+ callContext.Stack.push(math.U256(value))
return nil, nil
}
n := uint(shift.Uint64())
value.Rsh(value, n)
- callContext.stack.push(math.U256(value))
+ callContext.Stack.push(math.U256(value))
return nil, nil
}
-func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- offset, size := callContext.stack.pop(), callContext.stack.pop()
- data := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ offset, size := callContext.Stack.pop(), callContext.Stack.pop()
+ data := callContext.Memory.GetPtr(offset.Int64(), size.Int64())
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@@ -389,73 +389,73 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
interpreter.hasher.Read(interpreter.hasherBuf[:])
evm := interpreter.evm
- if evm.vmConfig.EnablePreimageRecording {
+ if evm.Config.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
interpreter.intPool.put(offset, size)
return nil, nil
}
-func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Address().Bytes()))
+func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(callContext.Contract.Address().Bytes()))
return nil, nil
}
-func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- slot := callContext.stack.peek()
+func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ slot := callContext.Stack.peek()
slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
return nil, nil
}
-func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
+func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
-func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Caller().Bytes()))
+func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(callContext.Contract.Caller().Bytes()))
return nil, nil
}
-func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(callContext.contract.value))
+func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().Set(callContext.Contract.value))
return nil, nil
}
-func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.contract.Input, callContext.stack.pop(), big32)))
+func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.Contract.Input, callContext.Stack.pop(), big32)))
return nil, nil
}
-func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Input))))
+func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.Contract.Input))))
return nil, nil
}
-func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- memOffset = callContext.stack.pop()
- dataOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ memOffset = callContext.Stack.pop()
+ dataOffset = callContext.Stack.pop()
+ length = callContext.Stack.pop()
)
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.contract.Input, dataOffset, length))
+ callContext.Memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.Contract.Input, dataOffset, length))
interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil
}
-func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
+func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
-func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- memOffset = callContext.stack.pop()
- dataOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ memOffset = callContext.Stack.pop()
+ dataOffset = callContext.Stack.pop()
+ length = callContext.Stack.pop()
end = interpreter.intPool.get().Add(dataOffset, length)
)
@@ -464,47 +464,47 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
return nil, ErrReturnDataOutOfBounds
}
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
+ callContext.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
return nil, nil
}
-func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- slot := callContext.stack.peek()
+func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ slot := callContext.Stack.peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
return nil, nil
}
-func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- l := interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Code)))
- callContext.stack.push(l)
+func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ l := interpreter.intPool.get().SetInt64(int64(len(callContext.Contract.Code)))
+ callContext.Stack.push(l)
return nil, nil
}
-func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- memOffset = callContext.stack.pop()
- codeOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ memOffset = callContext.Stack.pop()
+ codeOffset = callContext.Stack.pop()
+ length = callContext.Stack.pop()
)
- codeCopy := getDataBig(callContext.contract.Code, codeOffset, length)
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ codeCopy := getDataBig(callContext.Contract.Code, codeOffset, length)
+ callContext.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
-func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- addr = common.BigToAddress(callContext.stack.pop())
- memOffset = callContext.stack.pop()
- codeOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ addr = common.BigToAddress(callContext.Stack.pop())
+ memOffset = callContext.Stack.pop()
+ codeOffset = callContext.Stack.pop()
+ length = callContext.Stack.pop()
)
codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ callContext.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
@@ -513,16 +513,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// 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
+//
+// (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
+// (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,
+// (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
+// (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,
@@ -531,13 +536,15 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// 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
+// (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,
+// (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, callContext *callCtx) ([]byte, error) {
- slot := callContext.stack.peek()
+func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ slot := callContext.Stack.peek()
address := common.BigToAddress(slot)
if interpreter.evm.StateDB.Empty(address) {
slot.SetUint64(0)
@@ -547,96 +554,96 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
return nil, nil
}
-func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
+func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
return nil, nil
}
-func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- num := callContext.stack.pop()
+func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ num := callContext.Stack.pop()
n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
- callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
+ callContext.Stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
} else {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
}
interpreter.intPool.put(num, n)
return nil, nil
}
-func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
+func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil
}
-func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
+func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
return nil, nil
}
-func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
+func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
return nil, nil
}
-func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty)))
+func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty)))
return nil, nil
}
-func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit)))
+func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit)))
return nil, nil
}
-func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- interpreter.intPool.putOne(callContext.stack.pop())
+func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ interpreter.intPool.putOne(callContext.Stack.pop())
return nil, nil
}
-func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- v := callContext.stack.peek()
+func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ v := callContext.Stack.peek()
offset := v.Int64()
- v.SetBytes(callContext.memory.GetPtr(offset, 32))
+ v.SetBytes(callContext.Memory.GetPtr(offset, 32))
return nil, nil
}
-func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// pop value of the stack
- mStart, val := callContext.stack.pop(), callContext.stack.pop()
- callContext.memory.Set32(mStart.Uint64(), val)
+ mStart, val := callContext.Stack.pop(), callContext.Stack.pop()
+ callContext.Memory.Set32(mStart.Uint64(), val)
interpreter.intPool.put(mStart, val)
return nil, nil
}
-func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64()
- callContext.memory.store[off] = byte(val & 0xff)
+func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ off, val := callContext.Stack.pop().Int64(), callContext.Stack.pop().Int64()
+ callContext.Memory.store[off] = byte(val & 0xff)
return nil, nil
}
-func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- loc := callContext.stack.peek()
- val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), common.BigToHash(loc))
+func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ loc := callContext.Stack.peek()
+ val := interpreter.evm.StateDB.GetState(callContext.Contract.Address(), common.BigToHash(loc))
loc.SetBytes(val.Bytes())
return nil, nil
}
-func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- loc := common.BigToHash(callContext.stack.pop())
- val := callContext.stack.pop()
- interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val))
+func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ loc := common.BigToHash(callContext.Stack.pop())
+ val := callContext.Stack.pop()
+ interpreter.evm.StateDB.SetState(callContext.Contract.Address(), loc, common.BigToHash(val))
interpreter.intPool.putOne(val)
return nil, nil
}
-func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- pos := callContext.stack.pop()
- if !callContext.contract.validJumpdest(pos) {
+func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ pos := callContext.Stack.pop()
+ if !callContext.Contract.validJumpdest(pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
@@ -645,10 +652,10 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
return nil, nil
}
-func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- pos, cond := callContext.stack.pop(), callContext.stack.pop()
+func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ pos, cond := callContext.Stack.pop(), callContext.Stack.pop()
if cond.Sign() != 0 {
- if !callContext.contract.validJumpdest(pos) {
+ if !callContext.Contract.validJumpdest(pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
@@ -660,50 +667,50 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
return nil, nil
}
-func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
return nil, nil
}
-func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(*pc))
+func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(*pc))
return nil, nil
}
-func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(callContext.memory.Len())))
+func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetInt64(int64(callContext.Memory.Len())))
return nil, nil
}
-func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(callContext.contract.Gas))
+func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(callContext.Contract.Gas))
return nil, nil
}
-func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- value = callContext.stack.pop()
- offset, size = callContext.stack.pop(), callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
- gas = callContext.contract.Gas
+ value = callContext.Stack.pop()
+ offset, size = callContext.Stack.pop(), callContext.Stack.pop()
+ input = callContext.Memory.GetCopy(offset.Int64(), size.Int64())
+ gas = callContext.Contract.Gas
)
if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64
}
- callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value)
+ callContext.Contract.UseGas(gas)
+ res, addr, returnGas, suberr := interpreter.evm.Create(callContext.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 interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(value, offset, size)
if suberr == ErrExecutionReverted {
@@ -712,26 +719,26 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
return nil, nil
}
-func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- endowment = callContext.stack.pop()
- offset, size = callContext.stack.pop(), callContext.stack.pop()
- salt = callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
- gas = callContext.contract.Gas
+ endowment = callContext.Stack.pop()
+ offset, size = callContext.Stack.pop(), callContext.Stack.pop()
+ salt = callContext.Stack.pop()
+ input = callContext.Memory.GetCopy(offset.Int64(), size.Int64())
+ gas = callContext.Contract.Gas
)
// Apply EIP150
gas -= gas / 64
- callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, endowment, salt)
+ callContext.Contract.UseGas(gas)
+ res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.Contract, input, gas, endowment, salt)
// Push item on the stack based on the returned error.
if suberr != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ callContext.Stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(endowment, offset, size, salt)
if suberr == ErrExecutionReverted {
@@ -740,143 +747,148 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
return nil, nil
}
-func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ interpreter.intPool.putOne(callContext.Stack.pop())
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+ addr, value, inOffset, inSize, retOffset, retSize := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
toAddr := common.BigToAddress(addr)
value = math.U256(value)
// Get the arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.Memory.GetPtr(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.Call(callContext.Contract, toAddr, args, gas, value)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
- callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ interpreter.intPool.putOne(callContext.Stack.pop())
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+ addr, value, inOffset, inSize, retOffset, retSize := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
toAddr := common.BigToAddress(addr)
value = math.U256(value)
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.Memory.GetPtr(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.CallCode(callContext.Contract, toAddr, args, gas, value)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
- callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ interpreter.intPool.putOne(callContext.Stack.pop())
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+ addr, inOffset, inSize, retOffset, retSize := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
toAddr := common.BigToAddress(addr)
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.Memory.GetPtr(inOffset.Int64(), inSize.Int64())
- ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.DelegateCall(callContext.Contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
- callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ interpreter.intPool.putOne(callContext.Stack.pop())
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
+ addr, inOffset, inSize, retOffset, retSize := callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop(), callContext.Stack.pop()
toAddr := common.BigToAddress(addr)
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.Memory.GetPtr(inOffset.Int64(), inSize.Int64())
- ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.StaticCall(callContext.Contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ callContext.Stack.push(interpreter.intPool.getZero())
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ callContext.Stack.push(interpreter.intPool.get().SetUint64(1))
}
if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret)
- callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- callContext.contract.Gas += returnGas
+ callContext.Contract.Gas += returnGas
interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ offset, size := callContext.Stack.pop(), callContext.Stack.pop()
+ ret := callContext.Memory.GetPtr(offset.Int64(), size.Int64())
interpreter.intPool.put(offset, size)
return ret, nil
}
-func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ offset, size := callContext.Stack.pop(), callContext.Stack.pop()
+ ret := callContext.Memory.GetPtr(offset.Int64(), size.Int64())
interpreter.intPool.put(offset, size)
return ret, nil
}
-func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
return nil, nil
}
-func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
- interpreter.evm.StateDB.AddBalance(common.BigToAddress(callContext.stack.pop()), balance)
+func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ beneficiary := callContext.Stack.pop()
+ balance := interpreter.evm.StateDB.GetBalance(callContext.Contract.Address())
+ interpreter.evm.StateDB.AddBalance(common.BigToAddress(beneficiary), balance)
- interpreter.evm.StateDB.Suicide(callContext.contract.Address())
+ interpreter.evm.StateDB.Suicide(callContext.Contract.Address())
+ if tracer := interpreter.evm.Config.Tracer; tracer != nil {
+ tracer.CaptureEnter(SELFDESTRUCT, callContext.Contract.Address(), common.BigToAddress(beneficiary), []byte{}, 0, balance)
+ tracer.CaptureExit([]byte{}, 0, nil)
+ }
return nil, nil
}
@@ -884,16 +896,16 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
// make log instruction function
func makeLog(size int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
topics := make([]common.Hash, size)
- mStart, mSize := callContext.stack.pop(), callContext.stack.pop()
+ mStart, mSize := callContext.Stack.pop(), callContext.Stack.pop()
for i := 0; i < size; i++ {
- topics[i] = common.BigToHash(callContext.stack.pop())
+ topics[i] = common.BigToHash(callContext.Stack.pop())
}
- d := callContext.memory.GetCopy(mStart.Int64(), mSize.Int64())
+ d := callContext.Memory.GetCopy(mStart.Int64(), mSize.Int64())
interpreter.evm.StateDB.AddLog(&types.Log{
- Address: callContext.contract.Address(),
+ Address: callContext.Contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
@@ -907,24 +919,24 @@ func makeLog(size int) executionFunc {
}
// opPush1 is a specialized version of pushN
-func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
var (
- codeLen = uint64(len(callContext.contract.Code))
+ codeLen = uint64(len(callContext.Contract.Code))
integer = interpreter.intPool.get()
)
*pc += 1
if *pc < codeLen {
- callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
+ callContext.Stack.push(integer.SetUint64(uint64(callContext.Contract.Code[*pc])))
} else {
- callContext.stack.push(integer.SetUint64(0))
+ callContext.Stack.push(integer.SetUint64(0))
}
return nil, nil
}
// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- codeLen := len(callContext.contract.Code)
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ codeLen := len(callContext.Contract.Code)
startMin := codeLen
if int(*pc+1) < startMin {
@@ -937,7 +949,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
}
integer := interpreter.intPool.get()
- callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize)))
+ callContext.Stack.push(integer.SetBytes(common.RightPadBytes(callContext.Contract.Code[startMin:endMin], pushByteSize)))
*pc += size
return nil, nil
@@ -946,8 +958,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.dup(interpreter.intPool, int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.dup(interpreter.intPool, int(size))
return nil, nil
}
}
@@ -956,8 +968,8 @@ func makeDup(size int64) executionFunc {
func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size++
- return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.swap(int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error) {
+ callContext.Stack.swap(int(size))
return nil, nil
}
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 1b96aed67b..025a409f39 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -92,7 +92,7 @@ func init() {
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
var (
- env = NewEVM(Context{}, nil,nil, params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = env.interpreter.(*EVMInterpreter)
@@ -109,7 +109,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x)
stack.push(y)
- opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
+ opFn(&pc, evmInterpreter, &CallCtx{nil, stack, nil})
actual := stack.pop()
if actual.Cmp(expected) != 0 {
@@ -211,7 +211,7 @@ func TestSAR(t *testing.T) {
// getResult is a convenience function to generate the expected values
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var (
- env = NewEVM(Context{}, nil, nil,params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
interpreter = env.interpreter.(*EVMInterpreter)
@@ -223,7 +223,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
y := new(big.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x)
stack.push(y)
- opFn(&pc, interpreter, &callCtx{nil, stack, nil})
+ opFn(&pc, interpreter, &CallCtx{nil, stack, nil})
actual := stack.pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
}
@@ -262,9 +262,9 @@ func TestJsonTestcases(t *testing.T) {
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var (
- env = NewEVM(Context{}, nil,nil, params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env, env.Config)
)
env.interpreter = evmInterpreter
@@ -281,7 +281,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
a := new(big.Int).SetBytes(arg)
stack.push(a)
}
- op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
+ op(&pc, evmInterpreter, &CallCtx{nil, stack, nil})
stack.pop()
}
poolOfIntPools.put(evmInterpreter.intPool)
@@ -497,10 +497,10 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) {
var (
- env = NewEVM(Context{}, nil,nil, params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env, env.Config)
)
env.interpreter = evmInterpreter
@@ -509,12 +509,12 @@ func TestOpMstore(t *testing.T) {
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
- opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
+ opMstore(&pc, evmInterpreter, &CallCtx{mem, stack, nil})
if got := common.Bytes2Hex(mem.GetCopy(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, &callCtx{mem, stack, nil})
+ opMstore(&pc, evmInterpreter, &CallCtx{mem, stack, nil})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
@@ -523,10 +523,10 @@ func TestOpMstore(t *testing.T) {
func BenchmarkOpMstore(bench *testing.B) {
var (
- env = NewEVM(Context{}, nil,nil, params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env, env.Config)
)
env.interpreter = evmInterpreter
@@ -539,17 +539,17 @@ func BenchmarkOpMstore(bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(value, memStart)
- opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
+ opMstore(&pc, evmInterpreter, &CallCtx{mem, stack, nil})
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpSHA3(bench *testing.B) {
var (
- env = NewEVM(Context{}, nil,nil, params.TestChainConfig, Config{})
+ env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env, env.Config)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
@@ -560,7 +560,7 @@ func BenchmarkOpSHA3(bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(big.NewInt(32), start)
- opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
+ opSha3(&pc, evmInterpreter, &CallCtx{mem, stack, nil})
}
poolOfIntPools.put(evmInterpreter.intPool)
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index fc5b17a4f3..3f37fffab0 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -27,10 +27,10 @@ import (
// Config are the configuration options for the Interpreter
type Config struct {
- Debug bool // Enables debugging
- Tracer Tracer // Opcode logger
- NoRecursion bool // Disables call, callcode, delegate call and create
- EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
+ Debug bool // Enables debugging
+ Tracer EVMLogger // Opcode logger
+ NoRecursion bool // Disables call, callcode, delegate call and create
+ EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
JumpTable [256]operation // EVM instruction table, automatically populated if unset
@@ -62,12 +62,12 @@ type Interpreter interface {
CanRun([]byte) bool
}
-// callCtx contains the things that are per-call, such as stack and memory,
+// CallCtx contains the things that are per-call, such as stack and memory,
// but not transients like pc and gas
-type callCtx struct {
- memory *Memory
- stack *Stack
- contract *Contract
+type CallCtx struct {
+ Memory *Memory
+ Stack *Stack
+ Contract *Contract
}
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
@@ -170,10 +170,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
op OpCode // current opcode
mem = NewMemory() // bound memory
stack = newstack() // local stack
- callContext = &callCtx{
- memory: mem,
- stack: stack,
- contract: contract,
+ callContext = &CallCtx{
+ Memory: mem,
+ Stack: stack,
+ Contract: contract,
}
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
@@ -185,19 +185,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
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
+ debug = in.evm.Config.Tracer != nil
)
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 {
+ if debug {
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
}
}
}()
@@ -279,7 +280,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, stack, contract, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 143bf254f7..f441cde4ba 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -21,7 +21,7 @@ import (
)
type (
- executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
+ executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *CallCtx) ([]byte, error)
gasFunc func(*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)
@@ -1143,7 +1143,7 @@ func newFrontierInstructionSet() JumpTable {
valid: true,
},
SELFDESTRUCT: {
- execute: opSuicide,
+ execute: opSelfdestruct,
dynamicGas: gasSelfdestruct,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
diff --git a/core/vm/logger.go b/core/vm/logger.go
index b4a55c68c1..6cd057930b 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -17,243 +17,27 @@
package vm
import (
- "encoding/hex"
- "errors"
- "fmt"
- "io"
"math/big"
- "time"
"github.com/tomochain/tomochain/common"
- "github.com/tomochain/tomochain/common/hexutil"
- "github.com/tomochain/tomochain/common/math"
- "github.com/tomochain/tomochain/core/types"
)
-var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
-
-// Storage represents a contract's storage.
-type Storage map[common.Hash]common.Hash
-
-// Copy duplicates the current storage.
-func (s Storage) Copy() Storage {
- cpy := make(Storage)
- for key, value := range s {
- cpy[key] = value
- }
-
- return cpy
-}
-
-// 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
-}
-
-//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
-
-// 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"`
- RefundCounter uint64 `json:"refund"`
- Err error `json:"-"`
-}
-
-// overrides for gencodec
-type structLogMarshaling struct {
- Stack []*math.HexOrDecimal256
- Gas math.HexOrDecimal64
- GasCost math.HexOrDecimal64
- Memory hexutil.Bytes
- OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
- 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()
- }
- return ""
-}
-
-// Tracer is used to collect execution traces from an EVM transaction
+// EVMLogger is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
// 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(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, contract *Contract, depth int, err error) error
- CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
- CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
-}
-
-// StructLogger is an EVM state logger and implements Tracer.
-//
-// StructLogger can capture state based on the given Log configuration and also keeps
-// a track record of modified storage which is used in reporting snapshots of the
-// contract their storage.
-type StructLogger struct {
- cfg LogConfig
-
- logs []StructLog
- changedValues map[common.Address]Storage
- output []byte
- err error
-}
-
-// NewStructLogger returns a new logger
-func NewStructLogger(cfg *LogConfig) *StructLogger {
- logger := &StructLogger{
- changedValues: make(map[common.Address]Storage),
- }
- if cfg != nil {
- logger.cfg = *cfg
- }
- 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
-}
-
-// CaptureState logs a new structured log message and pushes it out to the environment
-//
-// CaptureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, 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
- }
-
- // initialise new changed values storage container for this contract
- // if not present.
- if l.changedValues[contract.Address()] == nil {
- l.changedValues[contract.Address()] = make(Storage)
- }
-
- // capture SSTORE opcodes and determine the changed value and store
- // it in the local storage container.
- if op == SSTORE && stack.len() >= 2 {
- var (
- value = common.BigToHash(stack.data[stack.len()-2])
- address = common.BigToHash(stack.data[stack.len()-1])
- )
- l.changedValues[contract.Address()][address] = value
- }
- // Copy a snapshot of the current memory state to a new buffer
- var mem []byte
- if !l.cfg.DisableMemory {
- mem = make([]byte, len(memory.Data()))
- copy(mem, memory.Data())
- }
- // Copy a snapshot of the current stack state to a new buffer
- var stck []*big.Int
- if !l.cfg.DisableStack {
- stck = make([]*big.Int, len(stack.Data()))
- for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item)
- }
- }
- // Copy a snapshot of the current storage to a new container
- var storage Storage
- if !l.cfg.DisableStorage {
- storage = l.changedValues[contract.Address()].Copy()
- }
- // create a new snapshot of the EVM.
- 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
-}
-
-// StructLogs returns the captured log entries.
-func (l *StructLogger) StructLogs() []StructLog { return l.logs }
-
-// Error returns the VM error captured by the trace.
-func (l *StructLogger) Error() error { return l.err }
-
-// Output returns the VM return value captured by the trace.
-func (l *StructLogger) Output() []byte { return l.output }
-
-// WriteTrace writes a formatted trace to the given writer
-func WriteTrace(writer io.Writer, logs []StructLog) {
- for _, log := range logs {
- fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
- if log.Err != nil {
- fmt.Fprintf(writer, " ERROR: %v", log.Err)
- }
- fmt.Fprintln(writer)
-
- if len(log.Stack) > 0 {
- fmt.Fprintln(writer, "Stack:")
- for i := len(log.Stack) - 1; i >= 0; i-- {
- fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
- }
- }
- if len(log.Memory) > 0 {
- fmt.Fprintln(writer, "Memory:")
- fmt.Fprint(writer, hex.Dump(log.Memory))
- }
- if len(log.Storage) > 0 {
- fmt.Fprintln(writer, "Storage:")
- for h, item := range log.Storage {
- fmt.Fprintf(writer, "%x: %x\n", h, item)
- }
- }
- fmt.Fprintln(writer)
- }
-}
-
-// WriteLogs writes vm logs in a readable format to the given writer
-func WriteLogs(writer io.Writer, logs []*types.Log) {
- for _, log := range logs {
- fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
-
- for i, topic := range log.Topics {
- fmt.Fprintf(writer, "%08d %x\n", i, topic)
- }
-
- fmt.Fprint(writer, hex.Dump(log.Data))
- fmt.Fprintln(writer)
- }
+type EVMLogger interface {
+ // Transaction level
+ CaptureTxStart(gasLimit uint64)
+ CaptureTxEnd(restGas uint64)
+ // Top call frame
+ CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
+ CaptureEnd(output []byte, gasUsed uint64, err error)
+ // Rest of call frames
+ CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
+ CaptureExit(output []byte, gasUsed uint64, err error)
+ // Opcode level
+ CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *CallCtx, rData []byte, depth int, err error)
+ CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *CallCtx, depth int, err error)
}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
deleted file mode 100644
index 8fce96c646..0000000000
--- a/core/vm/logger_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 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 (
- "github.com/tomochain/tomochain/params"
- "math/big"
- "testing"
-
- "github.com/tomochain/tomochain/common"
- "github.com/tomochain/tomochain/core/state"
-)
-
-type dummyContractRef struct {
- calledForEach bool
-}
-
-func (dummyContractRef) ReturnGas(*big.Int) {}
-func (dummyContractRef) Address() common.Address { return common.Address{} }
-func (dummyContractRef) Value() *big.Int { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte) {}
-func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
- d.calledForEach = true
-}
-func (d *dummyContractRef) SubBalance(amount *big.Int) {}
-func (d *dummyContractRef) AddBalance(amount *big.Int) {}
-func (d *dummyContractRef) SetBalance(*big.Int) {}
-func (d *dummyContractRef) SetNonce(uint64) {}
-func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
-
-type dummyStatedb struct {
- state.StateDB
-}
-
-func (*dummyStatedb) GetRefund() uint64 { return 1337 }
-
-func TestStoreCapture(t *testing.T) {
- var (
- env = NewEVM(Context{}, &dummyStatedb{},nil, params.TestChainConfig, Config{})
- logger = NewStructLogger(nil)
- mem = NewMemory()
- stack = newstack()
- contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
- )
- 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()]))
- }
- exp := common.BigToHash(big.NewInt(1))
- if logger.changedValues[contract.Address()][index] != exp {
- t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index])
- }
-}
diff --git a/eth/api.go b/eth/api.go
index 76a466a49f..c0ac28b839 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -368,7 +368,7 @@ type storageEntry struct {
// StorageRangeAt returns the storage at the given block height and transaction index.
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
- _, _, statedb, err := api.computeTxEnv(blockHash, txIndex, 0)
+ _, _, statedb, _, err := api.computeTxEnv(blockHash, txIndex, 0)
if err != nil {
return StorageRangeResult{}, err
}
@@ -494,11 +494,10 @@ func (api *PublicEthereumAPI) ChainId() hexutil.Uint64 {
}
// GetOwner return masternode owner of the given coinbase address
-func (api *PublicEthereumAPI) GetOwnerByCoinbase(ctx context.Context, coinbase common.Address, blockNr rpc.BlockNumber) (common.Address, error) {
+func (api *PublicEthereumAPI) GetOwnerByCoinbase(ctx context.Context, coinbase common.Address, blockNr rpc.BlockNumber) (common.Address, error) {
statedb, _, err := api.e.ApiBackend.StateAndHeaderByNumber(ctx, blockNr)
if err != nil {
return common.Address{}, err
}
return statedb.GetOwner(coinbase), nil
}
-
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index e1744dc2c1..40e08cd7ee 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -21,7 +21,6 @@ import (
"context"
"errors"
"fmt"
- "github.com/tomochain/tomochain/tomox/tradingstate"
"io/ioutil"
"math/big"
"runtime"
@@ -35,10 +34,12 @@ import (
"github.com/tomochain/tomochain/core/types"
"github.com/tomochain/tomochain/core/vm"
"github.com/tomochain/tomochain/eth/tracers"
+ "github.com/tomochain/tomochain/eth/tracers/logger"
"github.com/tomochain/tomochain/internal/ethapi"
"github.com/tomochain/tomochain/log"
"github.com/tomochain/tomochain/rlp"
"github.com/tomochain/tomochain/rpc"
+ "github.com/tomochain/tomochain/tomox/tradingstate"
"github.com/tomochain/tomochain/trie"
)
@@ -55,7 +56,7 @@ const (
// TraceConfig holds extra parameters to trace functions.
type TraceConfig struct {
- *vm.LogConfig
+ *logger.Config
Tracer *string
Timeout *string
Reexec *uint64
@@ -198,16 +199,22 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
- var balacne *big.Int
+ var balance *big.Int
if tx.To() != nil {
if value, ok := feeCapacity[*tx.To()]; ok {
- balacne = value
+ balance = value
}
}
- msg, _ := tx.AsMessage(signer, balacne, task.block.Number())
+ msg, _ := tx.AsMessage(signer, balance, task.block.Number())
vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil)
- res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
+ traceCtx := &tracers.Context{
+ BlockHash: task.block.Hash(),
+ BlockNumber: task.block.Number(),
+ TxIndex: i,
+ TxHash: tx.Hash(),
+ }
+ res, err := api.traceTx(ctx, msg, vmctx, traceCtx, task.statedb, tomoxState, config)
if err != nil {
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@@ -438,16 +445,22 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
// Fetch and execute the next transaction trace tasks
for task := range jobs {
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
- var balacne *big.Int
+ var balance *big.Int
if txs[task.index].To() != nil {
if value, ok := feeCapacity[*txs[task.index].To()]; ok {
- balacne = value
+ balance = value
}
}
- msg, _ := txs[task.index].AsMessage(signer, balacne, block.Number())
+ msg, _ := txs[task.index].AsMessage(signer, balance, block.Number())
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
- res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
+ traceCtx := &tracers.Context{
+ BlockHash: block.Hash(),
+ BlockNumber: block.Number(),
+ TxIndex: task.index,
+ TxHash: txs[task.index].Hash(),
+ }
+ res, err := api.traceTx(ctx, msg, vmctx, traceCtx, task.statedb, tomoxState, config)
if err != nil {
results[task.index] = &txTraceResult{Error: err.Error()}
continue
@@ -462,14 +475,14 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
for i, tx := range txs {
// Send the trace task over for execution
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
- var balacne *big.Int
+ var balance *big.Int
if tx.To() != nil {
if value, ok := feeCapacity[*tx.To()]; ok {
- balacne = value
+ balance = value
}
}
// Generate the next state snapshot fast without tracing
- msg, _ := tx.AsMessage(signer, balacne, block.Number())
+ msg, _ := tx.AsMessage(signer, balance, block.Number())
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
vmenv := vm.NewEVM(vmctx, statedb, tomoxState, api.config, vm.Config{})
@@ -497,13 +510,13 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*state.StateDB, *tradingstate.TradingStateDB, error) {
// If we have the state fully available, use that
statedb, err := api.eth.blockchain.StateAt(block.Root())
- tomoxState := &tradingstate.TradingStateDB{}
- if err == nil {
- tomoxState, err = api.eth.blockchain.OrderStateAt(block)
- if err == nil {
- return statedb, tomoxState, nil
- }
- }
+ //tomoxState := &tradingstate.TradingStateDB{}
+ //if err == nil {
+ // tomoxState, err = api.eth.blockchain.OrderStateAt(block)
+ // if err == nil {
+ // return statedb, tomoxState, nil
+ // }
+ //}
// Otherwise try to reexec blocks until we find a state or reach our limit
origin := block.NumberU64()
database := state.NewDatabase(api.eth.ChainDb())
@@ -514,10 +527,10 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
break
}
if statedb, err = state.New(block.Root(), database); err == nil {
- tomoxState, err = tradingstate.New(block.Root(), tradingstate.NewDatabase(api.eth.TomoX.GetLevelDB()))
- if err == nil {
- break
- }
+ //tomoxState, err = tradingstate.New(block.Root(), tradingstate.NewDatabase(api.eth.TomoX.GetLevelDB()))
+ //if err == nil {
+ break
+ //}
}
}
if err != nil {
@@ -545,7 +558,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
return nil, nil, fmt.Errorf("block #%d not found", block.NumberU64()+1)
}
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
- _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, tomoxState, vm.Config{}, feeCapacity)
+ _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, nil, vm.Config{}, feeCapacity)
if err != nil {
return nil, nil, err
}
@@ -567,14 +580,14 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
}
size, _ := database.TrieDB().Size()
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "size", size)
- return statedb,tomoxState, nil
+ return statedb, nil, nil
}
// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
// Retrieve the transaction and assemble its EVM context
- tx, blockHash, _, index := core.GetTransaction(api.eth.ChainDb(), hash)
+ tx, blockHash, blockNumber, index := core.GetTransaction(api.eth.ChainDb(), hash)
if tx == nil {
return nil, fmt.Errorf("transaction %x not found", hash)
}
@@ -582,25 +595,34 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), reexec)
+ msg, vmctx, statedb, tomoxState, err := api.computeTxEnv(blockHash, int(index), reexec)
if err != nil {
return nil, err
}
// Trace the transaction and return
- return api.traceTx(ctx, msg, vmctx, statedb, config)
+ traceCtx := &tracers.Context{
+ BlockHash: blockHash,
+ BlockNumber: new(big.Int).SetUint64(blockNumber),
+ TxIndex: int(index),
+ TxHash: hash,
+ }
+ return api.traceTx(ctx, msg, vmctx, traceCtx, statedb, tomoxState, config)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
-func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
+func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, traceCtx *tracers.Context,
+ statedb *state.StateDB, tomoxState *tradingstate.TradingStateDB, config *TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
- tracer vm.Tracer
+ tracer tracers.Tracer
err error
)
switch {
- case config != nil && config.Tracer != nil:
+ case config == nil:
+ tracer = logger.NewStructLogger(nil)
+ case config.Tracer != nil:
// Define a meaningful timeout of a single transaction trace
timeout := defaultTraceTimeout
if config.Timeout != nil {
@@ -608,26 +630,24 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
return nil, err
}
}
- // Constuct the JavaScript tracer to execute with
- if tracer, err = tracers.New(*config.Tracer); err != nil {
+ if t, err := tracers.New(*config.Tracer, traceCtx); err != nil {
return nil, err
+ } else {
+ deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
+ go func() {
+ <-deadlineCtx.Done()
+ if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
+ t.Stop(errors.New("execution timeout"))
+ }
+ }()
+ defer cancel()
+ tracer = t
}
- // Handle timeouts and RPC cancellations
- deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
- go func() {
- <-deadlineCtx.Done()
- tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
- }()
- defer cancel()
-
- case config == nil:
- tracer = vm.NewStructLogger(nil)
-
default:
- tracer = vm.NewStructLogger(config.LogConfig)
+ tracer = logger.NewStructLogger(config.Config)
}
// Run the transaction with tracing enabled.
- vmenv := vm.NewEVM(vmctx, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer})
+ vmenv := vm.NewEVM(vmctx, statedb, tomoxState, api.config, vm.Config{Debug: true, Tracer: tracer})
owner := common.Address{}
ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
@@ -636,15 +656,15 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
}
// Depending on the tracer type, format and return the output
switch tracer := tracer.(type) {
- case *vm.StructLogger:
+ case *logger.StructLogger:
return ðapi.ExecutionResult{
Gas: gas,
Failed: failed,
- ReturnValue: fmt.Sprintf("%x", ret),
+ ReturnValue: common.Bytes2Hex(ret),
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
- case *tracers.Tracer:
+ case tracers.Tracer:
return tracer.GetResult()
default:
@@ -653,19 +673,19 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
}
// computeTxEnv returns the execution environment of a certain transaction.
-func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) {
+func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, *tradingstate.TradingStateDB, error) {
// Create the parent state database
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
+ return nil, vm.Context{}, nil, nil, fmt.Errorf("block %x not found", blockHash)
}
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
+ return nil, vm.Context{}, nil, nil, fmt.Errorf("parent %x not found", block.ParentHash())
}
statedb, tomoxState, err := api.computeStateDB(parent, reexec)
if err != nil {
- return nil, vm.Context{}, nil, err
+ return nil, vm.Context{}, nil, nil, err
}
// Recompute transactions up to the target index.
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
@@ -689,14 +709,14 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
}
msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number())
if err != nil {
- return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
+ return nil, vm.Context{}, nil, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
}
context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
- return msg, context, statedb, nil
+ return msg, context, statedb, tomoxState, nil
}
_, gas, err, tokenFeeUsed := core.ApplyTransaction(api.config, feeCapacity, api.eth.blockchain, nil, gp, statedb, tomoxState, block.Header(), tx, usedGas, vm.Config{})
if err != nil {
- return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
+ return nil, vm.Context{}, nil, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
}
if tokenFeeUsed {
@@ -710,5 +730,5 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
}
}
statedb.DeleteSuicides()
- return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
+ return nil, vm.Context{}, nil, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
}
diff --git a/eth/tracers/tracer.go b/eth/tracers/js/bigint.go
similarity index 57%
rename from eth/tracers/tracer.go
rename to eth/tracers/js/bigint.go
index 5340ed43f7..9aeb330420 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/js/bigint.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2021 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
@@ -14,637 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package tracers
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "math/big"
- "sync/atomic"
- "time"
- "unsafe"
-
- "github.com/tomochain/tomochain/common"
- "github.com/tomochain/tomochain/common/hexutil"
- "github.com/tomochain/tomochain/core/vm"
- "github.com/tomochain/tomochain/crypto"
- "github.com/tomochain/tomochain/log"
- duktape "gopkg.in/olebedev/go-duktape.v3"
-)
+package js
// bigIntegerJS is the minified version of https://github.com/peterolson/BigInteger.js.
const bigIntegerJS = `var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt`
-
-// makeSlice convert an unsafe memory pointer with the given type into a Go byte
-// slice.
-//
-// Note, the returned slice uses the same memory area as the input arguments.
-// If those are duktape stack items, popping them off **will** make the slice
-// contents change.
-func makeSlice(ptr unsafe.Pointer, size uint) []byte {
- var sl = struct {
- addr uintptr
- len int
- cap int
- }{uintptr(ptr), int(size), int(size)}
-
- return *(*[]byte)(unsafe.Pointer(&sl))
-}
-
-// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
-func popSlice(ctx *duktape.Context) []byte {
- blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
- ctx.Pop()
- return blob
-}
-
-// pushBigInt create a JavaScript BigInteger in the VM.
-func pushBigInt(n *big.Int, ctx *duktape.Context) {
- ctx.GetGlobalString("bigInt")
- ctx.PushString(n.String())
- ctx.Call(1)
-}
-
-// opWrapper provides a JavaScript wrapper around OpCode.
-type opWrapper struct {
- op vm.OpCode
-}
-
-// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
-// onto the VM stack.
-func (ow *opWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
- vm.PutPropString(obj, "toNumber")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
- vm.PutPropString(obj, "toString")
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
- vm.PutPropString(obj, "isPush")
-}
-
-// memoryWrapper provides a JavaScript wrapper around vm.Memory.
-type memoryWrapper struct {
- memory *vm.Memory
-}
-
-// slice returns the requested range of memory as a byte slice.
-func (mw *memoryWrapper) slice(begin, end int64) []byte {
- if end == begin {
- return []byte{}
- }
- if end < begin || begin < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
- return nil
- }
- if mw.memory.Len() < int(end) {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
- return nil
- }
- return mw.memory.GetCopy(begin, end-begin)
-}
-
-// getUint returns the 32 bytes at the specified address interpreted as a uint.
-func (mw *memoryWrapper) getUint(addr int64) *big.Int {
- if mw.memory.Len() < int(addr)+32 || addr < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
- return new(big.Int)
- }
- return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
-}
-
-// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
-// onto the VM stack.
-func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Generate the `slice` method which takes two ints and returns a buffer
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
- ctx.Pop2()
-
- ptr := ctx.PushFixedBuffer(len(blob))
- copy(makeSlice(ptr, uint(len(blob))), blob)
- return 1
- })
- vm.PutPropString(obj, "slice")
-
- // Generate the `getUint` method which takes an int and returns a bigint
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- offset := int64(ctx.GetInt(-1))
- ctx.Pop()
-
- pushBigInt(mw.getUint(offset), ctx)
- return 1
- })
- vm.PutPropString(obj, "getUint")
-}
-
-// stackWrapper provides a JavaScript wrapper around vm.Stack.
-type stackWrapper struct {
- stack *vm.Stack
-}
-
-// peek returns the nth-from-the-top element of the stack.
-func (sw *stackWrapper) peek(idx int) *big.Int {
- if len(sw.stack.Data()) <= idx || idx < 0 {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
- return new(big.Int)
- }
- return sw.stack.Data()[len(sw.stack.Data())-idx-1]
-}
-
-// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
-// onto the VM stack.
-func (sw *stackWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
- vm.PutPropString(obj, "length")
-
- // Generate the `peek` method which takes an int and returns a bigint
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- offset := ctx.GetInt(-1)
- ctx.Pop()
-
- pushBigInt(sw.peek(offset), ctx)
- return 1
- })
- vm.PutPropString(obj, "peek")
-}
-
-// dbWrapper provides a JavaScript wrapper around vm.Database.
-type dbWrapper struct {
- db vm.StateDB
-}
-
-// pushObject assembles a JSVM object wrapping a swappable database and pushes it
-// onto the VM stack.
-func (dw *dbWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Push the wrapper for statedb.GetBalance
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
- return 1
- })
- vm.PutPropString(obj, "getBalance")
-
- // Push the wrapper for statedb.GetNonce
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
- return 1
- })
- vm.PutPropString(obj, "getNonce")
-
- // Push the wrapper for statedb.GetCode
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
-
- ptr := ctx.PushFixedBuffer(len(code))
- copy(makeSlice(ptr, uint(len(code))), code)
- return 1
- })
- vm.PutPropString(obj, "getCode")
-
- // Push the wrapper for statedb.GetState
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- hash := popSlice(ctx)
- addr := popSlice(ctx)
-
- state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
-
- ptr := ctx.PushFixedBuffer(len(state))
- copy(makeSlice(ptr, uint(len(state))), state[:])
- return 1
- })
- vm.PutPropString(obj, "getState")
-
- // Push the wrapper for statedb.Exists
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
- return 1
- })
- vm.PutPropString(obj, "exists")
-}
-
-// contractWrapper provides a JavaScript wrapper around vm.Contract
-type contractWrapper struct {
- contract *vm.Contract
-}
-
-// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
-// onto the VM stack.
-func (cw *contractWrapper) pushObject(vm *duktape.Context) {
- obj := vm.PushObject()
-
- // Push the wrapper for contract.Caller
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ptr := ctx.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
- return 1
- })
- vm.PutPropString(obj, "getCaller")
-
- // Push the wrapper for contract.Address
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- ptr := ctx.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
- return 1
- })
- vm.PutPropString(obj, "getAddress")
-
- // Push the wrapper for contract.Value
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- pushBigInt(cw.contract.Value(), ctx)
- return 1
- })
- vm.PutPropString(obj, "getValue")
-
- // Push the wrapper for contract.Input
- vm.PushGoFunction(func(ctx *duktape.Context) int {
- blob := cw.contract.Input
-
- ptr := ctx.PushFixedBuffer(len(blob))
- copy(makeSlice(ptr, uint(len(blob))), blob)
- return 1
- })
- vm.PutPropString(obj, "getInput")
-}
-
-// Tracer provides an implementation of Tracer that evaluates a Javascript
-// function for each VM execution step.
-type Tracer struct {
- inited bool // Flag whether the context was already inited from the EVM
-
- vm *duktape.Context // Javascript VM instance
-
- tracerObject int // Stack index of the tracer JavaScript object
- stateObject int // Stack index of the global state to pull arguments from
-
- opWrapper *opWrapper // Wrapper around the VM opcode
- stackWrapper *stackWrapper // Wrapper around the VM stack
- memoryWrapper *memoryWrapper // Wrapper around the VM memory
- contractWrapper *contractWrapper // Wrapper around the contract object
- dbWrapper *dbWrapper // Wrapper around the VM environment
-
- pcValue *uint // Swappable pc value wrapped by a log accessor
- gasValue *uint // Swappable gas value wrapped by a log accessor
- costValue *uint // Swappable cost value wrapped by a log accessor
- depthValue *uint // Swappable depth value wrapped by a log accessor
- errorValue *string // Swappable error value wrapped by a log accessor
- refundValue *uint // Swappable refund value wrapped by a log accessor
-
- ctx map[string]interface{} // Transaction context gathered throughout execution
- err error // Error, if one has occurred
-
- interrupt uint32 // Atomic flag to signal execution interruption
- reason error // Textual reason for the interruption
-}
-
-// New instantiates a new tracer instance. code specifies a Javascript snippet,
-// which must evaluate to an expression returning an object with 'step', 'fault'
-// and 'result' functions.
-func New(code string) (*Tracer, error) {
- // Resolve any tracers by name and assemble the tracer object
- if tracer, ok := tracer(code); ok {
- code = tracer
- }
- tracer := &Tracer{
- vm: duktape.New(),
- ctx: make(map[string]interface{}),
- opWrapper: new(opWrapper),
- stackWrapper: new(stackWrapper),
- memoryWrapper: new(memoryWrapper),
- contractWrapper: new(contractWrapper),
- dbWrapper: new(dbWrapper),
- pcValue: new(uint),
- gasValue: new(uint),
- costValue: new(uint),
- depthValue: new(uint),
- refundValue: new(uint),
- }
- // Set up builtins for this environment
- tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
- ctx.PushString(hexutil.Encode(popSlice(ctx)))
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
- var word common.Hash
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- word = common.BytesToHash(makeSlice(ptr, size))
- } else {
- word = common.HexToHash(ctx.GetString(-1))
- }
- ctx.Pop()
- copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
- var addr common.Address
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- addr = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- addr = common.HexToAddress(ctx.GetString(-1))
- }
- ctx.Pop()
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
- var from common.Address
- if ptr, size := ctx.GetBuffer(-2); ptr != nil {
- from = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- from = common.HexToAddress(ctx.GetString(-2))
- }
- nonce := uint64(ctx.GetInt(-1))
- ctx.Pop2()
-
- contract := crypto.CreateAddress(from, nonce)
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
- var from common.Address
- if ptr, size := ctx.GetBuffer(-3); ptr != nil {
- from = common.BytesToAddress(makeSlice(ptr, size))
- } else {
- from = common.HexToAddress(ctx.GetString(-3))
- }
- // Retrieve salt hex string from js stack
- salt := common.HexToHash(ctx.GetString(-2))
- // Retrieve code slice from js stack
- var code []byte
- if ptr, size := ctx.GetBuffer(-1); ptr != nil {
- code = common.CopyBytes(makeSlice(ptr, size))
- } else {
- code = common.FromHex(ctx.GetString(-1))
- }
- codeHash := crypto.Keccak256(code)
- ctx.Pop3()
- contract := crypto.CreateAddress2(from, salt, codeHash)
- copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
- return 1
- })
- tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
- _, ok := vm.PrecompiledContractsIstanbul[common.BytesToAddress(popSlice(ctx))]
- ctx.PushBoolean(ok)
- return 1
- })
- tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
- start, end := ctx.GetInt(-2), ctx.GetInt(-1)
- ctx.Pop2()
-
- blob := popSlice(ctx)
- size := end - start
-
- if start < 0 || start > end || end > len(blob) {
- // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
- // runtime goes belly up https://github.com/golang/go/issues/15639.
- log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
- ctx.PushFixedBuffer(0)
- return 1
- }
- copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
- return 1
- })
- // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
- if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
- log.Warn("Failed to compile tracer", "err", err)
- return nil, err
- }
- tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
-
- if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
- return nil, fmt.Errorf("trace object must expose a function step()")
- }
- tracer.vm.Pop()
-
- if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
- return nil, fmt.Errorf("trace object must expose a function fault()")
- }
- tracer.vm.Pop()
-
- if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
- return nil, fmt.Errorf("trace object must expose a function result()")
- }
- tracer.vm.Pop()
-
- // Tracer is valid, inject the big int library to access large numbers
- tracer.vm.EvalString(bigIntegerJS)
- tracer.vm.PutGlobalString("bigInt")
-
- // Push the global environment state as object #1 into the JSVM stack
- tracer.stateObject = tracer.vm.PushObject()
-
- logObject := tracer.vm.PushObject()
-
- tracer.opWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "op")
-
- tracer.stackWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "stack")
-
- tracer.memoryWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "memory")
-
- tracer.contractWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(logObject, "contract")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
- tracer.vm.PutPropString(logObject, "getPC")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
- tracer.vm.PutPropString(logObject, "getGas")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
- tracer.vm.PutPropString(logObject, "getCost")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
- tracer.vm.PutPropString(logObject, "getDepth")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
- tracer.vm.PutPropString(logObject, "getRefund")
-
- tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
- if tracer.errorValue != nil {
- ctx.PushString(*tracer.errorValue)
- } else {
- ctx.PushUndefined()
- }
- return 1
- })
- tracer.vm.PutPropString(logObject, "getError")
-
- tracer.vm.PutPropString(tracer.stateObject, "log")
-
- tracer.dbWrapper.pushObject(tracer.vm)
- tracer.vm.PutPropString(tracer.stateObject, "db")
-
- return tracer, nil
-}
-
-// Stop terminates execution of the tracer at the first opportune moment.
-func (jst *Tracer) Stop(err error) {
- jst.reason = err
- atomic.StoreUint32(&jst.interrupt, 1)
-}
-
-// call executes a method on a JS object, catching any errors, formatting and
-// returning them as error objects.
-func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error) {
- // Execute the JavaScript call and return any error
- jst.vm.PushString(method)
- for _, arg := range args {
- jst.vm.GetPropString(jst.stateObject, arg)
- }
- code := jst.vm.PcallProp(jst.tracerObject, len(args))
- defer jst.vm.Pop()
-
- if code != 0 {
- err := jst.vm.SafeToString(-1)
- return nil, errors.New(err)
- }
- // No error occurred, extract return value and return
- return json.RawMessage(jst.vm.JsonEncode(-1)), nil
-}
-
-func wrapError(context string, err error) error {
- return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
-}
-
-// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
- jst.ctx["type"] = "CALL"
- if create {
- jst.ctx["type"] = "CREATE"
- }
- jst.ctx["from"] = from
- jst.ctx["to"] = to
- jst.ctx["input"] = input
- jst.ctx["gas"] = gas
- jst.ctx["value"] = value
-
- return nil
-}
-
-// 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 *vm.Stack, contract *vm.Contract, depth int, err error) error {
- if jst.err == nil {
- // Initialize the context if it wasn't done yet
- if !jst.inited {
- jst.ctx["block"] = env.BlockNumber.Uint64()
- jst.inited = true
- }
- // If tracing was interrupted, set the error and stop
- if atomic.LoadUint32(&jst.interrupt) > 0 {
- jst.err = jst.reason
- return nil
- }
- jst.opWrapper.op = op
- jst.stackWrapper.stack = stack
- jst.memoryWrapper.memory = memory
- jst.contractWrapper.contract = contract
- jst.dbWrapper.db = env.StateDB
-
- *jst.pcValue = uint(pc)
- *jst.gasValue = uint(gas)
- *jst.costValue = uint(cost)
- *jst.depthValue = uint(depth)
- *jst.refundValue = uint(env.StateDB.GetRefund())
-
- jst.errorValue = nil
- if err != nil {
- jst.errorValue = new(string)
- *jst.errorValue = err.Error()
- }
- _, err := jst.call("step", "log", "db")
- if err != nil {
- jst.err = wrapError("step", err)
- }
- }
- return nil
-}
-
-// CaptureFault implements the Tracer interface to trace an execution fault
-// while running an opcode.
-func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
- if jst.err == nil {
- // Apart from the error, everything matches the previous invocation
- jst.errorValue = new(string)
- *jst.errorValue = err.Error()
-
- _, err := jst.call("fault", "log", "db")
- if err != nil {
- jst.err = wrapError("fault", err)
- }
- }
- return nil
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
- jst.ctx["output"] = output
- jst.ctx["gasUsed"] = gasUsed
- jst.ctx["time"] = t.String()
-
- if err != nil {
- jst.ctx["error"] = err.Error()
- }
- return nil
-}
-
-// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
-func (jst *Tracer) GetResult() (json.RawMessage, error) {
- // Transform the context into a JavaScript object and inject into the state
- obj := jst.vm.PushObject()
-
- for key, val := range jst.ctx {
- switch val := val.(type) {
- case uint64:
- jst.vm.PushUint(uint(val))
-
- case string:
- jst.vm.PushString(val)
-
- case []byte:
- ptr := jst.vm.PushFixedBuffer(len(val))
- copy(makeSlice(ptr, uint(len(val))), val)
-
- case common.Address:
- ptr := jst.vm.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), val[:])
-
- case *big.Int:
- pushBigInt(val, jst.vm)
-
- default:
- panic(fmt.Sprintf("unsupported type: %T", val))
- }
- jst.vm.PutPropString(obj, key)
- }
- jst.vm.PutPropString(jst.stateObject, "ctx")
-
- // Finalize the trace and return the results
- result, err := jst.call("result", "ctx", "db")
- if err != nil {
- jst.err = wrapError("result", err)
- }
- // Clean up the JavaScript environment
- jst.vm.DestroyHeap()
- jst.vm.Destroy()
-
- return result, jst.err
-}
diff --git a/eth/tracers/internal/tracers/4byte_tracer.js b/eth/tracers/js/internal/tracers/4byte_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/4byte_tracer.js
rename to eth/tracers/js/internal/tracers/4byte_tracer.js
diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/js/internal/tracers/assets.go
similarity index 100%
rename from eth/tracers/internal/tracers/assets.go
rename to eth/tracers/js/internal/tracers/assets.go
diff --git a/eth/tracers/internal/tracers/bigram_tracer.js b/eth/tracers/js/internal/tracers/bigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/bigram_tracer.js
rename to eth/tracers/js/internal/tracers/bigram_tracer.js
diff --git a/eth/tracers/internal/tracers/call_tracer.js b/eth/tracers/js/internal/tracers/call_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/call_tracer.js
rename to eth/tracers/js/internal/tracers/call_tracer.js
diff --git a/eth/tracers/internal/tracers/evmdis_tracer.js b/eth/tracers/js/internal/tracers/evmdis_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/evmdis_tracer.js
rename to eth/tracers/js/internal/tracers/evmdis_tracer.js
diff --git a/eth/tracers/internal/tracers/noop_tracer.js b/eth/tracers/js/internal/tracers/noop_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/noop_tracer.js
rename to eth/tracers/js/internal/tracers/noop_tracer.js
diff --git a/eth/tracers/internal/tracers/opcount_tracer.js b/eth/tracers/js/internal/tracers/opcount_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/opcount_tracer.js
rename to eth/tracers/js/internal/tracers/opcount_tracer.js
diff --git a/eth/tracers/internal/tracers/prestate_tracer.js b/eth/tracers/js/internal/tracers/prestate_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/prestate_tracer.js
rename to eth/tracers/js/internal/tracers/prestate_tracer.js
diff --git a/eth/tracers/internal/tracers/tracers.go b/eth/tracers/js/internal/tracers/tracers.go
similarity index 100%
rename from eth/tracers/internal/tracers/tracers.go
rename to eth/tracers/js/internal/tracers/tracers.go
diff --git a/eth/tracers/internal/tracers/trigram_tracer.js b/eth/tracers/js/internal/tracers/trigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/trigram_tracer.js
rename to eth/tracers/js/internal/tracers/trigram_tracer.js
diff --git a/eth/tracers/internal/tracers/unigram_tracer.js b/eth/tracers/js/internal/tracers/unigram_tracer.js
similarity index 100%
rename from eth/tracers/internal/tracers/unigram_tracer.js
rename to eth/tracers/js/internal/tracers/unigram_tracer.js
diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go
new file mode 100644
index 0000000000..6ee87f53ae
--- /dev/null
+++ b/eth/tracers/js/tracer.go
@@ -0,0 +1,883 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package js
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+ "sync/atomic"
+ "unicode"
+ "unsafe"
+
+ "github.com/tomochain/tomochain/common"
+ "github.com/tomochain/tomochain/common/hexutil"
+ "github.com/tomochain/tomochain/core"
+ "github.com/tomochain/tomochain/core/vm"
+ "github.com/tomochain/tomochain/crypto"
+ tracers2 "github.com/tomochain/tomochain/eth/tracers"
+ "github.com/tomochain/tomochain/eth/tracers/js/internal/tracers"
+ "github.com/tomochain/tomochain/log"
+
+ "gopkg.in/olebedev/go-duktape.v3"
+)
+
+// camel converts a snake cased input string into a camel cased output.
+func camel(str string) string {
+ pieces := strings.Split(str, "_")
+ for i := 1; i < len(pieces); i++ {
+ pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
+ }
+ return strings.Join(pieces, "")
+}
+
+var assetTracers = make(map[string]string)
+
+// init retrieves the JavaScript transaction tracers included in go-kardia.
+func init() {
+ for _, file := range tracers.AssetNames() {
+ name := camel(strings.TrimSuffix(file, ".js"))
+ assetTracers[name] = string(tracers.MustAsset(file))
+ }
+ tracers2.RegisterLookup(true, newJsTracer)
+}
+
+// makeSlice convert an unsafe memory pointer with the given type into a Go byte
+// slice.
+//
+// Note, the returned slice uses the same memory area as the input arguments.
+// If those are duktape stack items, popping them off **will** make the slice
+// contents change.
+func makeSlice(ptr unsafe.Pointer, size uint) []byte {
+ var sl = struct {
+ addr uintptr
+ len int
+ cap int
+ }{uintptr(ptr), int(size), int(size)}
+
+ return *(*[]byte)(unsafe.Pointer(&sl))
+}
+
+// popSlice pops a buffer off the JavaScript stack and returns it as a slice.
+func popSlice(ctx *duktape.Context) []byte {
+ blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
+ ctx.Pop()
+ return blob
+}
+
+// pushBigInt create a JavaScript BigInteger in the vm.
+func pushBigInt(n *big.Int, ctx *duktape.Context) {
+ ctx.GetGlobalString("bigInt")
+ ctx.PushString(n.String())
+ ctx.Call(1)
+}
+
+// opWrapper provides a JavaScript wrapper around OpCode.
+type opWrapper struct {
+ op vm.OpCode
+}
+
+// pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
+// onto the VM stack.
+func (ow *opWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
+ vm.PutPropString(obj, "toNumber")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
+ vm.PutPropString(obj, "toString")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
+ vm.PutPropString(obj, "isPush")
+}
+
+// memoryWrapper provides a JavaScript wrapper around vm.Memory.
+type memoryWrapper struct {
+ memory *vm.Memory
+}
+
+// slice returns the requested range of memory as a byte slice.
+func (mw *memoryWrapper) slice(begin, end int64) []byte {
+ if end == begin {
+ return []byte{}
+ }
+ if end < begin || begin < 0 {
+ // TODO: We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
+ return nil
+ }
+ if mw.memory.Len() < int(end) {
+ // TODO: We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
+ return nil
+ }
+ return mw.memory.GetCopy(begin, end-begin)
+}
+
+// getUint returns the 32 bytes at the specified address interpreted as a uint.
+func (mw *memoryWrapper) getUint(addr int64) *big.Int {
+ if mw.memory.Len() < int(addr)+32 || addr < 0 {
+ // TODO: We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
+ return new(big.Int)
+ }
+ return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
+}
+
+// pushObject assembles a JSVM object wrapping a swappable memory and pushes it
+// onto the VM stack.
+func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Generate the `slice` method which takes two ints and returns a buffer
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
+ ctx.Pop2()
+
+ ptr := ctx.PushFixedBuffer(len(blob))
+ copy(makeSlice(ptr, uint(len(blob))), blob)
+ return 1
+ })
+ vm.PutPropString(obj, "slice")
+
+ // Generate the `getUint` method which takes an int and returns a bigint
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ offset := int64(ctx.GetInt(-1))
+ ctx.Pop()
+
+ pushBigInt(mw.getUint(offset), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getUint")
+}
+
+// stackWrapper provides a JavaScript wrapper around vm.Stack.
+type stackWrapper struct {
+ stack *vm.Stack
+}
+
+// peek returns the nth-from-the-top element of the stack.
+func (sw *stackWrapper) peek(idx int) *big.Int {
+ if len(sw.stack.Data()) <= idx || idx < 0 {
+ // TODO: We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
+ return new(big.Int)
+ }
+ return sw.stack.Back(idx)
+}
+
+// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
+// onto the VM stack.
+func (sw *stackWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
+ vm.PutPropString(obj, "length")
+
+ // Generate the `peek` method which takes an int and returns a bigint
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ offset := ctx.GetInt(-1)
+ ctx.Pop()
+
+ pushBigInt(sw.peek(offset), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "peek")
+}
+
+// dbWrapper provides a JavaScript wrapper around vm.Database.
+type dbWrapper struct {
+ db vm.StateDB
+}
+
+// pushObject assembles a JSVM object wrapping a swappable database and pushes it
+// onto the VM stack.
+func (dw *dbWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Push the wrapper for statedb.GetBalance
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getBalance")
+
+ // Push the wrapper for statedb.GetNonce
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
+ return 1
+ })
+ vm.PutPropString(obj, "getNonce")
+
+ // Push the wrapper for statedb.GetCode
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
+
+ ptr := ctx.PushFixedBuffer(len(code))
+ copy(makeSlice(ptr, uint(len(code))), code)
+ return 1
+ })
+ vm.PutPropString(obj, "getCode")
+
+ // Push the wrapper for statedb.GetState
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ hash := popSlice(ctx)
+ addr := popSlice(ctx)
+
+ state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
+
+ ptr := ctx.PushFixedBuffer(len(state))
+ copy(makeSlice(ptr, uint(len(state))), state[:])
+ return 1
+ })
+ vm.PutPropString(obj, "getState")
+
+ // Push the wrapper for statedb.Exists
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
+ return 1
+ })
+ vm.PutPropString(obj, "exists")
+}
+
+// contractWrapper provides a JavaScript wrapper around vm.Contract
+type contractWrapper struct {
+ contract *vm.Contract
+}
+
+// pushObject assembles a JSVM object wrapping a swappable contract and pushes it
+// onto the VM stack.
+func (cw *contractWrapper) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ // Push the wrapper for contract.Caller
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
+ return 1
+ })
+ vm.PutPropString(obj, "getCaller")
+
+ // Push the wrapper for contract.Address
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
+ return 1
+ })
+ vm.PutPropString(obj, "getAddress")
+
+ // Push the wrapper for contract.Value
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ pushBigInt(cw.contract.Value(), ctx)
+ return 1
+ })
+ vm.PutPropString(obj, "getValue")
+
+ // Push the wrapper for contract.Input
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ blob := cw.contract.Input
+
+ ptr := ctx.PushFixedBuffer(len(blob))
+ copy(makeSlice(ptr, uint(len(blob))), blob)
+ return 1
+ })
+ vm.PutPropString(obj, "getInput")
+}
+
+type frame struct {
+ typ *string
+ from *common.Address
+ to *common.Address
+ input []byte
+ gas *uint
+ value *big.Int
+}
+
+func newFrame() *frame {
+ return &frame{
+ typ: new(string),
+ from: new(common.Address),
+ to: new(common.Address),
+ gas: new(uint),
+ }
+}
+
+func (f *frame) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
+ vm.PutPropString(obj, "getType")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
+ vm.PutPropString(obj, "getFrom")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
+ vm.PutPropString(obj, "getTo")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
+ vm.PutPropString(obj, "getInput")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
+ vm.PutPropString(obj, "getGas")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if f.value != nil {
+ pushValue(ctx, f.value)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ vm.PutPropString(obj, "getValue")
+}
+
+type frameResult struct {
+ gasUsed *uint
+ output []byte
+ errorValue *string
+}
+
+func newFrameResult() *frameResult {
+ return &frameResult{
+ gasUsed: new(uint),
+ }
+}
+
+func (r *frameResult) pushObject(vm *duktape.Context) {
+ obj := vm.PushObject()
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
+ vm.PutPropString(obj, "getGasUsed")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
+ vm.PutPropString(obj, "getOutput")
+
+ vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if r.errorValue != nil {
+ pushValue(ctx, *r.errorValue)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ vm.PutPropString(obj, "getError")
+}
+
+// jsTracer provides an implementation of Tracer that evaluates a Javascript
+// function for each VM execution step.
+type jsTracer struct {
+ vm *duktape.Context // Javascript VM instance
+ env *vm.EVM // EVM instance executing the code being traced
+
+ tracerObject int // Stack index of the tracer JavaScript object
+ stateObject int // Stack index of the global state to pull arguments from
+
+ opWrapper *opWrapper // Wrapper around the VM opcode
+ stackWrapper *stackWrapper // Wrapper around the VM stack
+ memoryWrapper *memoryWrapper // Wrapper around the VM memory
+ contractWrapper *contractWrapper // Wrapper around the contract object
+ dbWrapper *dbWrapper // Wrapper around the VM environment
+
+ pcValue *uint // Swappable pc value wrapped by a log accessor
+ gasValue *uint // Swappable gas value wrapped by a log accessor
+ costValue *uint // Swappable cost value wrapped by a log accessor
+ depthValue *uint // Swappable depth value wrapped by a log accessor
+ errorValue *string // Swappable error value wrapped by a log accessor
+ refundValue *uint // Swappable refund value wrapped by a log accessor
+
+ frame *frame // Represents entry into call frame. Fields are swappable
+ frameResult *frameResult // Represents exit from a call frame. Fields are swappable
+
+ ctx map[string]interface{} // Transaction context gathered throughout execution
+ err error // Error, if one has occurred
+
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+ traceSteps bool // When true, will invoke step() on each opcode
+ traceCallFrames bool // When true, will invoke enter() and exit() js funcs
+}
+
+// New instantiates a new tracer instance. code specifies a Javascript snippet,
+// which must evaluate to an expression returning an object with 'step', 'fault'
+// and 'result' functions.
+func newJsTracer(code string, ctx *tracers2.Context) (tracers2.Tracer, error) {
+ if c, ok := assetTracers[code]; ok {
+ code = c
+ }
+ if ctx == nil {
+ ctx = new(tracers2.Context)
+ }
+ tracer := &jsTracer{
+ vm: duktape.New(),
+ ctx: make(map[string]interface{}),
+ opWrapper: new(opWrapper),
+ stackWrapper: new(stackWrapper),
+ memoryWrapper: new(memoryWrapper),
+ contractWrapper: new(contractWrapper),
+ dbWrapper: new(dbWrapper),
+ pcValue: new(uint),
+ gasValue: new(uint),
+ costValue: new(uint),
+ depthValue: new(uint),
+ refundValue: new(uint),
+ frame: newFrame(),
+ frameResult: newFrameResult(),
+ }
+ if ctx.BlockHash != (common.Hash{}) {
+ tracer.ctx["blockHash"] = ctx.BlockHash
+
+ if ctx.TxHash != (common.Hash{}) {
+ tracer.ctx["txIndex"] = ctx.TxIndex
+ tracer.ctx["txHash"] = ctx.TxHash
+ }
+ }
+ // Set up builtins for this environment
+ tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
+ ctx.PushString(hexutil.Encode(popSlice(ctx)))
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
+ var word common.Hash
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ word = common.BytesToHash(makeSlice(ptr, size))
+ } else {
+ word = common.HexToHash(ctx.GetString(-1))
+ }
+ ctx.Pop()
+ copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
+ var addr common.Address
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ addr = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ addr = common.HexToAddress(ctx.GetString(-1))
+ }
+ ctx.Pop()
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
+ var from common.Address
+ if ptr, size := ctx.GetBuffer(-2); ptr != nil {
+ from = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ from = common.HexToAddress(ctx.GetString(-2))
+ }
+ nonce := uint64(ctx.GetInt(-1))
+ ctx.Pop2()
+
+ contract := crypto.CreateAddress(from, nonce)
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
+ var from common.Address
+ if ptr, size := ctx.GetBuffer(-3); ptr != nil {
+ from = common.BytesToAddress(makeSlice(ptr, size))
+ } else {
+ from = common.HexToAddress(ctx.GetString(-3))
+ }
+ // Retrieve salt hex string from js stack
+ salt := common.HexToHash(ctx.GetString(-2))
+ // Retrieve code slice from js stack
+ var code []byte
+ if ptr, size := ctx.GetBuffer(-1); ptr != nil {
+ code = common.CopyBytes(makeSlice(ptr, size))
+ } else {
+ code = common.FromHex(ctx.GetString(-1))
+ }
+ codeHash := crypto.Keccak256(code)
+ ctx.Pop3()
+ contract := crypto.CreateAddress2(from, salt, codeHash)
+ copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
+ addr := common.BytesToAddress(popSlice(ctx))
+ for _, p := range tracer.activePrecompiles {
+ if p == addr {
+ ctx.PushBoolean(true)
+ return 1
+ }
+ }
+ ctx.PushBoolean(false)
+ return 1
+ })
+ tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
+ start, end := ctx.GetInt(-2), ctx.GetInt(-1)
+ ctx.Pop2()
+
+ blob := popSlice(ctx)
+ size := end - start
+
+ if start < 0 || start > end || end > len(blob) {
+ // TODO: We can't js-throw from Go inside duktape inside Go. The Go
+ // runtime goes belly up https://github.com/golang/go/issues/15639.
+ log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
+ ctx.PushFixedBuffer(0)
+ return 1
+ }
+ copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
+ return 1
+ })
+ // Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
+ if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
+ log.Warn("Failed to compile tracer", "err", err)
+ return nil, err
+ }
+ tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
+
+ hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
+ tracer.vm.Pop()
+
+ if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
+ return nil, fmt.Errorf("trace object must expose a function fault()")
+ }
+ tracer.vm.Pop()
+
+ if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
+ return nil, fmt.Errorf("trace object must expose a function result()")
+ }
+ tracer.vm.Pop()
+
+ hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
+ tracer.vm.Pop()
+ hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
+ tracer.vm.Pop()
+ if hasEnter != hasExit {
+ return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
+ }
+ tracer.traceCallFrames = hasEnter && hasExit
+ tracer.traceSteps = hasStep
+
+ // Tracer is valid, inject the big int library to access large numbers
+ tracer.vm.EvalString(bigIntegerJS)
+ tracer.vm.PutGlobalString("bigInt")
+
+ // Push the global environment state as object #1 into the JSVM stack
+ tracer.stateObject = tracer.vm.PushObject()
+
+ logObject := tracer.vm.PushObject()
+
+ tracer.opWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "op")
+
+ tracer.stackWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "stack")
+
+ tracer.memoryWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "memory")
+
+ tracer.contractWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(logObject, "contract")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getPC")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getGas")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getCost")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getDepth")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
+ tracer.vm.PutPropString(logObject, "getRefund")
+
+ tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
+ if tracer.errorValue != nil {
+ ctx.PushString(*tracer.errorValue)
+ } else {
+ ctx.PushUndefined()
+ }
+ return 1
+ })
+ tracer.vm.PutPropString(logObject, "getError")
+
+ tracer.vm.PutPropString(tracer.stateObject, "log")
+
+ tracer.frame.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "frame")
+
+ tracer.frameResult.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "frameResult")
+
+ tracer.dbWrapper.pushObject(tracer.vm)
+ tracer.vm.PutPropString(tracer.stateObject, "db")
+
+ return tracer, nil
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (jst *jsTracer) Stop(err error) {
+ jst.reason = err
+ atomic.StoreUint32(&jst.interrupt, 1)
+}
+
+// call executes a method on a JS object, catching any errors, formatting and
+// returning them as error objects.
+func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
+ // Execute the JavaScript call and return any error
+ jst.vm.PushString(method)
+ for _, arg := range args {
+ jst.vm.GetPropString(jst.stateObject, arg)
+ }
+ code := jst.vm.PcallProp(jst.tracerObject, len(args))
+ defer jst.vm.Pop()
+
+ if code != 0 {
+ err := jst.vm.SafeToString(-1)
+ return nil, errors.New(err)
+ }
+ // No error occurred, extract return value and return
+ if noret {
+ return nil, nil
+ }
+ // Push a JSON marshaller onto the stack. We can't marshal from the out-
+ // side because duktape can crash on large nestings and we can't catch
+ // C++ exceptions ourselves from Go.
+ jst.vm.PushString("(JSON.stringify)")
+ jst.vm.Eval()
+
+ jst.vm.Swap(-1, -2)
+ if code = jst.vm.Pcall(1); code != 0 {
+ err := jst.vm.SafeToString(-1)
+ return nil, errors.New(err)
+ }
+ return json.RawMessage(jst.vm.SafeToString(-1)), nil
+}
+
+func wrapError(context string, err error) error {
+ return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
+}
+
+func (jst *jsTracer) CaptureTxStart(gasLimit uint64) {}
+func (jst *jsTracer) CaptureTxEnd(restGas uint64) {}
+
+// CaptureStart implements the Tracer interface to initialize the tracing operation.
+func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ jst.env = env
+ jst.ctx["type"] = "CALL"
+ if create {
+ jst.ctx["type"] = "CREATE"
+ }
+ jst.ctx["from"] = from
+ jst.ctx["to"] = to
+ jst.ctx["input"] = input
+ jst.ctx["gas"] = gas
+ jst.ctx["gasPrice"] = env.Context.GasPrice
+ jst.ctx["value"] = value
+
+ // Initialize the context
+ jst.ctx["block"] = env.Context.BlockNumber
+ jst.dbWrapper.db = env.StateDB
+ // Update list of precompiles based on current block
+ PrecompiledContractsIstanbulAddresses := make([]common.Address, 0, len(vm.PrecompiledContractsIstanbul))
+ for k := range vm.PrecompiledContractsIstanbul {
+ PrecompiledContractsIstanbulAddresses = append(PrecompiledContractsIstanbulAddresses, k)
+ }
+ jst.activePrecompiles = PrecompiledContractsIstanbulAddresses
+
+ // Compute intrinsic gas
+ isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
+ intrinsicGas, err := core.IntrinsicGas(input, jst.ctx["type"] == "CREATE", isHomestead)
+ if err != nil {
+ return
+ }
+ jst.ctx["intrinsicGas"] = intrinsicGas
+}
+
+// CaptureState implements the Tracer interface to trace a single step of VM execution.
+func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, rData []byte, depth int, err error) {
+ if !jst.traceSteps {
+ return
+ }
+ if jst.err != nil {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ jst.env.Cancel()
+ return
+ }
+ jst.opWrapper.op = op
+ jst.stackWrapper.stack = scope.Stack
+ jst.memoryWrapper.memory = scope.Memory
+ jst.contractWrapper.contract = scope.Contract
+
+ *jst.pcValue = uint(pc)
+ *jst.gasValue = uint(gas)
+ *jst.costValue = uint(cost)
+ *jst.depthValue = uint(depth)
+ *jst.refundValue = uint(jst.env.StateDB.GetRefund())
+
+ jst.errorValue = nil
+ if err != nil {
+ jst.errorValue = new(string)
+ *jst.errorValue = err.Error()
+ }
+
+ if _, err := jst.call(true, "step", "log", "db"); err != nil {
+ jst.err = wrapError("step", err)
+ }
+}
+
+// CaptureFault implements the Tracer interface to trace an execution fault
+func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, depth int, err error) {
+ if jst.err != nil {
+ return
+ }
+ // Apart from the error, everything matches the previous invocation
+ jst.errorValue = new(string)
+ *jst.errorValue = err.Error()
+
+ if _, err := jst.call(true, "fault", "log", "db"); err != nil {
+ jst.err = wrapError("fault", err)
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ jst.ctx["output"] = output
+ jst.ctx["gasUsed"] = gasUsed
+
+ if err != nil {
+ jst.ctx["error"] = err.Error()
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ if !jst.traceCallFrames {
+ return
+ }
+ if jst.err != nil {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ *jst.frame.typ = typ.String()
+ *jst.frame.from = from
+ *jst.frame.to = to
+ jst.frame.input = common.CopyBytes(input)
+ *jst.frame.gas = uint(gas)
+ jst.frame.value = nil
+ if value != nil {
+ jst.frame.value = new(big.Int).SetBytes(value.Bytes())
+ }
+
+ if _, err := jst.call(true, "enter", "frame"); err != nil {
+ jst.err = wrapError("enter", err)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ if !jst.traceCallFrames {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ jst.frameResult.output = common.CopyBytes(output)
+ *jst.frameResult.gasUsed = uint(gasUsed)
+ jst.frameResult.errorValue = nil
+ if err != nil {
+ jst.frameResult.errorValue = new(string)
+ *jst.frameResult.errorValue = err.Error()
+ }
+
+ if _, err := jst.call(true, "exit", "frameResult"); err != nil {
+ jst.err = wrapError("exit", err)
+ }
+}
+
+// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
+func (jst *jsTracer) GetResult() (json.RawMessage, error) {
+ // Transform the context into a JavaScript object and inject into the state
+ obj := jst.vm.PushObject()
+
+ for key, val := range jst.ctx {
+ jst.addToObj(obj, key, val)
+ }
+ jst.vm.PutPropString(jst.stateObject, "ctx")
+
+ // Finalize the trace and return the results
+ result, err := jst.call(false, "result", "ctx", "db")
+ if err != nil {
+ jst.err = wrapError("result", err)
+ }
+ // Clean up the JavaScript environment
+ jst.vm.DestroyHeap()
+ jst.vm.Destroy()
+
+ return result, jst.err
+}
+
+// addToObj pushes a field to a JS object.
+func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
+ pushValue(jst.vm, val)
+ jst.vm.PutPropString(obj, key)
+}
+
+func pushValue(ctx *duktape.Context, val interface{}) {
+ switch val := val.(type) {
+ case uint64:
+ ctx.PushUint(uint(val))
+ case string:
+ ctx.PushString(val)
+ case []byte:
+ ptr := ctx.PushFixedBuffer(len(val))
+ copy(makeSlice(ptr, uint(len(val))), val)
+ case common.Address:
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), val[:])
+ case *big.Int:
+ pushBigInt(val, ctx)
+ case int:
+ ctx.PushInt(val)
+ case uint:
+ ctx.PushUint(val)
+ case common.Hash:
+ ptr := ctx.PushFixedBuffer(32)
+ copy(makeSlice(ptr, 32), val[:])
+ default:
+ panic(fmt.Sprintf("unsupported type: %T", val))
+ }
+}
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
new file mode 100644
index 0000000000..b4de443e3d
--- /dev/null
+++ b/eth/tracers/js/tracer_test.go
@@ -0,0 +1,307 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package js
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/json"
+ "errors"
+ "math/big"
+ "testing"
+ "time"
+
+ "github.com/tomochain/tomochain/common"
+ "github.com/tomochain/tomochain/common/hexutil"
+ "github.com/tomochain/tomochain/core"
+ "github.com/tomochain/tomochain/core/rawdb"
+ "github.com/tomochain/tomochain/core/state"
+ "github.com/tomochain/tomochain/core/types"
+ "github.com/tomochain/tomochain/core/vm"
+ "github.com/tomochain/tomochain/crypto"
+ "github.com/tomochain/tomochain/eth/tracers"
+ "github.com/tomochain/tomochain/params"
+ "github.com/tomochain/tomochain/tests"
+)
+
+type account struct{}
+
+func (account) SubBalance(amount *big.Int) {}
+func (account) AddBalance(amount *big.Int) {}
+func (account) SetAddress(common.Address) {}
+func (account) Value() *big.Int { return nil }
+func (account) SetBalance(*big.Int) {}
+func (account) SetNonce(uint64) {}
+func (account) Balance() *big.Int { return nil }
+func (account) Address() common.Address { return common.Address{} }
+func (account) SetCode(common.Hash, []byte) {}
+func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
+
+type dummyStatedb struct {
+ state.StateDB
+}
+
+func (*dummyStatedb) GetRefund() uint64 { return 1337 }
+func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) }
+
+func testCtx() *vm.Context {
+ return &vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(100000)}
+}
+
+func runTrace(tracer tracers.Tracer, vmctx *vm.Context, chaincfg *params.ChainConfig) (json.RawMessage, error) {
+ var (
+ env = vm.NewEVM(*vmctx, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer})
+ startGas uint64 = 10000
+ value = big.NewInt(0)
+ contract = vm.NewContract(account{}, account{}, value, startGas)
+ )
+ contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
+
+ tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
+ ret, err := env.Interpreter().Run(contract, []byte{}, false)
+ tracer.CaptureEnd(ret, startGas-contract.Gas, err)
+ if err != nil {
+ return nil, err
+ }
+ return tracer.GetResult()
+}
+
+func TestTracer(t *testing.T) {
+ execTracer := func(code string) ([]byte, string) {
+ t.Helper()
+ tracer, err := newJsTracer(code, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ if err != nil {
+ return nil, err.Error() // Stringify to allow comparison without nil checks
+ }
+ return ret, ""
+ }
+ for i, tt := range []struct {
+ code string
+ want string
+ fail string
+ }{
+ { // tests that we don't panic on bad arguments to memory access
+ code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
+ want: `[{},{},{}]`,
+ }, { // tests that we don't panic on bad arguments to stack peeks
+ code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
+ want: `["0","0","0"]`,
+ }, { // tests that we don't panic on bad arguments to memory getUint
+ code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
+ want: `["0","0","0"]`,
+ }, { // tests some general counting
+ code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
+ want: `3`,
+ }, { // tests that depth is reported correctly
+ code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}",
+ want: `[0,1,2]`,
+ }, { // tests to-string of opcodes
+ code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}",
+ want: `["PUSH1","PUSH1","STOP"]`,
+ }, { // tests intrinsic gas
+ code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
+ want: `"100000.6.21000"`,
+ }, { // tests too deep object / serialization crash
+ code: "{step: function() {}, fault: function() {}, result: function() { var o={}; var x=o; for (var i=0; i<1000; i++){ o.foo={}; o=o.foo; } return x; }}",
+ fail: "RangeError: json encode recursion limit in server-side tracer function 'result'",
+ },
+ } {
+ if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err {
+ t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code)
+ }
+ }
+}
+
+func TestHalt(t *testing.T) {
+ t.Skip("duktape doesn't support abortion")
+ timeout := errors.New("stahp")
+ tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ go func() {
+ time.Sleep(1 * time.Second)
+ tracer.Stop(timeout)
+ }()
+ if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
+ t.Errorf("Expected timeout error, got %v", err)
+ }
+}
+
+func TestHaltBetweenSteps(t *testing.T) {
+ tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ scope := &vm.CallCtx{
+ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
+ }
+ tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
+ tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
+ timeout := errors.New("stahp")
+ tracer.Stop(timeout)
+ tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
+
+ if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
+ t.Errorf("Expected timeout error, got %v", err)
+ }
+}
+
+// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
+// in 'result'
+func TestNoStepExec(t *testing.T) {
+ execTracer := func(code string) []byte {
+ t.Helper()
+ tracer, err := newJsTracer(code, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(100)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
+ tracer.CaptureEnd(nil, 0, nil)
+ ret, err := tracer.GetResult()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return ret
+ }
+ for i, tt := range []struct {
+ code string
+ want string
+ }{
+ { // tests that we don't panic on accessing the db methods
+ code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx, db){ return db.getBalance(ctx.to)} }",
+ want: `"0"`,
+ },
+ } {
+ if have := execTracer(tt.code); tt.want != string(have) {
+ t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code)
+ }
+ }
+}
+
+func TestEnterExit(t *testing.T) {
+ // test that either both or none of enter() and exit() are defined
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context)); err == nil {
+ t.Fatal("tracer creation should've failed without exit() definition")
+ }
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context)); err != nil {
+ t.Fatal(err)
+ }
+ // test that the enter and exit method are correctly invoked and the values passed
+ tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context))
+ if err != nil {
+ t.Fatal(err)
+ }
+ scope := &vm.CallCtx{
+ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
+ }
+ tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
+ tracer.CaptureExit([]byte{}, 400, nil)
+
+ have, err := tracer.GetResult()
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := `{"enters":1,"exits":1,"enterGas":1000,"gasUsed":400}`
+ if string(have) != want {
+ t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
+ }
+}
+
+func TestPrestateTracerCreate2(t *testing.T) {
+ unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
+ new(big.Int), 5000000, big.NewInt(1), []byte{})
+
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ signer := types.HomesteadSigner{}
+ tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA)
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ /**
+ This comes from one of the test-vectors on the Skinny Create2 - EIP
+
+ address 0x00000000000000000000000000000000deadbeef
+ salt 0x00000000000000000000000000000000000000000000000000000000cafebabe
+ init_code 0xdeadbeef
+ gas (assuming no mem expansion): 32006
+ result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7
+ */
+ origin, _ := signer.Sender(tx)
+ context := vm.Context{
+ Origin: origin,
+ GasPrice: big.NewInt(1),
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: common.Address{},
+ BlockNumber: new(big.Int).SetUint64(8000000),
+ Time: new(big.Int).SetUint64(5),
+ GasLimit: uint64(6000000),
+ }
+ alloc := core.GenesisAlloc{}
+
+ // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
+ // the address
+ alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
+ Nonce: 1,
+ Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"),
+ Balance: big.NewInt(1),
+ }
+ alloc[origin] = core.GenesisAccount{
+ Nonce: 1,
+ Code: []byte{},
+ Balance: big.NewInt(500000000000000),
+ }
+ statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
+
+ // Create the tracer, the KVM environment and run it
+ tracer, err := newJsTracer("prestateTracer", new(tracers.Context))
+ if err != nil {
+ t.Fatalf("failed to create call tracer: %v", err)
+ }
+ evm := vm.NewEVM(context, statedb, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+
+ msg, err := tx.AsMessage(signer, nil, context.BlockNumber)
+ if err != nil {
+ t.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ if _, _, _, err = st.TransitionDb(common.Address{}); err != nil {
+ t.Fatalf("failed to execute transaction: %v", err)
+ }
+ // Retrieve the trace result and compare against the etalon
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatalf("failed to retrieve trace result: %v", err)
+ }
+ ret := make(map[string]interface{})
+ if err := json.Unmarshal(res, &ret); err != nil {
+ t.Fatalf("failed to unmarshal trace result: %v", err)
+ }
+ if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has {
+ t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result")
+ }
+}
diff --git a/core/vm/gen_structlog.go b/eth/tracers/logger/gen_structlog.go
similarity index 90%
rename from core/vm/gen_structlog.go
rename to eth/tracers/logger/gen_structlog.go
index f7d6581266..2f6146d8be 100644
--- a/core/vm/gen_structlog.go
+++ b/eth/tracers/logger/gen_structlog.go
@@ -1,11 +1,12 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-package vm
+package logger
import (
"encoding/json"
"math/big"
+ "github.com/tomochain/tomochain/core/vm"
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/common/hexutil"
"github.com/tomochain/tomochain/common/math"
@@ -17,10 +18,10 @@ var _ = (*structLogMarshaling)(nil)
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
Pc uint64 `json:"pc"`
- Op OpCode `json:"op"`
+ Op vm.OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
- Memory hexutil.Bytes `json:"memory"`
+ Memory hexutil.Bytes `json:"memory,omitempty"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
@@ -28,7 +29,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
OpName string `json:"opName"`
- ErrorString string `json:"error"`
+ ErrorString string `json:"error,omitempty"`
}
var enc StructLog
enc.Pc = s.Pc
@@ -56,7 +57,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
Pc *uint64 `json:"pc"`
- Op *OpCode `json:"op"`
+ Op *vm.OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
new file mode 100644
index 0000000000..2984f834be
--- /dev/null
+++ b/eth/tracers/logger/logger.go
@@ -0,0 +1,461 @@
+// Copyright 2021 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 logger
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "math/big"
+ "strings"
+ "sync/atomic"
+
+ "github.com/tomochain/tomochain/common"
+ "github.com/tomochain/tomochain/common/hexutil"
+ "github.com/tomochain/tomochain/common/math"
+ "github.com/tomochain/tomochain/core/types"
+ "github.com/tomochain/tomochain/core/vm"
+ "github.com/tomochain/tomochain/params"
+)
+
+// Storage represents a contract's storage.
+type Storage map[common.Hash]common.Hash
+
+// Copy duplicates the current storage.
+func (s Storage) Copy() Storage {
+ cpy := make(Storage, len(s))
+ for key, value := range s {
+ cpy[key] = value
+ }
+ return cpy
+}
+
+// Config are the configuration options for structured logger the EVM
+type Config struct {
+ EnableMemory bool // enable memory capture
+ DisableStack bool // disable stack capture
+ DisableStorage bool // disable storage capture
+ EnableReturnData bool // enable return data capture
+ Debug bool // print output during capture end
+ Limit int // maximum length of output, but zero means unlimited
+ // Chain overrides, can be used to execute a trace using future fork rules
+ Overrides *params.ChainConfig `json:"overrides,omitempty"`
+}
+
+//go:generate go run github.com/fjl/gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
+
+// 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 vm.OpCode `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Memory []byte `json:"memory,omitempty"`
+ MemorySize int `json:"memSize"`
+ Stack []*big.Int `json:"stack"`
+ ReturnData []byte `json:"returnData,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"-"`
+ Depth int `json:"depth"`
+ RefundCounter uint64 `json:"refund"`
+ Err error `json:"-"`
+}
+
+// overrides for gencodec
+type structLogMarshaling struct {
+ Gas math.HexOrDecimal64
+ GasCost math.HexOrDecimal64
+ Memory hexutil.Bytes
+ ReturnData hexutil.Bytes
+ OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
+ ErrorString string `json:"error,omitempty"` // 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()
+ }
+ return ""
+}
+
+// StructLogger is an EVM state logger and implements EVMLogger.
+//
+// StructLogger can capture state based on the given Log configuration and also keeps
+// a track record of modified storage which is used in reporting snapshots of the
+// contract their storage.
+type StructLogger struct {
+ cfg Config
+ env *vm.EVM
+
+ storage map[common.Address]Storage
+ logs []StructLog
+ output []byte
+ err error
+ gasLimit uint64
+ usedGas uint64
+
+ interrupt atomic.Bool // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+}
+
+// NewStructLogger returns a new logger
+func NewStructLogger(cfg *Config) *StructLogger {
+ logger := &StructLogger{
+ storage: make(map[common.Address]Storage),
+ }
+ if cfg != nil {
+ logger.cfg = *cfg
+ }
+ return logger
+}
+
+// Reset clears the data held by the logger.
+func (l *StructLogger) Reset() {
+ l.storage = make(map[common.Address]Storage)
+ l.output = make([]byte, 0)
+ l.logs = l.logs[:0]
+ l.err = nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ l.env = env
+}
+
+// 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(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, rData []byte, depth int, err error) {
+ // If tracing was interrupted, set the error and stop
+ if l.interrupt.Load() {
+ return
+ }
+ // check if already accumulated the specified number of logs
+ if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
+ return
+ }
+
+ memory := scope.Memory
+ stack := scope.Stack
+ contract := scope.Contract
+ // Copy a snapshot of the current memory state to a new buffer
+ var mem []byte
+ if l.cfg.EnableMemory {
+ mem = make([]byte, len(memory.Data()))
+ copy(mem, memory.Data())
+ }
+ // Copy a snapshot of the current stack state to a new buffer
+ var stck []*big.Int
+ if !l.cfg.DisableStack {
+ stck = make([]*big.Int, len(stack.Data()))
+ for i, item := range stack.Data() {
+ stck[i] = item
+ }
+ }
+ stackData := stack.Data()
+ stackLen := len(stackData)
+ // Copy a snapshot of the current storage to a new container
+ var storage Storage
+ if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) {
+ // initialise new changed values storage container for this contract
+ // if not present.
+ if l.storage[contract.Address()] == nil {
+ l.storage[contract.Address()] = make(Storage)
+ }
+ // capture SLOAD opcodes and record the read entry in the local storage
+ if op == vm.SLOAD && stackLen >= 1 {
+ var (
+ address = common.BigToHash(stackData[stackLen-1])
+ value = l.env.StateDB.GetState(contract.Address(), address)
+ )
+ l.storage[contract.Address()][address] = value
+ storage = l.storage[contract.Address()].Copy()
+ } else if op == vm.SSTORE && stackLen >= 2 {
+ // capture SSTORE opcodes and record the written entry in the local storage.
+ var (
+ value = common.BigToHash(stackData[stackLen-2])
+ address = common.BigToHash(stackData[stackLen-1])
+ )
+ l.storage[contract.Address()][address] = value
+ storage = l.storage[contract.Address()].Copy()
+ }
+ }
+ var rdata []byte
+ if l.cfg.EnableReturnData {
+ 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, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
+ l.logs = append(l.logs, log)
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault
+// while running an opcode.
+func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, depth int, err error) {
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ l.output = output
+ l.err = err
+ if l.cfg.Debug {
+ fmt.Printf("%#x\n", output)
+ if err != nil {
+ fmt.Printf(" error: %v\n", err)
+ }
+ }
+}
+
+func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
+}
+
+func (l *StructLogger) GetResult() (json.RawMessage, error) {
+ // Tracing aborted
+ if l.reason != nil {
+ return nil, l.reason
+ }
+ failed := l.err != nil
+ returnData := common.CopyBytes(l.output)
+ // Return data when successful and revert reason when reverted, otherwise empty.
+ returnVal := fmt.Sprintf("%x", returnData)
+ if failed && l.err != vm.ErrExecutionReverted {
+ returnVal = ""
+ }
+ return json.Marshal(&ExecutionResult{
+ Gas: l.usedGas,
+ Failed: failed,
+ ReturnValue: returnVal,
+ StructLogs: formatLogs(l.StructLogs()),
+ })
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (l *StructLogger) Stop(err error) {
+ l.reason = err
+ l.interrupt.Store(true)
+}
+
+func (l *StructLogger) CaptureTxStart(gasLimit uint64) {
+ l.gasLimit = gasLimit
+}
+
+func (l *StructLogger) CaptureTxEnd(restGas uint64) {
+ l.usedGas = l.gasLimit - restGas
+}
+
+// StructLogs returns the captured log entries.
+func (l *StructLogger) StructLogs() []StructLog { return l.logs }
+
+// Error returns the VM error captured by the trace.
+func (l *StructLogger) Error() error { return l.err }
+
+// Output returns the VM return value captured by the trace.
+func (l *StructLogger) Output() []byte { return l.output }
+
+// WriteTrace writes a formatted trace to the given writer
+func WriteTrace(writer io.Writer, logs []StructLog) {
+ for _, log := range logs {
+ fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
+ if log.Err != nil {
+ fmt.Fprintf(writer, " ERROR: %v", log.Err)
+ }
+ fmt.Fprintln(writer)
+
+ if len(log.Stack) > 0 {
+ fmt.Fprintln(writer, "Stack:")
+ for i := len(log.Stack) - 1; i >= 0; i-- {
+ fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, common.BigToHash(log.Stack[i]).Hex())
+ }
+ }
+ if len(log.Memory) > 0 {
+ fmt.Fprintln(writer, "Memory:")
+ fmt.Fprint(writer, hex.Dump(log.Memory))
+ }
+ if len(log.Storage) > 0 {
+ fmt.Fprintln(writer, "Storage:")
+ for h, item := range log.Storage {
+ 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)
+ }
+}
+
+// WriteLogs writes vm logs in a readable format to the given writer
+func WriteLogs(writer io.Writer, logs []*types.Log) {
+ for _, log := range logs {
+ fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
+
+ for i, topic := range log.Topics {
+ fmt.Fprintf(writer, "%08d %x\n", i, topic)
+ }
+
+ fmt.Fprint(writer, hex.Dump(log.Data))
+ fmt.Fprintln(writer)
+ }
+}
+
+type mdLogger struct {
+ out io.Writer
+ cfg *Config
+ env *vm.EVM
+}
+
+// NewMarkdownLogger creates a logger which outputs information in a format adapted
+// for human readability, and is also a valid markdown table
+func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
+ l := &mdLogger{out: writer, cfg: cfg}
+ if l.cfg == nil {
+ l.cfg = &Config{}
+ }
+ return l
+}
+
+func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.env = env
+ if !create {
+ fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ } else {
+ fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ }
+
+ fmt.Fprintf(t.out, `
+| Pc | Op | Cost | Stack | RStack | Refund |
+|-------|-------------|------|-----------|-----------|---------|
+`)
+}
+
+// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
+func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, rData []byte, depth int, err error) {
+ stack := scope.Stack
+ fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
+
+ if !t.cfg.DisableStack {
+ // format stack
+ var a []string
+ for _, elem := range stack.Data() {
+ a = append(a, common.BigToHash(elem).Hex())
+ }
+ b := fmt.Sprintf("[%v]", strings.Join(a, ","))
+ fmt.Fprintf(t.out, "%10v |", b)
+ }
+ fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund())
+ fmt.Fprintln(t.out, "")
+ if err != nil {
+ fmt.Fprintf(t.out, "Error: %v\n", err)
+ }
+}
+
+func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, depth int, err error) {
+ fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
+}
+
+func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
+ output, gasUsed, err)
+}
+
+func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
+
+func (*mdLogger) CaptureTxStart(gasLimit uint64) {}
+
+func (*mdLogger) CaptureTxEnd(restGas uint64) {}
+
+// ExecutionResult groups all structured logs emitted by the EVM
+// while replaying a transaction in debug mode as well as transaction
+// execution status, the amount of gas used and the return value
+type ExecutionResult struct {
+ Gas uint64 `json:"gas"`
+ Failed bool `json:"failed"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []StructLogRes `json:"structLogs"`
+}
+
+// StructLogRes stores a structured log emitted by the EVM while replaying a
+// transaction in debug mode
+type StructLogRes struct {
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Depth int `json:"depth"`
+ Error string `json:"error,omitempty"`
+ Stack *[]string `json:"stack,omitempty"`
+ Memory *[]string `json:"memory,omitempty"`
+ Storage *map[string]string `json:"storage,omitempty"`
+ RefundCounter uint64 `json:"refund,omitempty"`
+}
+
+// formatLogs formats EVM returned structured logs for json output
+func formatLogs(logs []StructLog) []StructLogRes {
+ formatted := make([]StructLogRes, len(logs))
+ for index, trace := range logs {
+ formatted[index] = StructLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Depth: trace.Depth,
+ Error: trace.ErrorString(),
+ RefundCounter: trace.RefundCounter,
+ }
+ if trace.Stack != nil {
+ stack := make([]string, len(trace.Stack))
+ for i, stackValue := range trace.Stack {
+ stack[i] = common.BigToHash(stackValue).Hex()
+ }
+ formatted[index].Stack = &stack
+ }
+ if trace.Memory != nil {
+ memory := make([]string, 0, (len(trace.Memory)+31)/32)
+ for i := 0; i+32 <= len(trace.Memory); i += 32 {
+ memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ }
+ formatted[index].Memory = &memory
+ }
+ if trace.Storage != nil {
+ storage := make(map[string]string)
+ for i, storageValue := range trace.Storage {
+ storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ }
+ formatted[index].Storage = &storage
+ }
+ }
+ return formatted
+}
diff --git a/core/vm/logger_json.go b/eth/tracers/logger/logger_json.go
similarity index 52%
rename from core/vm/logger_json.go
rename to eth/tracers/logger/logger_json.go
index fdcc04d288..cab87e036c 100644
--- a/core/vm/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2021 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
@@ -14,74 +14,89 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package vm
+package logger
import (
"encoding/json"
"io"
"math/big"
- "time"
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/common/math"
+ "github.com/tomochain/tomochain/core/vm"
)
type JSONLogger struct {
encoder *json.Encoder
- cfg *LogConfig
+ cfg *Config
+ env *vm.EVM
}
// 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}
+func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
+ l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg}
if l.cfg == nil {
- l.cfg = &LogConfig{}
+ l.cfg = &Config{}
}
return l
}
-func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
- return nil
+func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ l.env = env
+}
+
+func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.CallCtx, depth int, err error) {
+ // TODO: Add rData to this interface as well
+ l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
}
// 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 {
+func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.CallCtx, rData []byte, depth int, err error) {
+ memory := scope.Memory
+ stack := scope.Stack
+
log := StructLog{
Pc: pc,
Op: op,
Gas: gas,
GasCost: cost,
MemorySize: memory.Len(),
- Storage: nil,
Depth: depth,
- RefundCounter: env.StateDB.GetRefund(),
+ RefundCounter: l.env.StateDB.GetRefund(),
Err: err,
}
- if !l.cfg.DisableMemory {
+ if l.cfg.EnableMemory {
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
+ if l.cfg.EnableReturnData {
+ log.ReturnData = rData
+ }
+ l.encoder.Encode(log)
}
// CaptureEnd is triggered at end of execution.
-func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
+func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
- Time time.Duration `json:"time"`
Err string `json:"error,omitempty"`
}
+ var errMsg string
if err != nil {
- return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
+ errMsg = err.Error()
}
- return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
+ l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
}
+
+func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
+
+func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
+
+func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}
diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go
new file mode 100644
index 0000000000..1daa744b74
--- /dev/null
+++ b/eth/tracers/logger/logger_test.go
@@ -0,0 +1,107 @@
+// Copyright 2021 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 logger
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+ "testing"
+
+ "github.com/tomochain/tomochain/common"
+ "github.com/tomochain/tomochain/core/state"
+ "github.com/tomochain/tomochain/core/vm"
+ "github.com/tomochain/tomochain/params"
+)
+
+type dummyContractRef struct {
+ calledForEach bool
+}
+
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
+ d.calledForEach = true
+}
+func (d *dummyContractRef) SubBalance(amount *big.Int) {}
+func (d *dummyContractRef) AddBalance(amount *big.Int) {}
+func (d *dummyContractRef) SetBalance(*big.Int) {}
+func (d *dummyContractRef) SetNonce(uint64) {}
+func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
+
+type dummyStatedb struct {
+ state.StateDB
+}
+
+func (*dummyStatedb) GetRefund() uint64 { return 1337 }
+func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} }
+func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {}
+
+func TestStoreCapture(t *testing.T) {
+ var (
+ logger = NewStructLogger(nil)
+ env = vm.NewEVM(vm.Context{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: logger})
+ contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
+ )
+ contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
+ var index common.Hash
+ logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
+ _, err := env.Interpreter().Run(contract, []byte{}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(logger.storage[contract.Address()]) == 0 {
+ t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(),
+ len(logger.storage[contract.Address()]))
+ }
+ exp := common.BigToHash(big.NewInt(1))
+ if logger.storage[contract.Address()][index] != exp {
+ t.Errorf("expected %x, got %x", exp, logger.storage[contract.Address()][index])
+ }
+}
+
+// Tests that blank fields don't appear in logs when JSON marshalled, to reduce
+// logs bloat and confusion. See https://github.com/tomochain/tomochain/issues/24487
+func TestStructLogMarshalingOmitEmpty(t *testing.T) {
+ tests := []struct {
+ name string
+ log *StructLog
+ want string
+ }{
+ {"empty err and no fields", &StructLog{},
+ `{"pc":0,"op":0,"gas":"0x0","gasCost":"0x0","memSize":0,"stack":null,"depth":0,"refund":0,"opName":"STOP"}`},
+ {"with err", &StructLog{Err: errors.New("this failed")},
+ `{"pc":0,"op":0,"gas":"0x0","gasCost":"0x0","memSize":0,"stack":null,"depth":0,"refund":0,"opName":"STOP","error":"this failed"}`},
+ {"with mem", &StructLog{Memory: make([]byte, 2), MemorySize: 2},
+ `{"pc":0,"op":0,"gas":"0x0","gasCost":"0x0","memory":"0x0000","memSize":2,"stack":null,"depth":0,"refund":0,"opName":"STOP"}`},
+ {"with 0-size mem", &StructLog{Memory: make([]byte, 0)},
+ `{"pc":0,"op":0,"gas":"0x0","gasCost":"0x0","memSize":0,"stack":null,"depth":0,"refund":0,"opName":"STOP"}`},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ blob, err := json.Marshal(tt.log)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have, want := string(blob), tt.want; have != want {
+ t.Fatalf("mismatched results\n\thave: %v\n\twant: %v", have, want)
+ }
+ })
+ }
+}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
deleted file mode 100644
index 8f0b05acac..0000000000
--- a/eth/tracers/tracer_test.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package tracers
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "math/big"
- "testing"
- "time"
-
- "github.com/tomochain/tomochain/common"
- "github.com/tomochain/tomochain/core/state"
- "github.com/tomochain/tomochain/core/vm"
- "github.com/tomochain/tomochain/params"
-)
-
-type account struct{}
-
-func (account) SubBalance(amount *big.Int) {}
-func (account) AddBalance(amount *big.Int) {}
-func (account) SetAddress(common.Address) {}
-func (account) Value() *big.Int { return nil }
-func (account) SetBalance(*big.Int) {}
-func (account) SetNonce(uint64) {}
-func (account) Balance() *big.Int { return nil }
-func (account) Address() common.Address { return common.Address{} }
-func (account) ReturnGas(*big.Int) {}
-func (account) SetCode(common.Hash, []byte) {}
-func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
-
-type dummyStatedb struct {
- state.StateDB
-}
-
-func (*dummyStatedb) GetRefund() uint64 { return 1337 }
-
-func runTrace(tracer *Tracer) (json.RawMessage, error) {
- env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
-
- contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
- contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
-
- _, err := env.Interpreter().Run(contract, []byte{}, false)
- if err != nil {
- return nil, err
- }
- return tracer.GetResult()
-}
-
-// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory access
-func TestRegressionPanicSlice(t *testing.T) {
- tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}")
- if err != nil {
- t.Fatal(err)
- }
- if _, err = runTrace(tracer); err != nil {
- t.Fatal(err)
- }
-}
-
-// TestRegressionPanicSlice tests that we don't panic on bad arguments to stack peeks
-func TestRegressionPanicPeek(t *testing.T) {
- tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}")
- if err != nil {
- t.Fatal(err)
- }
- if _, err = runTrace(tracer); err != nil {
- t.Fatal(err)
- }
-}
-
-// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory getUint
-func TestRegressionPanicGetUint(t *testing.T) {
- tracer, err := New("{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}")
- if err != nil {
- t.Fatal(err)
- }
- if _, err = runTrace(tracer); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestTracing(t *testing.T) {
- tracer, err := New("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}")
- if err != nil {
- t.Fatal(err)
- }
-
- ret, err := runTrace(tracer)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(ret, []byte("3")) {
- t.Errorf("Expected return value to be 3, got %s", string(ret))
- }
-}
-
-func TestStack(t *testing.T) {
- tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}")
- if err != nil {
- t.Fatal(err)
- }
-
- ret, err := runTrace(tracer)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(ret, []byte("[0,1,2]")) {
- t.Errorf("Expected return value to be [0,1,2], got %s", string(ret))
- }
-}
-
-func TestOpcodes(t *testing.T) {
- tracer, err := New("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}")
- if err != nil {
- t.Fatal(err)
- }
-
- ret, err := runTrace(tracer)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(ret, []byte("[\"PUSH1\",\"PUSH1\",\"STOP\"]")) {
- t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret))
- }
-}
-
-func TestHalt(t *testing.T) {
- t.Skip("duktape doesn't support abortion")
-
- timeout := errors.New("stahp")
- tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}")
- if err != nil {
- t.Fatal(err)
- }
-
- go func() {
- time.Sleep(1 * time.Second)
- tracer.Stop(timeout)
- }()
-
- if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" {
- t.Errorf("Expected timeout error, got %v", err)
- }
-}
-
-func TestHaltBetweenSteps(t *testing.T) {
- tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}")
- if err != nil {
- t.Fatal(err)
- }
- env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
-
- tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
- timeout := errors.New("stahp")
- tracer.Stop(timeout)
- tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
-
- if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
- t.Errorf("Expected timeout error, got %v", err)
- }
-}
diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go
index e22bf6f29e..c3a64d0ffb 100644
--- a/eth/tracers/tracers.go
+++ b/eth/tracers/tracers.go
@@ -18,36 +18,58 @@
package tracers
import (
- "strings"
- "unicode"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
- "github.com/tomochain/tomochain/eth/tracers/internal/tracers"
+ "github.com/tomochain/tomochain/common"
+ "github.com/tomochain/tomochain/core/vm"
)
-// all contains all the built in JavaScript tracers by name.
-var all = make(map[string]string)
+// Context contains some contextual infos for a transaction execution that is not
+// available from within the EVM object.
+type Context struct {
+ BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
+ BlockNumber *big.Int
+ TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
+ TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
+}
-// camel converts a snake cased input string into a camel cased output.
-func camel(str string) string {
- pieces := strings.Split(str, "_")
- for i := 1; i < len(pieces); i++ {
- pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
- }
- return strings.Join(pieces, "")
+// Tracer interface extends vm.EVMLogger and additionally
+// allows collecting the tracing result.
+type Tracer interface {
+ vm.EVMLogger
+ GetResult() (json.RawMessage, error)
+ // Stop terminates execution of the tracer at the first opportune moment.
+ Stop(err error)
}
-// init retrieves the JavaScript transaction tracers included in go-ethereum.
-func init() {
- for _, file := range tracers.AssetNames() {
- name := camel(strings.TrimSuffix(file, ".js"))
- all[name] = string(tracers.MustAsset(file))
+type lookupFunc func(string, *Context) (Tracer, error)
+
+var (
+ lookups []lookupFunc
+)
+
+// RegisterLookup registers a method as a lookup for tracers, meaning that
+// users can invoke a named tracer through that lookup. If 'wildcard' is true,
+// then the lookup will be placed last. This is typically meant for interpreted
+// engines (js) which can evaluate dynamic user-supplied code.
+func RegisterLookup(wildcard bool, lookup lookupFunc) {
+ if wildcard {
+ lookups = append(lookups, lookup)
+ } else {
+ lookups = append([]lookupFunc{lookup}, lookups...)
}
}
-// tracer retrieves a specific JavaScript tracer by name.
-func tracer(name string) (string, bool) {
- if tracer, ok := all[name]; ok {
- return tracer, true
+// New returns a new instance of a tracer, by iterating through the
+// registered lookups.
+func New(code string, ctx *Context) (Tracer, error) {
+ for _, lookup := range lookups {
+ if tracer, err := lookup(code, ctx); err == nil {
+ return tracer, nil
+ }
}
- return "", false
+ return nil, errors.New(fmt.Sprintf("tracer not found: %v", code))
}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 38d4075175..26d2ec943a 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -17,78 +17,21 @@
package tracers
import (
- "crypto/ecdsa"
- "crypto/rand"
- "encoding/json"
- "github.com/tomochain/tomochain/core/rawdb"
- "io/ioutil"
"math/big"
- "path/filepath"
- "reflect"
- "strings"
"testing"
+
"github.com/tomochain/tomochain/common"
"github.com/tomochain/tomochain/common/hexutil"
- "github.com/tomochain/tomochain/common/math"
"github.com/tomochain/tomochain/core"
+ "github.com/tomochain/tomochain/core/rawdb"
"github.com/tomochain/tomochain/core/types"
"github.com/tomochain/tomochain/core/vm"
"github.com/tomochain/tomochain/crypto"
+ "github.com/tomochain/tomochain/eth/tracers/logger"
"github.com/tomochain/tomochain/params"
- "github.com/tomochain/tomochain/rlp"
"github.com/tomochain/tomochain/tests"
)
-// To generate a new callTracer test, copy paste the makeTest method below into
-// a Geth console and call it with a transaction hash you which to export.
-
-/*
-// makeTest generates a callTracer test by running a prestate reassembled and a
-// call trace run, assembling all the gathered information into a test case.
-var makeTest = function(tx, rewind) {
- // Generate the genesis block from the block, transaction and prestate data
- var block = eth.getBlock(eth.getTransaction(tx).blockHash);
- var genesis = eth.getBlock(block.parentHash);
-
- delete genesis.gasUsed;
- delete genesis.logsBloom;
- delete genesis.parentHash;
- delete genesis.receiptsRoot;
- delete genesis.sha3Uncles;
- delete genesis.size;
- delete genesis.transactions;
- delete genesis.transactionsRoot;
- delete genesis.uncles;
-
- genesis.gasLimit = genesis.gasLimit.toString();
- genesis.number = genesis.number.toString();
- genesis.timestamp = genesis.timestamp.toString();
-
- genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
- for (var key in genesis.alloc) {
- genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
- }
- genesis.config = admin.nodeInfo.protocols.eth.config;
-
- // Generate the call trace and produce the test input
- var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
- delete result.time;
-
- console.log(JSON.stringify({
- genesis: genesis,
- context: {
- number: block.number.toString(),
- difficulty: block.difficulty,
- timestamp: block.timestamp.toString(),
- gasLimit: block.gasLimit.toString(),
- miner: block.miner,
- },
- input: eth.getRawTransaction(tx),
- result: result,
- }, null, 2));
-}
-*/
-
// callTrace is the result of a callTracer run.
type callTrace struct {
Type string `json:"type"`
@@ -103,177 +46,71 @@ type callTrace struct {
Calls []callTrace `json:"calls,omitempty"`
}
-type callContext struct {
- Number math.HexOrDecimal64 `json:"number"`
- Difficulty *math.HexOrDecimal256 `json:"difficulty"`
- Time math.HexOrDecimal64 `json:"timestamp"`
- GasLimit math.HexOrDecimal64 `json:"gasLimit"`
- Miner common.Address `json:"miner"`
-}
-
-// callTracerTest defines a single test to check the call tracer against.
-type callTracerTest struct {
- Genesis *core.Genesis `json:"genesis"`
- Context *callContext `json:"context"`
- Input string `json:"input"`
- Result *callTrace `json:"result"`
-}
-
-func TestPrestateTracerCreate2(t *testing.T) {
- common.TIPTomoXCancellationFee = big.NewInt(10000000000000)
- unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
- new(big.Int), 5000000, big.NewInt(1), []byte{})
-
- privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
- if err != nil {
- t.Fatalf("err %v", err)
- }
- signer := types.NewEIP155Signer(big.NewInt(1))
- tx, err := types.SignTx(unsignedTx, signer, privateKeyECDSA)
+func BenchmarkTransactionTrace(b *testing.B) {
+ key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ from := crypto.PubkeyToAddress(key.PublicKey)
+ gas := uint64(1000000) // 1M gas
+ to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ signer := types.HomesteadSigner{}
+ tx, err := types.SignTx(types.NewTransaction(1, to, big.NewInt(0), gas, big.NewInt(500), nil), signer, key)
if err != nil {
- t.Fatalf("err %v", err)
+ b.Fatal(err)
}
- /**
- This comes from one of the test-vectors on the Skinny Create2 - EIP
-
- address 0x00000000000000000000000000000000deadbeef
- salt 0x00000000000000000000000000000000000000000000000000000000cafebabe
- init_code 0xdeadbeef
- gas (assuming no mem expansion): 32006
- result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7
- */
- origin, _ := signer.Sender(tx)
context := vm.Context{
+ Origin: from,
+ GasPrice: tx.GasPrice(),
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
- Origin: origin,
Coinbase: common.Address{},
- BlockNumber: new(big.Int).SetUint64(8000000),
- Time: new(big.Int).SetUint64(5),
- Difficulty: big.NewInt(0x30000),
- GasLimit: uint64(6000000),
- GasPrice: big.NewInt(1),
+ BlockNumber: new(big.Int).SetUint64(uint64(5)),
+ Time: new(big.Int).SetUint64(uint64(5)),
+ GasLimit: gas,
}
- alloc := core.GenesisAlloc{}
-
+ alloc := make(map[common.Address]core.GenesisAccount)
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
// the address
+ loop := []byte{
+ byte(vm.JUMPDEST), // [ count ]
+ byte(vm.PUSH1), 0, // jumpdestination
+ byte(vm.JUMP),
+ }
alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
Nonce: 1,
- Code: hexutil.MustDecode("0x63deadbeef60005263cafebabe6004601c6000F560005260206000F3"),
+ Code: loop,
Balance: big.NewInt(1),
}
- alloc[origin] = core.GenesisAccount{
+ alloc[from] = core.GenesisAccount{
Nonce: 1,
Code: []byte{},
Balance: big.NewInt(500000000000000),
}
- db := rawdb.NewMemoryDatabase()
- statedb := tests.MakePreState(db, alloc)
-
+ statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
// Create the tracer, the EVM environment and run it
- tracer, err := New("prestateTracer")
- if err != nil {
- t.Fatalf("failed to create call tracer: %v", err)
- }
- evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
-
- msg, err := tx.AsMessage(signer, nil, nil)
- if err != nil {
- t.Fatalf("failed to prepare transaction for tracing: %v", err)
- }
- st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err = st.TransitionDb(common.Address{}); err != nil {
- t.Fatalf("failed to execute transaction: %v", err)
- }
- // Retrieve the trace result and compare against the etalon
- res, err := tracer.GetResult()
- if err != nil {
- t.Fatalf("failed to retrieve trace result: %v", err)
- }
- ret := make(map[string]interface{})
- if err := json.Unmarshal(res, &ret); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
- }
- if _, has := ret["0x60f3f640a8508fc6a86d45df051962668e1e8ac7"]; !has {
- t.Fatalf("Expected 0x60f3f640a8508fc6a86d45df051962668e1e8ac7 in result")
- }
-}
-
-// Iterates over all the input-output datasets in the tracer test harness and
-// runs the JavaScript tracers against them.
-func TestCallTracer(t *testing.T) {
- files, err := ioutil.ReadDir("testdata")
+ tracer := logger.NewStructLogger(&logger.Config{
+ Debug: false,
+ //DisableStorage: true,
+ //EnableMemory: false,
+ //EnableReturnData: false,
+ })
+ evm := vm.NewEVM(context, statedb, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil, context.BlockNumber)
if err != nil {
- t.Fatalf("failed to retrieve tracer test suite: %v", err)
+ b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
- for _, file := range files {
- if !strings.HasPrefix(file.Name(), "call_tracer_") {
- continue
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ snap := statedb.Snapshot()
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ _, _, _, err = st.TransitionDb(common.Address{})
+ if err != nil {
+ b.Fatal(err)
}
- file := file // capture range variable
- t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
- t.Parallel()
-
- // Call tracer test found, read if from disk
- blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
- if err != nil {
- t.Fatalf("failed to read testcase: %v", err)
- }
- test := new(callTracerTest)
- if err := json.Unmarshal(blob, test); err != nil {
- t.Fatalf("failed to parse testcase: %v", err)
- }
- // Configure a blockchain with the given prestate
- tx := new(types.Transaction)
- if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
- t.Fatalf("failed to parse testcase input: %v", err)
- }
- signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
- origin, _ := signer.Sender(tx)
-
- context := vm.Context{
- CanTransfer: core.CanTransfer,
- Transfer: core.Transfer,
- Origin: origin,
- Coinbase: test.Context.Miner,
- BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
- Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
- Difficulty: (*big.Int)(test.Context.Difficulty),
- GasLimit: uint64(test.Context.GasLimit),
- GasPrice: tx.GasPrice(),
- }
- db := rawdb.NewMemoryDatabase()
- statedb := tests.MakePreState(db, test.Genesis.Alloc)
-
- // Create the tracer, the EVM environment and run it
- tracer, err := New("callTracer")
- if err != nil {
- t.Fatalf("failed to create call tracer: %v", err)
- }
- evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
-
- msg, err := tx.AsMessage(signer, nil, common.Big0)
- if err != nil {
- t.Fatalf("failed to prepare transaction for tracing: %v", err)
- }
- st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err = st.TransitionDb(common.Address{}); err != nil {
- t.Fatalf("failed to execute transaction: %v", err)
- }
- // Retrieve the trace result and compare against the etalon
- res, err := tracer.GetResult()
- if err != nil {
- t.Fatalf("failed to retrieve trace result: %v", err)
- }
- ret := new(callTrace)
- if err := json.Unmarshal(res, ret); err != nil {
- t.Fatalf("failed to unmarshal trace result: %v", err)
- }
-
- if !reflect.DeepEqual(ret, test.Result) {
- t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result)
- }
- })
+ statedb.RevertToSnapshot(snap)
+ if have, want := len(tracer.StructLogs()), 244752; have != want {
+ b.Fatalf("trace wrong, want %d steps, have %d", want, have)
+ }
+ tracer.Reset()
}
}
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 33376a1071..8874700480 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -21,14 +21,11 @@ import (
"context"
"errors"
"fmt"
- "github.com/tomochain/tomochain/tomoxlending/lendingstate"
"math/big"
"sort"
"strings"
"time"
- "github.com/tomochain/tomochain/tomox/tradingstate"
-
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
"github.com/tomochain/tomochain/accounts"
@@ -45,11 +42,14 @@ import (
"github.com/tomochain/tomochain/core/types"
"github.com/tomochain/tomochain/core/vm"
"github.com/tomochain/tomochain/crypto"
+ "github.com/tomochain/tomochain/eth/tracers/logger"
"github.com/tomochain/tomochain/log"
"github.com/tomochain/tomochain/p2p"
"github.com/tomochain/tomochain/params"
"github.com/tomochain/tomochain/rlp"
"github.com/tomochain/tomochain/rpc"
+ "github.com/tomochain/tomochain/tomox/tradingstate"
+ "github.com/tomochain/tomochain/tomoxlending/lendingstate"
)
const (
@@ -424,7 +424,8 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs
// safely used to calculate a signature from.
//
// The hash is calulcated as
-// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func signHash(data []byte) []byte {
@@ -1182,7 +1183,7 @@ type StructLogRes struct {
}
// formatLogs formats EVM returned structured logs for json output
-func FormatLogs(logs []vm.StructLog) []StructLogRes {
+func FormatLogs(logs []logger.StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
formatted[index] = StructLogRes{
@@ -1305,8 +1306,8 @@ func (s *PublicBlockChainAPI) findNearestSignedBlock(ctx context.Context, b *typ
}
/*
- findFinalityOfBlock return finality of a block
- Use blocksHashCache for to keep track - refer core/blockchain.go for more detail
+findFinalityOfBlock return finality of a block
+Use blocksHashCache for to keep track - refer core/blockchain.go for more detail
*/
func (s *PublicBlockChainAPI) findFinalityOfBlock(ctx context.Context, b *types.Block, masternodes []common.Address) (uint, error) {
engine, _ := s.b.GetEngine().(*posv.Posv)
@@ -1371,7 +1372,7 @@ func (s *PublicBlockChainAPI) findFinalityOfBlock(ctx context.Context, b *types.
}
/*
- Extract signers from block
+Extract signers from block
*/
func (s *PublicBlockChainAPI) getSigners(ctx context.Context, block *types.Block, engine *posv.Posv) ([]common.Address, error) {
var err error
@@ -2965,7 +2966,8 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash,
// GetStakerROI Estimate ROI for stakers using the last epoc reward
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0
// Formular:
-// ROI = average_latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
+//
+// ROI = average_latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
func (s *PublicBlockChainAPI) GetStakerROI() float64 {
blockNumber := s.b.CurrentBlock().Number().Uint64()
lastCheckpointNumber := blockNumber - (blockNumber % s.b.ChainConfig().Posv.Epoch) - s.b.ChainConfig().Posv.Epoch // calculate for 2 epochs ago
@@ -2991,7 +2993,8 @@ func (s *PublicBlockChainAPI) GetStakerROI() float64 {
// GetStakerROIMasternode Estimate ROI for stakers of a specific masternode using the last epoc reward
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0
// Formular:
-// ROI = latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
+//
+// ROI = latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address) float64 {
votersReward := s.b.GetVotersRewards(masternode)
if votersReward == nil {