mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
change: port js tracer changes (#3128)
* change: expose callCtx as ScopeContext * change: update tracer API * change: logger API update: jst tracer * add: port jst tracer 2 * add: port jst tracer 3 * add: access list tracer * chore: lint
This commit is contained in:
parent
37f0dcecc1
commit
499c27d2e1
@ -22,7 +22,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/types/accounts"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/ethdb"
|
||||
"github.com/ledgerwatch/erigon/rpc"
|
||||
"github.com/ledgerwatch/erigon/turbo/rpchelper"
|
||||
@ -240,7 +239,7 @@ type OeTracer struct {
|
||||
idx []string // Prefix for the "idx" inside operations, for easier navigation
|
||||
}
|
||||
|
||||
func (ot *OeTracer) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
func (ot *OeTracer) CaptureStart(env *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
//fmt.Printf("CaptureStart depth %d, from %x, to %x, create %t, input %x, gas %d, value %d, precompile %t\n", depth, from, to, create, input, gas, value, precompile)
|
||||
if ot.r.VmTrace != nil {
|
||||
var vmTrace *VmTrace
|
||||
@ -273,7 +272,7 @@ func (ot *OeTracer) CaptureStart(depth int, from common.Address, to common.Addre
|
||||
}
|
||||
if precompile && depth > 0 && value.Sign() <= 0 {
|
||||
ot.precompile = true
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if gas > 500000000 {
|
||||
gas = 500000001 - (0x8000000000000000 - gas)
|
||||
@ -336,10 +335,9 @@ func (ot *OeTracer) CaptureStart(depth int, from common.Address, to common.Addre
|
||||
}
|
||||
ot.r.Trace = append(ot.r.Trace, trace)
|
||||
ot.traceStack = append(ot.traceStack, trace)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *OeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
func (ot *OeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
if ot.r.VmTrace != nil {
|
||||
if len(ot.vmOpStack) > 0 {
|
||||
ot.lastOffStack = ot.vmOpStack[len(ot.vmOpStack)-1]
|
||||
@ -357,7 +355,7 @@ func (ot *OeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64
|
||||
}
|
||||
if ot.precompile {
|
||||
ot.precompile = false
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if depth == 0 {
|
||||
ot.r.Output = common.CopyBytes(output)
|
||||
@ -410,10 +408,12 @@ func (ot *OeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64
|
||||
if depth > 0 {
|
||||
ot.traceAddr = ot.traceAddr[:len(ot.traceAddr)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *OeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, st *stack.Stack, rData []byte, contract *vm.Contract, opDepth int, err error) error {
|
||||
func (ot *OeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, opDepth int, err error) {
|
||||
memory := scope.Memory
|
||||
st := scope.Stack
|
||||
|
||||
if ot.r.VmTrace != nil {
|
||||
var vmTrace *VmTrace
|
||||
if len(ot.vmOpStack) > 0 {
|
||||
@ -471,7 +471,7 @@ func (ot *OeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
|
||||
}
|
||||
if ot.lastOp == vm.STOP && op == vm.STOP && len(ot.vmOpStack) == 0 {
|
||||
// Looks like OE is "optimising away" the second STOP
|
||||
return nil
|
||||
return
|
||||
}
|
||||
ot.lastVmOp = &VmTraceOp{Ex: &VmTraceEx{}}
|
||||
vmTrace.Ops = append(vmTrace.Ops, ot.lastVmOp)
|
||||
@ -517,11 +517,9 @@ func (ot *OeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
|
||||
ot.lastVmOp.Ex = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *OeTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, opDepth int, err error) error {
|
||||
return nil
|
||||
func (ot *OeTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, opDepth int, err error) {
|
||||
}
|
||||
|
||||
func (ot *OeTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/core/state"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/ethdb"
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
@ -155,7 +154,7 @@ type blockTxs struct {
|
||||
Txs slicePtrTx
|
||||
}
|
||||
|
||||
func (ot *opcodeTracer) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
func (ot *opcodeTracer) CaptureStart(env *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
//fmt.Fprint(ot.summary, ot.lastLine)
|
||||
|
||||
// When a CaptureStart is called, a Tx is starting. Create its entry in our list and initialize it with the partial data available
|
||||
@ -181,11 +180,9 @@ func (ot *opcodeTracer) CaptureStart(depth int, from common.Address, to common.A
|
||||
|
||||
// take note in our own stack that the tx stack has grown
|
||||
ot.stack = append(ot.stack, &newTx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *opcodeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
func (ot *opcodeTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
// When a CaptureEnd is called, a Tx has finished. Pop our stack
|
||||
ls := len(ot.stack)
|
||||
currentEntry := ot.stack[ls-1]
|
||||
@ -216,11 +213,11 @@ func (ot *opcodeTracer) CaptureEnd(depth int, output []byte, startGas, endGas ui
|
||||
currentEntry.Fault = errstr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *opcodeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, st *stack.Stack, rData []byte, contract *vm.Contract, opDepth int, err error) error {
|
||||
func (ot *opcodeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, opDepth int, err error) {
|
||||
//CaptureState sees the system as it is before the opcode is run. It seems to never get an error.
|
||||
contract := scope.Contract
|
||||
|
||||
//sanity check
|
||||
if pc > uint64(MaxUint16) {
|
||||
@ -350,16 +347,14 @@ func (ot *opcodeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas,
|
||||
|
||||
currentEntry.lastPc16 = pc16
|
||||
currentEntry.lastOp = op
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ot *opcodeTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, opDepth int, err error) error {
|
||||
func (ot *opcodeTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, opDepth int, err error) {
|
||||
// CaptureFault sees the system as it is after the fault happens
|
||||
|
||||
// CaptureState might have already recorded the opcode before it failed. Let's centralize the processing there.
|
||||
e := ot.CaptureState(env, pc, op, gas, cost, memory, stack, nil, contract, opDepth, err)
|
||||
ot.CaptureState(env, pc, op, gas, cost, scope, nil, opDepth, err)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (ot *opcodeTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
|
191
core/vm/access_list_tracer.go
Normal file
191
core/vm/access_list_tracer.go
Normal file
@ -0,0 +1,191 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||
// contract execution touches.
|
||||
type accessList map[common.Address]accessListSlots
|
||||
|
||||
// accessListSlots is an accumulator for the set of storage slots within a single
|
||||
// contract that an EVM contract execution touches.
|
||||
type accessListSlots map[common.Hash]struct{}
|
||||
|
||||
// newAccessList creates a new accessList.
|
||||
func newAccessList() accessList {
|
||||
return make(map[common.Address]accessListSlots)
|
||||
}
|
||||
|
||||
// addAddress adds an address to the accesslist.
|
||||
func (al accessList) addAddress(address common.Address) {
|
||||
// Set address if not previously present
|
||||
if _, present := al[address]; !present {
|
||||
al[address] = make(map[common.Hash]struct{})
|
||||
}
|
||||
}
|
||||
|
||||
// addSlot adds a storage slot to the accesslist.
|
||||
func (al accessList) addSlot(address common.Address, slot common.Hash) {
|
||||
// Set address if not previously present
|
||||
al.addAddress(address)
|
||||
|
||||
// Set the slot on the surely existent storage set
|
||||
al[address][slot] = struct{}{}
|
||||
}
|
||||
|
||||
// equal checks if the content of the current access list is the same as the
|
||||
// content of the other one.
|
||||
func (al accessList) equal(other accessList) bool {
|
||||
// Cross reference the accounts first
|
||||
if len(al) != len(other) {
|
||||
return false
|
||||
}
|
||||
for addr := range al {
|
||||
if _, ok := other[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for addr := range other {
|
||||
if _, ok := al[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Accounts match, cross reference the storage slots too
|
||||
for addr, slots := range al {
|
||||
otherslots := other[addr]
|
||||
|
||||
if len(slots) != len(otherslots) {
|
||||
return false
|
||||
}
|
||||
for hash := range slots {
|
||||
if _, ok := otherslots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for hash := range otherslots {
|
||||
if _, ok := slots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// accesslist converts the accesslist to a types.AccessList.
|
||||
func (al accessList) accessList() types.AccessList {
|
||||
acl := make(types.AccessList, 0, len(al))
|
||||
for addr, slots := range al {
|
||||
tuple := types.AccessTuple{Address: addr}
|
||||
for slot := range slots {
|
||||
tuple.StorageKeys = append(tuple.StorageKeys, slot)
|
||||
}
|
||||
acl = append(acl, tuple)
|
||||
}
|
||||
return acl
|
||||
}
|
||||
|
||||
var _ Tracer = (*AccessListTracer)(nil)
|
||||
|
||||
// AccessListTracer is a tracer that accumulates touched accounts and storage
|
||||
// slots into an internal set.
|
||||
type AccessListTracer struct {
|
||||
excl map[common.Address]struct{} // Set of account to exclude from the list
|
||||
list accessList // Set of accounts and storage slots touched
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureAccountWrite(account common.Address) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// NewAccessListTracer creates a new tracer that can generate AccessLists.
|
||||
// An optional AccessList can be specified to occupy slots and addresses in
|
||||
// the resulting accesslist.
|
||||
func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
|
||||
excl := map[common.Address]struct{}{
|
||||
from: {}, to: {},
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
excl[addr] = struct{}{}
|
||||
}
|
||||
list := newAccessList()
|
||||
for _, al := range acl {
|
||||
if _, ok := excl[al.Address]; !ok {
|
||||
list.addAddress(al.Address)
|
||||
}
|
||||
for _, slot := range al.StorageKeys {
|
||||
list.addSlot(al.Address, slot)
|
||||
}
|
||||
}
|
||||
return &AccessListTracer{
|
||||
excl: excl,
|
||||
list: list,
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||
func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
if (op == SLOAD || op == SSTORE) && stack.Len() >= 1 {
|
||||
slot := common.Hash(stack.Data[stack.Len()-1].Bytes32())
|
||||
a.list.addSlot(scope.Contract.Address(), slot)
|
||||
}
|
||||
if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.Len() >= 1 {
|
||||
addr := common.Address(stack.Data[stack.Len()-1].Bytes20())
|
||||
if _, ok := a.excl[addr]; !ok {
|
||||
a.list.addAddress(addr)
|
||||
}
|
||||
}
|
||||
if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.Len() >= 5 {
|
||||
addr := common.Address(stack.Data[stack.Len()-2].Bytes20())
|
||||
if _, ok := a.excl[addr]; !ok {
|
||||
a.list.addAddress(addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureStart(env *EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, callType CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureAccountRead(account common.Address) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AccessList returns the current accesslist maintained by the tracer.
|
||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||
return a.list.accessList()
|
||||
}
|
||||
|
||||
// Equal returns if the content of two access list traces are equal.
|
||||
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
|
||||
return a.list.equal(other.list)
|
||||
}
|
@ -79,9 +79,9 @@ func enable1884(jt *JumpTable) {
|
||||
}
|
||||
}
|
||||
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
balance := interpreter.evm.IntraBlockState().GetBalance(callContext.contract.Address())
|
||||
callContext.stack.Push(balance)
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
balance := interpreter.evm.IntraBlockState().GetBalance(callContext.Contract.Address())
|
||||
callContext.Stack.Push(balance)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -98,9 +98,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 *ScopeContext) ([]byte, error) {
|
||||
chainId, _ := uint256.FromBig(interpreter.evm.ChainRules().ChainID)
|
||||
callContext.stack.Push(chainId)
|
||||
callContext.Stack.Push(chainId)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -166,8 +166,8 @@ func enable3198(jt *JumpTable) {
|
||||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
baseFee := interpreter.evm.Context().BaseFee
|
||||
callContext.stack.Push(baseFee)
|
||||
callContext.Stack.Push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -229,9 +229,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
}
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.config.Debug {
|
||||
_ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig(), code)
|
||||
evm.config.Tracer.CaptureStart(evm, evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig(), code)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
}
|
||||
|
||||
@ -315,9 +315,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
}
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.config.Debug {
|
||||
_ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLCODET, input, gas, value.ToBig(), code)
|
||||
evm.config.Tracer.CaptureStart(evm, evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLCODET, input, gas, value.ToBig(), code)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
}
|
||||
var (
|
||||
@ -372,9 +372,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
}
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.config.Debug {
|
||||
_ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, DELEGATECALLT, input, gas, big.NewInt(-1), code)
|
||||
evm.config.Tracer.CaptureStart(evm, evm.depth, caller.Address(), addr, isPrecompile, false /* create */, DELEGATECALLT, input, gas, big.NewInt(-1), code)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
}
|
||||
snapshot := evm.intraBlockState.Snapshot()
|
||||
@ -424,9 +424,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||
}
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.config.Debug {
|
||||
_ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2), code)
|
||||
evm.config.Tracer.CaptureStart(evm, evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2), code)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
}
|
||||
// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
|
||||
@ -499,9 +499,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||
}
|
||||
if evm.config.Debug || evm.config.EnableTEMV {
|
||||
_ = evm.config.Tracer.CaptureStart(evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig(), nil)
|
||||
evm.config.Tracer.CaptureStart(evm, evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig(), nil)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err) //nolint:errcheck
|
||||
evm.config.Tracer.CaptureEnd(evm.depth, ret, startGas, gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
}
|
||||
nonce := evm.intraBlockState.GetNonce(caller.Address())
|
||||
|
@ -28,50 +28,50 @@ import (
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Add(&x, y)
|
||||
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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Sub(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Mul(&x, 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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Div(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.SDiv(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Mod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.SMod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
base, exponent := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
base, exponent := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
switch {
|
||||
case exponent.IsZero():
|
||||
// x ^ 0 == 1
|
||||
@ -99,20 +99,20 @@ func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
back, num := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
back, num := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
num.ExtendSign(num, &back)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.Peek()
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.Peek()
|
||||
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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if x.Lt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
@ -121,8 +121,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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if x.Gt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
@ -131,8 +131,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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if x.Slt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
@ -141,8 +141,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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if x.Sgt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
@ -151,8 +151,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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if x.Eq(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
@ -161,8 +161,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, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.Peek()
|
||||
if x.IsZero() {
|
||||
x.SetOne()
|
||||
} else {
|
||||
@ -171,32 +171,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.Peek()
|
||||
func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.And(&x, 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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Or(&x, y)
|
||||
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, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
y.Xor(&x, y)
|
||||
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, scope *ScopeContext) ([]byte, error) {
|
||||
th, val := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
val.Byte(&th)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if z.IsZero() {
|
||||
z.Clear()
|
||||
} else {
|
||||
@ -205,8 +205,8 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if z.IsZero() {
|
||||
z.Clear()
|
||||
} else {
|
||||
@ -218,9 +218,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, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
shift, value := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Lsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
@ -232,9 +232,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, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
shift, value := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Rsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
@ -246,8 +246,8 @@ 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) {
|
||||
shift, value := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
shift, value := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
if shift.GtUint64(255) {
|
||||
if value.Sign() >= 0 {
|
||||
value.Clear()
|
||||
@ -262,9 +262,9 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.Pop(), callContext.stack.Peek()
|
||||
data := callContext.memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
func opSha3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.Pop(), scope.Stack.Peek()
|
||||
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
|
||||
@ -279,36 +279,36 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
||||
size.SetBytes(interpreter.hasherBuf[:])
|
||||
return nil, nil
|
||||
}
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetBytes(scope.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, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.Peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
slot.Set(interpreter.evm.IntraBlockState().GetBalance(address))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.TxContext().Origin.Bytes()))
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.TxContext().Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(callContext.contract.value)
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(scope.Contract.value)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.Peek()
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.Peek()
|
||||
if offset, overflow := x.Uint64WithOverflow(); !overflow {
|
||||
data := getData(callContext.contract.Input, offset, 32)
|
||||
data := getData(scope.Contract.Input, offset, 32)
|
||||
x.SetBytes(data)
|
||||
} else {
|
||||
x.Clear()
|
||||
@ -316,16 +316,16 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.Pop()
|
||||
dataOffset = callContext.stack.Pop()
|
||||
length = callContext.stack.Pop()
|
||||
memOffset = scope.Stack.Pop()
|
||||
dataOffset = scope.Stack.Pop()
|
||||
length = scope.Stack.Pop()
|
||||
)
|
||||
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
@ -334,20 +334,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
||||
// These values are checked for overflow during gas cost calculation
|
||||
memOffset64 := memOffset.Uint64()
|
||||
length64 := length.Uint64()
|
||||
callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
|
||||
scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.Pop()
|
||||
dataOffset = callContext.stack.Pop()
|
||||
length = callContext.stack.Pop()
|
||||
memOffset = scope.Stack.Pop()
|
||||
dataOffset = scope.Stack.Pop()
|
||||
length = scope.Stack.Pop()
|
||||
)
|
||||
|
||||
offset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
@ -365,41 +365,41 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
|
||||
if overflow || uint64(len(interpreter.returnData)) < end64 {
|
||||
return nil, ErrReturnDataOutOfBounds
|
||||
}
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.Peek()
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.Peek()
|
||||
slot.SetUint64(uint64(interpreter.evm.IntraBlockState().GetCodeSize(common.Address(slot.Bytes20()))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
l := new(uint256.Int)
|
||||
l.SetUint64(uint64(len(callContext.contract.Code)))
|
||||
callContext.stack.Push(l)
|
||||
l.SetUint64(uint64(len(scope.Contract.Code)))
|
||||
scope.Stack.Push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.Pop()
|
||||
codeOffset = callContext.stack.Pop()
|
||||
length = callContext.stack.Pop()
|
||||
memOffset = scope.Stack.Pop()
|
||||
codeOffset = scope.Stack.Pop()
|
||||
length = scope.Stack.Pop()
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
uint64CodeOffset = 0xffffffffffffffff
|
||||
}
|
||||
codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
stack = callContext.stack
|
||||
stack = scope.Stack
|
||||
a = stack.Pop()
|
||||
memOffset = stack.Pop()
|
||||
codeOffset = stack.Pop()
|
||||
@ -408,7 +408,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
||||
addr := common.Address(a.Bytes20())
|
||||
len64 := length.Uint64()
|
||||
codeCopy := getDataBig(interpreter.evm.IntraBlockState().GetCode(addr), &codeOffset, len64)
|
||||
callContext.memory.Set(memOffset.Uint64(), len64, codeCopy)
|
||||
scope.Memory.Set(memOffset.Uint64(), len64, codeCopy)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -438,8 +438,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
||||
//
|
||||
// (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, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.Peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
if interpreter.evm.IntraBlockState().Empty(address) {
|
||||
slot.Clear()
|
||||
@ -449,17 +449,17 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, overflow := uint256.FromBig(interpreter.evm.TxContext().GasPrice)
|
||||
if overflow {
|
||||
return nil, fmt.Errorf("interpreter.evm.GasPrice higher than 2^256-1")
|
||||
}
|
||||
callContext.stack.Push(v)
|
||||
scope.Stack.Push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
num := callContext.stack.Peek()
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
num := scope.Stack.Peek()
|
||||
num64, overflow := num.Uint64WithOverflow()
|
||||
if overflow {
|
||||
num.Clear()
|
||||
@ -480,83 +480,83 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context().Coinbase.Bytes()))
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context().Coinbase.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v := new(uint256.Int).SetUint64(interpreter.evm.Context().Time)
|
||||
callContext.stack.Push(v)
|
||||
scope.Stack.Push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v := new(uint256.Int).SetUint64(interpreter.evm.Context().BlockNumber)
|
||||
callContext.stack.Push(v)
|
||||
scope.Stack.Push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, overflow := uint256.FromBig(interpreter.evm.Context().Difficulty)
|
||||
if overflow {
|
||||
return nil, fmt.Errorf("interpreter.evm.Context.Difficulty higher than 2^256-1")
|
||||
}
|
||||
callContext.stack.Push(v)
|
||||
scope.Stack.Push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.evm.Context().MaxGasLimit {
|
||||
callContext.stack.Push(new(uint256.Int).SetAllOne())
|
||||
scope.Stack.Push(new(uint256.Int).SetAllOne())
|
||||
} else {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context().GasLimit))
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context().GasLimit))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Pop()
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.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, scope *ScopeContext) ([]byte, error) {
|
||||
v := scope.Stack.Peek()
|
||||
offset := v.Uint64()
|
||||
v.SetBytes(callContext.memory.GetPtr(offset, 32))
|
||||
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
mStart, val := callContext.stack.Pop(), callContext.stack.Pop()
|
||||
callContext.memory.Set32(mStart.Uint64(), &val)
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
mStart, val := scope.Stack.Pop(), scope.Stack.Pop()
|
||||
scope.Memory.Set32(mStart.Uint64(), &val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
off, val := callContext.stack.Pop(), callContext.stack.Pop()
|
||||
callContext.memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
off, val := scope.Stack.Pop(), scope.Stack.Pop()
|
||||
scope.Memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
loc := callContext.stack.Peek()
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.Peek()
|
||||
interpreter.hasherBuf = loc.Bytes32()
|
||||
interpreter.evm.IntraBlockState().GetState(callContext.contract.Address(), &interpreter.hasherBuf, loc)
|
||||
interpreter.evm.IntraBlockState().GetState(scope.Contract.Address(), &interpreter.hasherBuf, loc)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
loc := callContext.stack.Pop()
|
||||
val := callContext.stack.Pop()
|
||||
func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.Pop()
|
||||
val := scope.Stack.Pop()
|
||||
interpreter.hasherBuf = loc.Bytes32()
|
||||
interpreter.evm.IntraBlockState().SetState(callContext.contract.Address(), &interpreter.hasherBuf, val)
|
||||
interpreter.evm.IntraBlockState().SetState(scope.Contract.Address(), &interpreter.hasherBuf, val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
pos := callContext.stack.Pop()
|
||||
if valid, usedBitmap := callContext.contract.validJumpdest(&pos); !valid {
|
||||
func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
pos := scope.Stack.Pop()
|
||||
if valid, usedBitmap := scope.Contract.validJumpdest(&pos); !valid {
|
||||
if usedBitmap && interpreter.cfg.TraceJumpDest {
|
||||
log.Warn("Code Bitmap used for detecting invalid jump",
|
||||
"tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext().TxHash),
|
||||
@ -569,10 +569,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, scope *ScopeContext) ([]byte, error) {
|
||||
pos, cond := scope.Stack.Pop(), scope.Stack.Pop()
|
||||
if !cond.IsZero() {
|
||||
if valid, usedBitmap := callContext.contract.validJumpdest(&pos); !valid {
|
||||
if valid, usedBitmap := scope.Contract.validJumpdest(&pos); !valid {
|
||||
if usedBitmap && interpreter.cfg.TraceJumpDest {
|
||||
log.Warn("Code Bitmap used for detecting invalid jump",
|
||||
"tx", fmt.Sprintf("0x%x", interpreter.evm.TxContext().TxHash),
|
||||
@ -588,32 +588,32 @@ 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, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(*pc))
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(*pc))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.Push(new(uint256.Int).SetUint64(callContext.contract.Gas))
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Push(new(uint256.Int).SetUint64(scope.Contract.Gas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
value = callContext.stack.Pop()
|
||||
offset = callContext.stack.Pop()
|
||||
size = callContext.stack.Peek()
|
||||
input = callContext.memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = callContext.contract.Gas
|
||||
value = scope.Stack.Pop()
|
||||
offset = scope.Stack.Pop()
|
||||
size = scope.Stack.Peek()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
if interpreter.evm.ChainRules().IsEIP150 {
|
||||
gas -= gas / 64
|
||||
@ -621,9 +621,9 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
|
||||
callContext.contract.UseGas(gas)
|
||||
scope.Contract.UseGas(gas)
|
||||
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, &value)
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(scope.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
|
||||
@ -636,7 +636,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
||||
} else {
|
||||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
return res, nil
|
||||
@ -644,21 +644,21 @@ 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, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
endowment = callContext.stack.Pop()
|
||||
offset, size = callContext.stack.Pop(), callContext.stack.Pop()
|
||||
salt = callContext.stack.Pop()
|
||||
input = callContext.memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = callContext.contract.Gas
|
||||
endowment = scope.Stack.Pop()
|
||||
offset, size = scope.Stack.Pop(), scope.Stack.Pop()
|
||||
salt = scope.Stack.Pop()
|
||||
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
callContext.contract.UseGas(gas)
|
||||
scope.Contract.UseGas(gas)
|
||||
// reuse size int for stackvalue
|
||||
stackValue := size
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, &endowment, &salt)
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, &endowment, &salt)
|
||||
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
@ -667,8 +667,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
||||
stackValue.SetBytes(addr.Bytes())
|
||||
}
|
||||
|
||||
callContext.stack.Push(&stackValue)
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Stack.Push(&stackValue)
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
return res, nil
|
||||
@ -676,8 +676,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
|
||||
// We can use this as a temporary value
|
||||
temp := stack.Pop()
|
||||
@ -686,13 +686,13 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
||||
addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get the arguments from the memory.
|
||||
args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, &value, false /* bailout */)
|
||||
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value, false /* bailout */)
|
||||
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
@ -702,17 +702,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
||||
stack.Push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.Pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
@ -720,14 +720,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
||||
addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
//TODO: use uint256.Int instead of converting with toBig()
|
||||
if !value.IsZero() {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, &value)
|
||||
ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
@ -736,16 +736,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
||||
stack.Push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
// We use it as a temporary value
|
||||
temp := stack.Pop()
|
||||
@ -754,9 +754,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
||||
addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
|
||||
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
@ -765,17 +765,17 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
||||
stack.Push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.Pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
@ -783,9 +783,9 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
||||
addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// Get arguments from the memory.
|
||||
args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||
|
||||
ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
|
||||
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
temp.Clear()
|
||||
} else {
|
||||
@ -794,33 +794,33 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
||||
stack.Push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
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.Uint64(), size.Uint64())
|
||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.Pop(), scope.Stack.Pop()
|
||||
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
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.Uint64(), size.Uint64())
|
||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.Pop(), scope.Stack.Pop()
|
||||
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
beneficiary := callContext.stack.Pop()
|
||||
callerAddr := callContext.contract.Address()
|
||||
func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
beneficiary := scope.Stack.Pop()
|
||||
callerAddr := scope.Contract.Address()
|
||||
beneficiaryAddr := common.Address(beneficiary.Bytes20())
|
||||
balance := interpreter.evm.IntraBlockState().GetBalance(callerAddr)
|
||||
interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance)
|
||||
@ -835,18 +835,18 @@ 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, scope *ScopeContext) ([]byte, error) {
|
||||
topics := make([]common.Hash, size)
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
mStart, mSize := stack.Pop(), stack.Pop()
|
||||
for i := 0; i < size; i++ {
|
||||
addr := stack.Pop()
|
||||
topics[i] = common.Hash(addr.Bytes32())
|
||||
}
|
||||
|
||||
d := callContext.memory.GetCopy(mStart.Uint64(), mSize.Uint64())
|
||||
d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64())
|
||||
interpreter.evm.IntraBlockState().AddLog(&types.Log{
|
||||
Address: callContext.contract.Address(),
|
||||
Address: scope.Contract.Address(),
|
||||
Topics: topics,
|
||||
Data: d,
|
||||
// This is a non-consensus field, but assigned here because
|
||||
@ -859,24 +859,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, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
codeLen = uint64(len(callContext.contract.Code))
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
)
|
||||
*pc++
|
||||
if *pc < codeLen {
|
||||
callContext.stack.Push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
|
||||
scope.Stack.Push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||
} else {
|
||||
callContext.stack.Push(integer.Clear())
|
||||
scope.Stack.Push(integer.Clear())
|
||||
}
|
||||
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, scope *ScopeContext) ([]byte, error) {
|
||||
codeLen := len(scope.Contract.Code)
|
||||
|
||||
startMin := int(*pc + 1)
|
||||
if startMin >= codeLen {
|
||||
@ -888,9 +888,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
||||
}
|
||||
|
||||
integer := new(uint256.Int)
|
||||
callContext.stack.Push(integer.SetBytes(common.RightPadBytes(
|
||||
scope.Stack.Push(integer.SetBytes(common.RightPadBytes(
|
||||
// So it doesn't matter what we push onto the stack.
|
||||
callContext.contract.Code[startMin:endMin], pushByteSize)))
|
||||
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
||||
|
||||
*pc += size
|
||||
return nil, nil
|
||||
@ -899,8 +899,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(int(size))
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Dup(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
@ -909,8 +909,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, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.Swap(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
||||
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||
stack.Push(x)
|
||||
stack.Push(y)
|
||||
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.Data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.Data))
|
||||
}
|
||||
@ -226,7 +226,7 @@ func TestAddMod(t *testing.T) {
|
||||
stack.Push(z)
|
||||
stack.Push(y)
|
||||
stack.Push(x)
|
||||
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
actual := stack.Pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
||||
@ -306,7 +306,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||
a.SetBytes(arg)
|
||||
stack.Push(a)
|
||||
}
|
||||
op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
stack.Pop()
|
||||
}
|
||||
}
|
||||
@ -534,12 +534,12 @@ func TestOpMstore(t *testing.T) {
|
||||
pc := uint64(0)
|
||||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.PushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{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(*new(uint256.Int).SetOne(), *new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||
t.Fatalf("Mstore failed to overwrite previous value")
|
||||
}
|
||||
@ -564,7 +564,7 @@ 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, &ScopeContext{mem, stack, nil})
|
||||
}
|
||||
}
|
||||
|
||||
@ -585,7 +585,7 @@ func BenchmarkOpSHA3(bench *testing.B) {
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
stack.PushN(*uint256.NewInt(32), *start)
|
||||
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
|
||||
opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,12 +51,12 @@ type Interpreter interface {
|
||||
Run(contract *Contract, input []byte, static bool) ([]byte, error)
|
||||
}
|
||||
|
||||
// callCtx contains the things that are per-call, such as stack and memory,
|
||||
// ScopeContext 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.Stack
|
||||
contract *Contract
|
||||
type ScopeContext struct {
|
||||
Memory *Memory
|
||||
Stack *stack.Stack
|
||||
Contract *Contract
|
||||
}
|
||||
|
||||
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
||||
@ -197,10 +197,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
locStack = stack.New()
|
||||
callContext = &callCtx{
|
||||
memory: mem,
|
||||
stack: locStack,
|
||||
contract: contract,
|
||||
callContext = &ScopeContext{
|
||||
Memory: mem,
|
||||
Stack: locStack,
|
||||
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
|
||||
@ -225,9 +225,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, in.returnData, contract, in.evm.depth, err) //nolint:errcheck
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) //nolint:errcheck
|
||||
} else {
|
||||
_ = in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -310,7 +310,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
}
|
||||
|
||||
if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, locStack, in.returnData, contract, in.evm.depth, err) //nolint:errcheck
|
||||
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) //nolint:errcheck
|
||||
logged = true
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *stack.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.Stack) (size uint64, overflow bool)
|
||||
|
@ -30,7 +30,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/common/hexutil"
|
||||
"github.com/ledgerwatch/erigon/common/math"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
)
|
||||
|
||||
@ -120,10 +119,10 @@ const (
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, callType CallType, input []byte, gas uint64, value *big.Int, code []byte) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rData []byte, contract *Contract, depth int, err error) error
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error
|
||||
CaptureStart(env *EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, callType CallType, input []byte, gas uint64, value *big.Int, code []byte)
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
||||
CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error)
|
||||
CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int)
|
||||
CaptureAccountRead(account common.Address) error
|
||||
CaptureAccountWrite(account common.Address) error
|
||||
@ -155,17 +154,20 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (l *StructLogger) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureStart(evm *EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rData []byte, contract *Contract, depth int, err error) error {
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
contract := scope.Contract
|
||||
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return ErrTraceLimitReached
|
||||
return
|
||||
}
|
||||
|
||||
// Copy a snapshot of the current memory state to a new buffer
|
||||
@ -217,19 +219,17 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
// create a new snapshot of the EVM.
|
||||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.IntraBlockState().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.Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (l *StructLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
func (l *StructLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
if depth != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
l.output = output
|
||||
l.err = err
|
||||
@ -239,7 +239,6 @@ func (l *StructLogger) CaptureEnd(depth int, output []byte, startGas, endGas uin
|
||||
fmt.Printf(" error: %v\n", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
@ -324,7 +323,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureStart(_ int, from common.Address, to common.Address, preimage bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) error { //nolint:interfacer
|
||||
func (t *mdLogger) CaptureStart(env *EVM, _ int, from common.Address, to common.Address, preimage bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) { //nolint:interfacer
|
||||
if !create {
|
||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
|
||||
from.String(), to.String(),
|
||||
@ -339,10 +338,11 @@ func (t *mdLogger) CaptureStart(_ int, from common.Address, to common.Address, p
|
||||
| Pc | Op | Cost | Stack | RStack | Refund |
|
||||
|-------|-------------|------|-----------|-----------|---------|
|
||||
`)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rData []byte, contract *Contract, depth int, err error) error {
|
||||
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
|
||||
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
|
||||
|
||||
if !t.cfg.DisableStack {
|
||||
@ -359,20 +359,15 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
||||
if err != nil {
|
||||
fmt.Fprintf(t.out, "Error: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error {
|
||||
|
||||
func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureEnd(_ int, output []byte, startGas, endGas uint64, tm time.Duration, err error) error {
|
||||
func (t *mdLogger) CaptureEnd(_ int, output []byte, startGas, endGas uint64, tm time.Duration, err error) {
|
||||
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
output, startGas-endGas, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/common/math"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
)
|
||||
|
||||
type JSONLogger struct {
|
||||
@ -42,12 +41,14 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureStart(env *EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, rData []byte, contract *Contract, depth int, err error) error {
|
||||
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
|
||||
log := StructLog{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
@ -70,16 +71,15 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
||||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
_ = 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.Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
func (l *JSONLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
@ -87,13 +87,13 @@ func (l *JSONLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint6
|
||||
Err string `json:"error,omitempty"`
|
||||
}
|
||||
if depth != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
var errMsg string
|
||||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(startGas - endGas), t, errMsg})
|
||||
_ = l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(startGas - endGas), t, errMsg})
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
|
@ -40,9 +40,12 @@ func TestStoreCapture(t *testing.T) {
|
||||
stack.Push(uint256.NewInt(1))
|
||||
stack.Push(uint256.NewInt(0))
|
||||
var index common.Hash
|
||||
if err := logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, nil, contract, 0, nil); err != nil {
|
||||
t.Fatalf("error while caturing state %v", err)
|
||||
}
|
||||
logger.CaptureState(env, 0, SSTORE, 0, 0, &ScopeContext{
|
||||
Memory: mem,
|
||||
Stack: stack,
|
||||
Contract: contract,
|
||||
}, nil, 0, nil)
|
||||
|
||||
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()]))
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || appengine || gccgo
|
||||
// +build !amd64 appengine gccgo
|
||||
|
||||
package blake2b
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/crypto"
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
)
|
||||
@ -30,7 +29,7 @@ func NewCallTracer(hasTEVM func(contractHash common.Hash) (bool, error)) *CallTr
|
||||
}
|
||||
}
|
||||
|
||||
func (ct *CallTracer) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
func (ct *CallTracer) CaptureStart(evm *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
ct.froms[from] = struct{}{}
|
||||
|
||||
created, ok := ct.tos[to]
|
||||
@ -50,16 +49,12 @@ func (ct *CallTracer) CaptureStart(depth int, from common.Address, to common.Add
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (ct *CallTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rData []byte, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (ct *CallTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
func (ct *CallTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (ct *CallTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
}
|
||||
func (ct *CallTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
return nil
|
||||
func (ct *CallTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
}
|
||||
func (ct *CallTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
ct.froms[from] = struct{}{}
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
)
|
||||
|
||||
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||
@ -139,12 +138,14 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, callType vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
return nil
|
||||
func (a *AccessListTracer) CaptureStart(env *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, callType vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
}
|
||||
|
||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||
func (a *AccessListTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rData []byte, contract *vm.Contract, depth int, err error) error {
|
||||
func (a *AccessListTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
contract := scope.Contract
|
||||
|
||||
stackData := stack.Data
|
||||
stackLen := len(stackData)
|
||||
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
|
||||
@ -163,14 +164,11 @@ func (a *AccessListTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, ga
|
||||
a.list.addAddress(addr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (*AccessListTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
}
|
||||
func (*AccessListTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
return nil
|
||||
func (*AccessListTracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
}
|
||||
func (*AccessListTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ledgerwatch/erigon/core"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -30,7 +31,6 @@ import (
|
||||
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/common/hexutil"
|
||||
"github.com/ledgerwatch/erigon/core"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/crypto"
|
||||
@ -292,8 +292,6 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
|
||||
// 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
|
||||
@ -321,10 +319,18 @@ type Tracer struct {
|
||||
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
||||
}
|
||||
|
||||
// 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)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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, txCtx vm.TxContext) (*Tracer, error) {
|
||||
func New(code string, ctx *Context) (*Tracer, error) {
|
||||
// Resolve any tracers by name and assemble the tracer object
|
||||
if tracer, ok := tracer(code); ok {
|
||||
code = tracer
|
||||
@ -343,7 +349,14 @@ func New(code string, txCtx vm.TxContext) (*Tracer, error) {
|
||||
depthValue: new(uint),
|
||||
refundValue: new(uint),
|
||||
}
|
||||
tracer.ctx["gasPrice"] = txCtx.GasPrice
|
||||
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 {
|
||||
@ -523,7 +536,7 @@ func (jst *Tracer) Stop(err error) {
|
||||
|
||||
// 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) {
|
||||
func (jst *Tracer) 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 {
|
||||
@ -536,8 +549,21 @@ func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error)
|
||||
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
|
||||
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. TODO(karalabe): Yuck, why wrap?!
|
||||
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 {
|
||||
@ -545,9 +571,9 @@ func wrapError(context string, err error) error {
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (jst *Tracer) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
func (jst *Tracer) CaptureStart(env *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
if depth != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
jst.ctx["type"] = "CALL"
|
||||
if create {
|
||||
@ -557,85 +583,72 @@ func (jst *Tracer) CaptureStart(depth int, from common.Address, to common.Addres
|
||||
jst.ctx["to"] = to
|
||||
jst.ctx["input"] = input
|
||||
jst.ctx["gas"] = gas
|
||||
jst.ctx["gasPrice"] = env.TxContext().GasPrice
|
||||
jst.ctx["value"] = value
|
||||
|
||||
return nil
|
||||
// Initialize the context
|
||||
jst.ctx["block"] = env.Context().BlockNumber
|
||||
jst.dbWrapper.db = env.IntraBlockState()
|
||||
// Compute intrinsic gas
|
||||
isHomestead := env.ChainConfig().IsHomestead(env.Context().BlockNumber)
|
||||
isIstanbul := env.ChainConfig().IsIstanbul(env.Context().BlockNumber)
|
||||
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
jst.ctx["intrinsicGas"] = intrinsicGas
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rdata []byte, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
// Initialize the context if it wasn't done yet
|
||||
if !jst.inited {
|
||||
// Update list of precompiles based on current block
|
||||
rules := env.ChainConfig().Rules(env.Context().BlockNumber)
|
||||
jst.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||
|
||||
jst.ctx["block"] = env.Context().BlockNumber
|
||||
// Compute intrinsic gas
|
||||
isHomestead := env.ChainRules().IsHomestead
|
||||
isIstanbul := env.ChainRules().IsIstanbul
|
||||
var input []byte
|
||||
if data, ok := jst.ctx["input"].([]byte); ok {
|
||||
input = data
|
||||
}
|
||||
intrinsicGas, err1 := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
jst.ctx["intrinsicGas"] = intrinsicGas
|
||||
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.IntraBlockState()
|
||||
|
||||
*jst.pcValue = uint(pc)
|
||||
*jst.gasValue = uint(gas)
|
||||
*jst.costValue = uint(cost)
|
||||
*jst.depthValue = uint(depth)
|
||||
*jst.refundValue = uint(env.IntraBlockState().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)
|
||||
}
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rdata []byte, depth int, err error) {
|
||||
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.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(env.IntraBlockState().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)
|
||||
}
|
||||
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 *stack.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)
|
||||
}
|
||||
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, 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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (jst *Tracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
func (jst *Tracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
if depth != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
jst.ctx["output"] = output
|
||||
jst.ctx["time"] = t.String()
|
||||
@ -644,7 +657,6 @@ func (jst *Tracer) CaptureEnd(depth int, output []byte, startGas, endGas uint64,
|
||||
if err != nil {
|
||||
jst.ctx["error"] = err.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (jst *Tracer) CaptureSelfDestruct(from, to common.Address, value *big.Int) {
|
||||
@ -697,7 +709,7 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
|
||||
jst.vm.PutPropString(jst.stateObject, "ctx")
|
||||
|
||||
// Finalize the trace and return the results
|
||||
result, err := jst.call("result", "ctx", "db")
|
||||
result, err := jst.call(false, "result", "ctx", "db")
|
||||
if err != nil {
|
||||
jst.err = wrapError("result", err)
|
||||
}
|
||||
|
@ -48,7 +48,8 @@ type dummyStatedb struct {
|
||||
state.IntraBlockState
|
||||
}
|
||||
|
||||
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
|
||||
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
|
||||
func (*dummyStatedb) GetBalance(addr common.Address) *uint256.Int { return uint256.NewInt(0) }
|
||||
|
||||
type vmContext struct {
|
||||
blockCtx vm.BlockContext
|
||||
@ -71,13 +72,9 @@ func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
|
||||
contract := vm.NewContract(account{}, account{}, value, startGas, false, false)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
|
||||
if err := tracer.CaptureStart(0, contract.Caller(), contract.Address(), false, false, vm.CallType(0), []byte{}, startGas, big.NewInt(int64(value.Uint64())), contract.Code); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tracer.CaptureStart(env, 0, contract.Caller(), contract.Address(), false, false, vm.CallType(0), []byte{}, startGas, big.NewInt(int64(value.Uint64())), contract.Code)
|
||||
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||
if err1 := tracer.CaptureEnd(0, ret, startGas, contract.Gas, 1, err); err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
tracer.CaptureEnd(0, ret, startGas, contract.Gas, 1, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -85,25 +82,26 @@ func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
|
||||
}
|
||||
|
||||
func TestTracer(t *testing.T) {
|
||||
execTracer := func(code string) []byte {
|
||||
execTracer := func(code string) ([]byte, string) {
|
||||
t.Helper()
|
||||
ctx := &vmContext{blockCtx: vm.BlockContext{
|
||||
BlockNumber: 1,
|
||||
ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
|
||||
}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
|
||||
tracer, err := New(code, ctx.txCtx)
|
||||
tracer, err := New(code, new(Context))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err := runTrace(tracer, ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, err.Error()
|
||||
}
|
||||
return ret
|
||||
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; }}",
|
||||
@ -126,10 +124,13 @@ func TestTracer(t *testing.T) {
|
||||
}, { // 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 := 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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +140,7 @@ func TestHalt(t *testing.T) {
|
||||
|
||||
timeout := errors.New("stahp")
|
||||
vmctx := testCtx()
|
||||
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", vmctx.txCtx)
|
||||
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -155,8 +156,7 @@ func TestHalt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHaltBetweenSteps(t *testing.T) {
|
||||
vmctx := testCtx()
|
||||
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", vmctx.txCtx)
|
||||
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -166,12 +166,51 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||
}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
contract := vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0, false, false)
|
||||
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) //nolint:errcheck
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, &vm.ScopeContext{Contract: contract}, nil, 0, nil) //nolint:errcheck
|
||||
timeout := errors.New("stahp")
|
||||
tracer.Stop(timeout)
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) //nolint:errcheck
|
||||
tracer.CaptureState(env, 0, 0, 0, 0, &vm.ScopeContext{Contract: contract}, nil, 0, nil) //nolint:errcheck
|
||||
|
||||
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) {
|
||||
runEmptyTrace := func(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
|
||||
env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
startGas := uint64(10000)
|
||||
contract := vm.NewContract(account{}, account{}, uint256.NewInt(1), startGas, true, false)
|
||||
tracer.CaptureStart(env, 0, contract.Caller(), contract.Address(), false, false, vm.CALLT, nil, 0, big.NewInt(0), nil)
|
||||
tracer.CaptureEnd(0, nil, startGas-contract.Gas, 1, 0, nil)
|
||||
return tracer.GetResult()
|
||||
}
|
||||
execTracer := func(code string) []byte {
|
||||
t.Helper()
|
||||
ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: 1}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
|
||||
tracer, err := New(code, new(Context))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err := runEmptyTrace(tracer, ctx)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
|
||||
statedb, _ := tests.MakePreState(params.Rules{}, tx, alloc, context.BlockNumber)
|
||||
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer, err := New("prestateTracer", txContext)
|
||||
tracer, err := New("prestateTracer", new(Context))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
@ -259,7 +259,7 @@ func TestCallTracer(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer, err := New("callTracer", txContext)
|
||||
tracer, err := New("callTracer", new(Context))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"github.com/ledgerwatch/erigon/core/state"
|
||||
"github.com/ledgerwatch/erigon/core/types"
|
||||
"github.com/ledgerwatch/erigon/core/vm"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/eth/tracers"
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
)
|
||||
@ -100,8 +99,10 @@ func TraceTx(
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Constuct the JavaScript tracer to execute with
|
||||
if tracer, err = tracers.New(*config.Tracer, txCtx); err != nil {
|
||||
// Construct the JavaScript tracer to execute with
|
||||
if tracer, err = tracers.New(*config.Tracer, &tracers.Context{
|
||||
TxHash: txCtx.TxHash,
|
||||
}); err != nil {
|
||||
stream.WriteNil()
|
||||
return err
|
||||
}
|
||||
@ -204,22 +205,25 @@ func NewJsonStreamLogger(cfg *vm.LogConfig, ctx context.Context, stream *jsonite
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (l *JsonStreamLogger) CaptureStart(depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) error {
|
||||
return nil
|
||||
func (l *JsonStreamLogger) CaptureStart(env *vm.EVM, depth int, from common.Address, to common.Address, precompile bool, create bool, calltype vm.CallType, input []byte, gas uint64, value *big.Int, code []byte) {
|
||||
}
|
||||
|
||||
// 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 *JsonStreamLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, rData []byte, contract *vm.Contract, depth int, err error) error {
|
||||
func (l *JsonStreamLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
contract := scope.Contract
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
|
||||
select {
|
||||
case <-l.ctx.Done():
|
||||
return fmt.Errorf("interrupted")
|
||||
return
|
||||
default:
|
||||
}
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return vm.ErrTraceLimitReached
|
||||
return
|
||||
}
|
||||
if !l.firstCapture {
|
||||
l.stream.WriteMore()
|
||||
@ -328,18 +332,16 @@ func (l *JsonStreamLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, ga
|
||||
l.stream.WriteObjectEnd()
|
||||
}
|
||||
l.stream.WriteObjectEnd()
|
||||
return l.stream.Flush()
|
||||
_ = l.stream.Flush()
|
||||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
// while running an opcode.
|
||||
func (l *JsonStreamLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (l *JsonStreamLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (l *JsonStreamLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) error {
|
||||
return nil
|
||||
func (l *JsonStreamLogger) CaptureEnd(depth int, output []byte, startGas, endGas uint64, t time.Duration, err error) {
|
||||
}
|
||||
|
||||
func (l *JsonStreamLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *big.Int) {
|
||||
|
Loading…
Reference in New Issue
Block a user