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:
Frojdi Dymylja 2021-12-15 14:19:58 +01:00 committed by GitHub
parent 37f0dcecc1
commit 499c27d2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 665 additions and 436 deletions

View File

@ -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) {

View File

@ -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) {

View 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)
}

View File

@ -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
}

View File

@ -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())

View File

@ -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
}
}

View File

@ -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})
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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) {

View File

@ -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) {

View File

@ -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()]))
}

View File

@ -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

View File

@ -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{}{}

View File

@ -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) {
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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) {