diff --git a/core/state_transition.go b/core/state_transition.go index e71b98075..636c90636 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -39,8 +39,10 @@ The state transitioning model does all the necessary work to work out a valid ne 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 @@ -213,13 +215,13 @@ func (st *StateTransition) preCheck() error { // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. @@ -238,6 +240,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if err := st.preCheck(); err != nil { return nil, err } + + if st.evm.VmConfig.Debug { + st.evm.VmConfig.Tracer.CaptureTxStart(st.initialGas) + defer func() { + st.evm.VmConfig.Tracer.CaptureTxEnd(st.gas) + }() + } + msg := st.msg sender := vm.AccountRef(msg.From()) homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber) diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go index d7993b4f7..3f79d6d5a 100644 --- a/core/vm/access_list_tracer.go +++ b/core/vm/access_list_tracer.go @@ -180,3 +180,12 @@ func (a *AccessListTracer) AccessList() types.AccessList { func (a *AccessListTracer) Equal(other *AccessListTracer) bool { return a.list.equal(other.list) } + +// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// transaction processing. +func (t *AccessListTracer) CaptureTxStart(gasLimit uint64) { +} + +// CaptureTxStart implements the Tracer interface and is invoked at the end of +// transaction processing. +func (t *AccessListTracer) CaptureTxEnd(restGas uint64) {} diff --git a/core/vm/evm.go b/core/vm/evm.go index 1fcb6e9f5..06cf98e08 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -134,7 +134,7 @@ type EVM struct { chainRules params.Rules // virtual machine configuration options used to initialise the // evm. - vmConfig Config + VmConfig Config // global (to this context) ethereum virtual machine // used throughout the execution of the tx. interpreters []Interpreter @@ -155,7 +155,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig evm.Context = blockCtx evm.TxContext = txCtx evm.StateDB = statedb - evm.vmConfig = vmConfig + evm.VmConfig = vmConfig evm.chainConfig = chainConfig evm.chainRules = chainConfig.Rules(blockCtx.BlockNumber) evm.interpreters = make([]Interpreter, 0, 1) @@ -215,7 +215,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.VmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -232,13 +232,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer - if evm.vmConfig.Debug { + if evm.VmConfig.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) - evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) + evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.VmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) } else { - evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - evm.vmConfig.Tracer.CaptureExit(ret, 0, nil) + evm.VmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.VmConfig.Tracer.CaptureExit(ret, 0, nil) } } return nil, gas, nil @@ -248,17 +248,17 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode - if evm.vmConfig.Debug { + if evm.VmConfig.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) + evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters - evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) + evm.VmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err) }(gas, time.Now()) } else { // Handle tracer events for entering and exiting a call frame - evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + evm.VmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } } @@ -304,7 +304,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.VmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -321,10 +321,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) + if evm.VmConfig.Debug { + evm.VmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -355,7 +355,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.VmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -365,10 +365,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil) + if evm.VmConfig.Debug { + evm.VmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -397,7 +397,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.VmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit @@ -418,10 +418,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.AddBalance(addr, big0) // Invoke tracer hooks that signal entering/exiting a call frame - if evm.vmConfig.Debug { - evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) + if evm.VmConfig.Debug { + evm.VmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err) + evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -498,15 +498,15 @@ 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.VmConfig.NoRecursion && evm.depth > 0 { return nil, address, gas, nil } - if evm.vmConfig.Debug { + if evm.VmConfig.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) + evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) } else { - evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) + evm.VmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) } } @@ -546,11 +546,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } } - if evm.vmConfig.Debug { + if evm.VmConfig.Debug { if evm.depth == 0 { - evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + evm.VmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } else { - evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err) + evm.VmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err) } } return ret, address, contract.Gas, err diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0ecf28d59..68571ebd4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -244,7 +244,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt interpreter.hasher.Read(interpreter.hasherBuf[:]) evm := interpreter.evm - if evm.vmConfig.EnablePreimageRecording { + if evm.VmConfig.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } @@ -390,16 +390,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // 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, @@ -408,10 +413,12 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) // 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, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() diff --git a/core/vm/logger.go b/core/vm/logger.go index f8f08e726..131f83213 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -105,6 +105,10 @@ func (s *StructLog) ErrorString() string { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type EVMLogger interface { + // Transaction level + CaptureTxStart(gasLimit uint64) + CaptureTxEnd(restGas uint64) + CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) @@ -147,6 +151,10 @@ func (l *StructLogger) Reset() { l.err = nil } +func (*StructLogger) CaptureTxStart(gasLimit uint64) {} + +func (*StructLogger) CaptureTxEnd(restGas uint64) {} + // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { l.env = env @@ -307,6 +315,15 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { return l } +// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// transaction processing. +func (t *mdLogger) CaptureTxStart(gasLimit uint64) { +} + +// CaptureTxStart implements the Tracer interface and is invoked at the end of +// transaction processing. +func (t *mdLogger) CaptureTxEnd(restGas uint64) {} + func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { t.env = env if !create { diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index c249dba7e..8c4fbf3dc 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -42,6 +42,15 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { return l } +// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// transaction processing. +func (t *JSONLogger) CaptureTxStart(gasLimit uint64) { +} + +// CaptureTxStart implements the Tracer interface and is invoked at the end of +// transaction processing. +func (t *JSONLogger) CaptureTxEnd(restGas uint64) {} + func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { l.env = env } diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go index 0909add00..399aeb8dd 100644 --- a/eth/tracers/js/tracer.go +++ b/eth/tracers/js/tracer.go @@ -679,6 +679,15 @@ func wrapError(context string, err error) error { return fmt.Errorf("%v in server-side tracer function '%v'", err, context) } +// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// transaction processing. +func (jst *jsTracer) CaptureTxStart(gasLimit uint64) { +} + +// CaptureTxStart implements the Tracer interface and is invoked at the end of +// transaction processing. +func (t *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 diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index a27994e56..0804db9a0 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -63,6 +63,15 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e return t, nil } +// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// transaction processing. +func (t *callTracer) CaptureTxStart(gasLimit uint64) { +} + +// CaptureTxStart implements the Tracer interface and is invoked at the end of +// transaction processing. +func (t *callTracer) CaptureTxEnd(restGas uint64) {} + // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { t.env = env diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index f9f221562..65ccd7017 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -39,6 +39,12 @@ func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e return &noopTracer{}, nil } +func (l *noopTracer) CaptureTxStart(gasLimit uint64) { +} + +func (l *noopTracer) CaptureTxEnd(restGas uint64) { +} + // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index d06fd810f..fe4866e26 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -33,7 +33,7 @@ func init() { register("stateDiff", newStateDiffTracer) } -type state = map[common.Address]*account +type state = map[common.Address]account type account struct { Balance string `json:"balance,omitempty"` Nonce uint64 `json:"nonce,omitempty"` @@ -50,14 +50,15 @@ type stateDiffTracer struct { env *vm.EVM pre state post state - to common.Address create bool - config stateDiffTracerConfig + to common.Address gasLimit uint64 // Amount of gas bought for the whole tx interrupt uint32 // Atomic flag to signal execution interruption reason error // Textual reason for the interruption - created map[common.Address]bool - deleted map[common.Address]bool + + config stateDiffTracerConfig + created map[common.Address]bool + deleted map[common.Address]bool } type stateDiffTracerConfig struct { @@ -117,9 +118,6 @@ func (t *stateDiffTracer) CaptureStart(env *vm.EVM, from common.Address, to comm // CaptureEnd is called after the call finishes to finalize the tracing. func (t *stateDiffTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { - if err != nil { - return - } if t.config.DiffMode { return } @@ -135,9 +133,6 @@ func (t *stateDiffTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Durat // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *stateDiffTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - if err != nil { - return - } stack := scope.Stack stackData := stack.Data() stackLen := len(stackData) @@ -204,6 +199,7 @@ func (t *stateDiffTracer) CaptureTxEnd(restGas uint64) { postAccount := account{storage: make(map[common.Hash]common.Hash)} newBalance := bigToHex(t.env.StateDB.GetBalance(addr)) newNonce := t.env.StateDB.GetNonce(addr) + //newCode := t.env.StateDB.GetCode(addr) if newBalance != t.pre[addr].Balance { modified = true @@ -215,7 +211,7 @@ func (t *stateDiffTracer) CaptureTxEnd(restGas uint64) { } if modified { - t.post[addr] = &postAccount + t.post[addr] = postAccount } else { // if state is not modified, then no need to include into the pre state delete(t.pre, addr) @@ -262,7 +258,7 @@ func (t *stateDiffTracer) lookupAccount(addr common.Address) { return } - t.pre[addr] = &account{ + t.pre[addr] = account{ Balance: bigToHex(t.env.StateDB.GetBalance(addr)), Nonce: t.env.StateDB.GetNonce(addr), storage: make(map[common.Hash]common.Hash),