core, eth/tracer: initialize tracer statedb in CaptureStart (#22333)

This commit is contained in:
Daniel Liu 2024-04-23 14:35:43 +08:00
parent 5edfd8f9eb
commit 5cb014b60f
13 changed files with 711 additions and 415 deletions

View file

@ -33,15 +33,23 @@ type JSONLogger struct {
} }
func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger { func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
return &JSONLogger{json.NewEncoder(writer), cfg} l := &JSONLogger{json.NewEncoder(writer), cfg}
if l.cfg == nil {
l.cfg = &vm.LogConfig{}
}
return l
} }
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
return nil }
func (l *JSONLogger) CaptureFault(*vm.EVM, uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, int, error) {
} }
// CaptureState outputs state information on the logger. // CaptureState outputs state information on the logger.
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
memory := scope.Memory
stack := scope.Stack
log := vm.StructLog{ log := vm.StructLog{
Pc: pc, Pc: pc,
Op: op, Op: op,
@ -63,24 +71,20 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
} }
log.Stack = logstack log.Stack = logstack
} }
return l.encoder.Encode(log) l.encoder.Encode(log)
}
// CaptureFault outputs state information on the logger.
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
return nil
} }
// CaptureEnd is triggered at end of execution. // CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
type endLog struct { type endLog struct {
Output string `json:"output"` Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`
Time time.Duration `json:"time"` Time time.Duration `json:"time"`
Err string `json:"error,omitempty"` Err string `json:"error,omitempty"`
} }
var errMsg string
if err != nil { if err != nil {
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) errMsg = err.Error()
} }
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
} }

View file

@ -67,9 +67,9 @@ func enable1884(jt *JumpTable) {
} }
} }
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
callContext.stack.push(balance) scope.Stack.push(balance)
return nil, nil return nil, nil
} }
@ -86,9 +86,9 @@ func enable1344(jt *JumpTable) {
} }
// opChainID implements CHAINID opcode // opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId) chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId)
callContext.stack.push(chainId) scope.Stack.push(chainId)
return nil, nil return nil, nil
} }
@ -149,9 +149,9 @@ func enable3198(jt *JumpTable) {
} }
// opBaseFee implements BASEFEE opcode // 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, _ := uint256.FromBig(common.MinGasPrice50x) baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
callContext.stack.push(baseFee) callContext.Stack.push(baseFee)
return nil, nil return nil, nil
} }
@ -167,7 +167,7 @@ func enable3855(jt *JumpTable) {
} }
// opPush0 implements the PUSH0 opcode // opPush0 implements the PUSH0 opcode
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int)) callContext.Stack.push(new(uint256.Int))
return nil, nil return nil, nil
} }

View file

@ -248,7 +248,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer // Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
} }
return nil, gas, nil return nil, gas, nil
@ -266,7 +266,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Capture the tracer start/end events in debug mode // Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func() { // Lazy evaluation of the parameters defer func() { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
@ -439,7 +439,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract.SetCodeOptionalHash(&address, codeAndHash) contract.SetCodeOptionalHash(&address, codeAndHash)
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} }
start := time.Now() start := time.Now()

View file

@ -26,68 +26,68 @@ import (
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Add(&x, y) y.Add(&x, y)
return nil, nil return nil, nil
} }
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Sub(&x, y) y.Sub(&x, y)
return nil, nil return nil, nil
} }
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Mul(&x, y) y.Mul(&x, y)
return nil, nil return nil, nil
} }
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Div(&x, y) y.Div(&x, y)
return nil, nil return nil, nil
} }
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.SDiv(&x, y) y.SDiv(&x, y)
return nil, nil return nil, nil
} }
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Mod(&x, y) y.Mod(&x, y)
return nil, nil return nil, nil
} }
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.SMod(&x, y) y.SMod(&x, y)
return nil, nil return nil, nil
} }
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
base, exponent := callContext.stack.pop(), callContext.stack.peek() base, exponent := scope.Stack.pop(), scope.Stack.peek()
exponent.Exp(&base, exponent) exponent.Exp(&base, exponent)
return nil, nil return nil, nil
} }
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
back, num := callContext.stack.pop(), callContext.stack.peek() back, num := scope.Stack.pop(), scope.Stack.peek()
num.ExtendSign(num, &back) num.ExtendSign(num, &back)
return nil, nil return nil, nil
} }
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := callContext.stack.peek() x := scope.Stack.peek()
x.Not(x) x.Not(x)
return nil, nil return nil, nil
} }
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
if x.Lt(y) { if x.Lt(y) {
y.SetOne() y.SetOne()
} else { } else {
@ -96,8 +96,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil return nil, nil
} }
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
if x.Gt(y) { if x.Gt(y) {
y.SetOne() y.SetOne()
} else { } else {
@ -106,8 +106,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil return nil, nil
} }
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
if x.Slt(y) { if x.Slt(y) {
y.SetOne() y.SetOne()
} else { } else {
@ -116,8 +116,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil return nil, nil
} }
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
if x.Sgt(y) { if x.Sgt(y) {
y.SetOne() y.SetOne()
} else { } else {
@ -126,8 +126,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil return nil, nil
} }
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
if x.Eq(y) { if x.Eq(y) {
y.SetOne() y.SetOne()
} else { } else {
@ -136,8 +136,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
return nil, nil return nil, nil
} }
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := callContext.stack.peek() x := scope.Stack.peek()
if x.IsZero() { if x.IsZero() {
x.SetOne() x.SetOne()
} else { } else {
@ -146,32 +146,32 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
return nil, nil return nil, nil
} }
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.And(&x, y) y.And(&x, y)
return nil, nil return nil, nil
} }
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Or(&x, y) y.Or(&x, y)
return nil, nil return nil, nil
} }
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := scope.Stack.pop(), scope.Stack.peek()
y.Xor(&x, y) y.Xor(&x, y)
return nil, nil return nil, nil
} }
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
th, val := callContext.stack.pop(), callContext.stack.peek() th, val := scope.Stack.pop(), scope.Stack.peek()
val.Byte(&th) val.Byte(&th)
return nil, nil return nil, nil
} }
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
if z.IsZero() { if z.IsZero() {
z.Clear() z.Clear()
} else { } else {
@ -180,8 +180,8 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
return nil, nil return nil, nil
} }
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
z.MulMod(&x, &y, z) z.MulMod(&x, &y, z)
return nil, nil return nil, nil
} }
@ -189,9 +189,9 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// opSHL implements Shift Left // opSHL implements Shift Left
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, // 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. // 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 // 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) { if shift.LtUint64(256) {
value.Lsh(value, uint(shift.Uint64())) value.Lsh(value, uint(shift.Uint64()))
} else { } else {
@ -203,9 +203,9 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// opSHR implements Logical Shift Right // opSHR implements Logical Shift Right
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, // 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. // 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 // 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) { if shift.LtUint64(256) {
value.Rsh(value, uint(shift.Uint64())) value.Rsh(value, uint(shift.Uint64()))
} else { } else {
@ -217,8 +217,8 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// opSAR implements Arithmetic Shift Right // opSAR implements Arithmetic Shift Right
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // 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. // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
shift, value := callContext.stack.pop(), callContext.stack.peek() shift, value := scope.Stack.pop(), scope.Stack.peek()
if shift.GtUint64(256) { if shift.GtUint64(256) {
if value.Sign() >= 0 { if value.Sign() >= 0 {
value.Clear() value.Clear()
@ -233,9 +233,9 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
return nil, nil return nil, nil
} }
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.peek() offset, size := scope.Stack.pop(), scope.Stack.peek()
data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil { if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@ -253,37 +253,37 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
size.SetBytes(interpreter.hasherBuf[:]) size.SetBytes(interpreter.hasherBuf[:])
return nil, nil return nil, nil
} }
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes())) scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
return nil, nil return nil, nil
} }
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := callContext.stack.peek() slot := scope.Stack.peek()
address := common.Address(slot.Bytes20()) address := common.Address(slot.Bytes20())
slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil return nil, nil
} }
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil return nil, nil
} }
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes())) scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
return nil, nil return nil, nil
} }
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(callContext.contract.value) v, _ := uint256.FromBig(scope.Contract.value)
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := callContext.stack.peek() x := scope.Stack.peek()
if offset, overflow := x.Uint64WithOverflow(); !overflow { if offset, overflow := x.Uint64WithOverflow(); !overflow {
data := getData(callContext.contract.Input, offset, 32) data := getData(scope.Contract.Input, offset, 32)
x.SetBytes(data) x.SetBytes(data)
} else { } else {
x.Clear() x.Clear()
@ -291,16 +291,16 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
return nil, nil return nil, nil
} }
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input)))) scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
return nil, nil return nil, nil
} }
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
memOffset = callContext.stack.pop() memOffset = scope.Stack.pop()
dataOffset = callContext.stack.pop() dataOffset = scope.Stack.pop()
length = callContext.stack.pop() length = scope.Stack.pop()
) )
dataOffset64, overflow := dataOffset.Uint64WithOverflow() dataOffset64, overflow := dataOffset.Uint64WithOverflow()
if overflow { if overflow {
@ -309,21 +309,21 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
// These values are checked for overflow during gas cost calculation // These values are checked for overflow during gas cost calculation
memOffset64 := memOffset.Uint64() memOffset64 := memOffset.Uint64()
length64 := length.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 return nil, nil
} }
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil return nil, nil
} }
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
memOffset = callContext.stack.pop() memOffset = scope.Stack.pop()
dataOffset = callContext.stack.pop() dataOffset = scope.Stack.pop()
length = callContext.stack.pop() length = scope.Stack.pop()
) )
offset64, overflow := dataOffset.Uint64WithOverflow() offset64, overflow := dataOffset.Uint64WithOverflow()
@ -337,42 +337,42 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
if overflow || uint64(len(interpreter.returnData)) < end64 { if overflow || uint64(len(interpreter.returnData)) < end64 {
return nil, ErrReturnDataOutOfBounds 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 return nil, nil
} }
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := callContext.stack.peek() slot := scope.Stack.peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20())))) slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
return nil, nil 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 := new(uint256.Int)
l.SetUint64(uint64(len(callContext.contract.Code))) l.SetUint64(uint64(len(scope.Contract.Code)))
callContext.stack.push(l) scope.Stack.push(l)
return nil, nil return nil, nil
} }
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
memOffset = callContext.stack.pop() memOffset = scope.Stack.pop()
codeOffset = callContext.stack.pop() codeOffset = scope.Stack.pop()
length = callContext.stack.pop() length = scope.Stack.pop()
) )
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow { if overflow {
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = 0xffffffffffffffff
} }
codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64()) codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
return nil, nil return nil, nil
} }
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
stack = callContext.stack stack = scope.Stack
a = stack.pop() a = stack.pop()
memOffset = stack.pop() memOffset = stack.pop()
codeOffset = stack.pop() codeOffset = stack.pop()
@ -384,7 +384,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
} }
addr := common.Address(a.Bytes20()) addr := common.Address(a.Bytes20())
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
return nil, nil return nil, nil
} }
@ -392,16 +392,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// opExtCodeHash returns the code hash of a specified account. // opExtCodeHash returns the code hash of a specified account.
// There are several cases when the function is called, while we can relay everything // There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness. // to `state.GetCodeHash` function to ensure the correctness.
// (1) Caller tries to get the code hash of a normal contract account, state //
// (1) Caller tries to get the code hash of a normal contract account, state
//
// should return the relative code hash and set it as the result. // should return the relative code hash and set it as the result.
// //
// (2) Caller tries to get the code hash of a non-existent account, state should // (2) Caller tries to get the code hash of a non-existent account, state should
//
// return common.Hash{} and zero will be set as the result. // return common.Hash{} and zero will be set as the result.
// //
// (3) Caller tries to get the code hash for an account without contract code, // (3) Caller tries to get the code hash for an account without contract code,
//
// state should return emptyCodeHash(0xc5d246...) as the result. // state should return emptyCodeHash(0xc5d246...) as the result.
// //
// (4) Caller tries to get the code hash of a precompiled account, the result // (4) Caller tries to get the code hash of a precompiled account, the result
//
// should be zero or emptyCodeHash. // should be zero or emptyCodeHash.
// //
// It is worth noting that in order to avoid unnecessary create and clean, // It is worth noting that in order to avoid unnecessary create and clean,
@ -410,13 +415,15 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// If the precompile account is not transferred any amount on a private or // If the precompile account is not transferred any amount on a private or
// customized chain, the return value will be zero. // customized chain, the return value will be zero.
// //
// (5) Caller tries to get the code hash for an account which is marked as suicided // (5) Caller tries to get the code hash for an account which is marked as suicided
//
// in the current transaction, the code hash of this account should be returned. // in the current transaction, the code hash of this account should be returned.
// //
// (6) Caller tries to get the code hash for an account which is marked as deleted, // (6) Caller tries to get the code hash for an account which is marked as deleted,
//
// this account should be regarded as a non-existent account and zero should be returned. // 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) { func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := callContext.stack.peek() slot := scope.Stack.peek()
address := common.Address(slot.Bytes20()) address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) { if interpreter.evm.StateDB.Empty(address) {
slot.Clear() slot.Clear()
@ -426,14 +433,14 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
return nil, nil return nil, nil
} }
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.GasPrice) v, _ := uint256.FromBig(interpreter.evm.GasPrice)
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
num := callContext.stack.peek() num := scope.Stack.peek()
num64, overflow := num.Uint64WithOverflow() num64, overflow := num.Uint64WithOverflow()
if overflow { if overflow {
num.Clear() num.Clear()
@ -454,108 +461,108 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
return nil, nil return nil, nil
} }
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil return nil, nil
} }
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.Time) v, _ := uint256.FromBig(interpreter.evm.Time)
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.BlockNumber) v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.Difficulty) v, _ := uint256.FromBig(interpreter.evm.Difficulty)
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var v *uint256.Int var v *uint256.Int
if interpreter.evm.Context.Random != nil { if interpreter.evm.Context.Random != nil {
v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes())) v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes()))
} else { // if context random is not set, use emptyCodeHash as default } else { // if context random is not set, use emptyCodeHash as default
v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes()) v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes())
} }
callContext.stack.push(v) scope.Stack.push(v)
return nil, nil return nil, nil
} }
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
return nil, nil return nil, nil
} }
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.pop() scope.Stack.pop()
return nil, nil return nil, nil
} }
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v := callContext.stack.peek() v := scope.Stack.peek()
offset := int64(v.Uint64()) offset := int64(v.Uint64())
v.SetBytes(callContext.memory.GetPtr(offset, 32)) v.SetBytes(scope.Memory.GetPtr(offset, 32))
return nil, nil return nil, nil
} }
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// pop value of the stack // pop value of the stack
mStart, val := callContext.stack.pop(), callContext.stack.pop() mStart, val := scope.Stack.pop(), scope.Stack.pop()
callContext.memory.Set32(mStart.Uint64(), &val) scope.Memory.Set32(mStart.Uint64(), &val)
return nil, nil return nil, nil
} }
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
off, val := callContext.stack.pop(), callContext.stack.pop() off, val := scope.Stack.pop(), scope.Stack.pop()
callContext.memory.store[off.Uint64()] = byte(val.Uint64()) scope.Memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil return nil, nil
} }
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
loc := callContext.stack.peek() loc := scope.Stack.peek()
hash := common.Hash(loc.Bytes32()) hash := common.Hash(loc.Bytes32())
val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash) val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash)
loc.SetBytes(val.Bytes()) loc.SetBytes(val.Bytes())
return nil, nil return nil, nil
} }
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
loc := callContext.stack.pop() loc := scope.Stack.pop()
val := callContext.stack.pop() val := scope.Stack.pop()
interpreter.evm.StateDB.SetState(callContext.contract.Address(), interpreter.evm.StateDB.SetState(scope.Contract.Address(),
common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
return nil, nil return nil, nil
} }
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if atomic.LoadInt32(&interpreter.evm.abort) != 0 { if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
return nil, errStopToken return nil, errStopToken
} }
pos := callContext.stack.pop() pos := scope.Stack.pop()
if !callContext.contract.validJumpdest(&pos) { if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump return nil, ErrInvalidJump
} }
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
return nil, nil return nil, nil
} }
func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if atomic.LoadInt32(&interpreter.evm.abort) != 0 { if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
return nil, errStopToken return nil, errStopToken
} }
pos, cond := callContext.stack.pop(), callContext.stack.pop() pos, cond := scope.Stack.pop(), scope.Stack.pop()
if !cond.IsZero() { if !cond.IsZero() {
if !callContext.contract.validJumpdest(&pos) { if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump return nil, ErrInvalidJump
} }
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
@ -563,34 +570,34 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
return nil, nil 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 return nil, nil
} }
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(*pc)) scope.Stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil return nil, nil
} }
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len()))) scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
return nil, nil return nil, nil
} }
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas)) scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
return nil, nil return nil, nil
} }
func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
var ( var (
value = callContext.stack.pop() value = scope.Stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop() offset, size = scope.Stack.pop(), scope.Stack.pop()
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas gas = scope.Contract.Gas
) )
if interpreter.evm.chainRules.IsEIP150 { if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64 gas -= gas / 64
@ -598,8 +605,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
callContext.contract.UseGas(gas) scope.Contract.UseGas(gas)
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig()) res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, value.ToBig())
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
@ -611,8 +618,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
} else { } else {
stackvalue.SetBytes(addr.Bytes()) stackvalue.SetBytes(addr.Bytes())
} }
callContext.stack.push(&stackvalue) scope.Stack.push(&stackvalue)
callContext.contract.Gas += returnGas scope.Contract.Gas += returnGas
if suberr == ErrExecutionReverted { if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer interpreter.returnData = res // set REVERT data to return data buffer
@ -622,24 +629,24 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
return nil, nil return nil, nil
} }
func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
var ( var (
endowment = callContext.stack.pop() endowment = scope.Stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop() offset, size = scope.Stack.pop(), scope.Stack.pop()
salt = callContext.stack.pop() salt = scope.Stack.pop()
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas gas = scope.Contract.Gas
) )
// Apply EIP150 // Apply EIP150
gas -= gas / 64 gas -= gas / 64
callContext.contract.UseGas(gas) scope.Contract.UseGas(gas)
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
endowment.ToBig(), salt.ToBig()) endowment.ToBig(), salt.ToBig())
// Push item on the stack based on the returned error. // Push item on the stack based on the returned error.
if suberr != nil { if suberr != nil {
@ -647,8 +654,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
} else { } else {
stackvalue.SetBytes(addr.Bytes()) stackvalue.SetBytes(addr.Bytes())
} }
callContext.stack.push(&stackvalue) scope.Stack.push(&stackvalue)
callContext.contract.Gas += returnGas scope.Contract.Gas += returnGas
if suberr == ErrExecutionReverted { if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer interpreter.returnData = res // set REVERT data to return data buffer
@ -658,8 +665,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
return nil, nil return nil, nil
} }
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := callContext.stack stack := scope.Stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp. // Pop gas. The actual gas in interpreter.evm.callGasTemp.
// We can use this as a temporary value // We can use this as a temporary value
temp := stack.pop() temp := stack.pop()
@ -668,7 +675,7 @@ 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() addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20()) toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory. // Get the arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
if interpreter.readOnly && !value.IsZero() { if interpreter.readOnly && !value.IsZero() {
return nil, ErrWriteProtection return nil, ErrWriteProtection
@ -676,7 +683,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
} }
ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig()) ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, value.ToBig())
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -685,17 +692,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
stack.push(&temp) stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret) 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
interpreter.returnData = ret interpreter.returnData = ret
return ret, nil 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. // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := callContext.stack stack := scope.Stack
// We use it as a temporary value // We use it as a temporary value
temp := stack.pop() temp := stack.pop()
gas := interpreter.evm.callGasTemp gas := interpreter.evm.callGasTemp
@ -703,12 +710,12 @@ 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() addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20()) toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory. // Get arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
} }
ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig()) ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, value.ToBig())
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -717,16 +724,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
stack.push(&temp) stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret) 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
interpreter.returnData = ret interpreter.returnData = ret
return ret, nil return ret, nil
} }
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := callContext.stack stack := scope.Stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp. // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
// We use it as a temporary value // We use it as a temporary value
temp := stack.pop() temp := stack.pop()
@ -735,9 +742,9 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20()) toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory. // Get arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(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 { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -746,17 +753,17 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
stack.push(&temp) stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret) 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
interpreter.returnData = ret interpreter.returnData = ret
return ret, nil 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. // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := callContext.stack stack := scope.Stack
// We use it as a temporary value // We use it as a temporary value
temp := stack.pop() temp := stack.pop()
gas := interpreter.evm.callGasTemp gas := interpreter.evm.callGasTemp
@ -764,9 +771,9 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20()) toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory. // Get arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(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 { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -775,45 +782,45 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
stack.push(&temp) stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
ret = common.CopyBytes(ret) 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
interpreter.returnData = ret interpreter.returnData = ret
return ret, nil return ret, nil
} }
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop() offset, size := scope.Stack.pop(), scope.Stack.pop()
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
return ret, errStopToken return ret, errStopToken
} }
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop() offset, size := scope.Stack.pop(), scope.Stack.pop()
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
interpreter.returnData = ret interpreter.returnData = ret
return ret, ErrExecutionReverted return ret, ErrExecutionReverted
} }
func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])} return nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.Code[*pc])}
} }
func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return nil, errStopToken return nil, errStopToken
} }
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
beneficiary := callContext.stack.pop() beneficiary := scope.Stack.pop()
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
interpreter.evm.StateDB.Suicide(callContext.contract.Address()) interpreter.evm.StateDB.Suicide(scope.Contract.Address())
return nil, errStopToken return nil, errStopToken
} }
@ -821,21 +828,21 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
// make log instruction function // make log instruction function
func makeLog(size int) executionFunc { 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) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
topics := make([]common.Hash, size) topics := make([]common.Hash, size)
stack := callContext.stack stack := scope.Stack
mStart, mSize := stack.pop(), stack.pop() mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
addr := stack.pop() addr := stack.pop()
topics[i] = common.Hash(addr.Bytes32()) topics[i] = common.Hash(addr.Bytes32())
} }
d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
interpreter.evm.StateDB.AddLog(&types.Log{ interpreter.evm.StateDB.AddLog(&types.Log{
Address: callContext.contract.Address(), Address: scope.Contract.Address(),
Topics: topics, Topics: topics,
Data: d, Data: d,
// This is a non-consensus field, but assigned here because // This is a non-consensus field, but assigned here because
@ -848,24 +855,24 @@ func makeLog(size int) executionFunc {
} }
// opPush1 is a specialized version of pushN // 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 ( var (
codeLen = uint64(len(callContext.contract.Code)) codeLen = uint64(len(scope.Contract.Code))
integer = new(uint256.Int) integer = new(uint256.Int)
) )
*pc += 1 *pc += 1
if *pc < codeLen { if *pc < codeLen {
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc]))) scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
} else { } else {
callContext.stack.push(integer.Clear()) scope.Stack.push(integer.Clear())
} }
return nil, nil return nil, nil
} }
// make push instruction function // make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc { func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
codeLen := len(callContext.contract.Code) codeLen := len(scope.Contract.Code)
startMin := codeLen startMin := codeLen
if int(*pc+1) < startMin { if int(*pc+1) < startMin {
@ -878,8 +885,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
} }
integer := new(uint256.Int) integer := new(uint256.Int)
callContext.stack.push(integer.SetBytes(common.RightPadBytes( scope.Stack.push(integer.SetBytes(common.RightPadBytes(
callContext.contract.Code[startMin:endMin], pushByteSize))) scope.Contract.Code[startMin:endMin], pushByteSize)))
*pc += size *pc += size
return nil, nil return nil, nil
@ -888,8 +895,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function // make dup instruction function
func makeDup(size int64) executionFunc { func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.dup(int(size)) scope.Stack.dup(int(size))
return nil, nil return nil, nil
} }
} }
@ -898,8 +905,8 @@ func makeDup(size int64) executionFunc {
func makeSwap(size int64) executionFunc { func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n // switch n + 1 otherwise n would be swapped with n
size++ size++
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
callContext.stack.swap(int(size)) scope.Stack.swap(int(size))
return nil, nil return nil, nil
} }
} }

View file

@ -107,7 +107,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil}) opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 { if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
} }
@ -222,7 +222,7 @@ func TestAddMod(t *testing.T) {
stack.push(z) stack.push(z)
stack.push(y) stack.push(y)
stack.push(x) stack.push(x)
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil}) opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
actual := stack.pop() actual := stack.pop()
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
@ -244,7 +244,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
_, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil}) _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
@ -308,7 +308,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
a.SetBytes(arg) a.SetBytes(arg)
stack.push(a) stack.push(a)
} }
op(&pc, evmInterpreter, &callCtx{nil, stack, nil}) op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
stack.pop() stack.pop()
} }
} }
@ -535,13 +535,13 @@ func TestOpMstore(t *testing.T) {
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
stack.push(new(uint256.Int)) stack.push(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 { if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v) t.Fatalf("Mstore fail, got %v, expected %v", got, v)
} }
stack.push(new(uint256.Int).SetUint64(0x1)) stack.push(new(uint256.Int).SetUint64(0x1))
stack.push(new(uint256.Int)) stack.push(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" { if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value") t.Fatalf("Mstore failed to overwrite previous value")
} }
@ -565,7 +565,7 @@ func BenchmarkOpMstore(bench *testing.B) {
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.push(value) stack.push(value)
stack.push(memStart) stack.push(memStart)
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
} }
} }
@ -585,7 +585,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.push(uint256.NewInt(32)) stack.push(uint256.NewInt(32))
stack.push(start) stack.push(start)
opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
} }
} }
@ -681,7 +681,7 @@ func TestRandom(t *testing.T) {
pc = uint64(0) pc = uint64(0)
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil}) opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 { if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
} }

View file

@ -60,12 +60,12 @@ type Interpreter interface {
CanRun([]byte) bool CanRun([]byte) bool
} }
// 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 // but not transients like pc and gas
type callCtx struct { type ScopeContext struct {
memory *Memory Memory *Memory
stack *Stack Stack *Stack
contract *Contract Contract *Contract
} }
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
@ -170,10 +170,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
op OpCode // current opcode op OpCode // current opcode
mem = NewMemory() // bound memory mem = NewMemory() // bound memory
stack = newstack() // local stack stack = newstack() // local stack
callContext = &callCtx{ callContext = &ScopeContext{
memory: mem, Memory: mem,
stack: stack, Stack: stack,
contract: contract, Contract: contract,
} }
// For optimisation reason we're using uint64 as the program counter. // 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 // It's theoretically possible to go above 2^64. The YP defines the PC
@ -192,9 +192,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
defer func() { defer func() {
if err != nil { if err != nil {
if !logged { if !logged {
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else { } else {
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
} }
} }
}() }()
@ -257,7 +257,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
if in.cfg.Debug { if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true logged = true
} }

View file

@ -21,7 +21,7 @@ import (
) )
type ( 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, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64 // memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool) memorySizeFunc func(*Stack) (size uint64, overflow bool)

View file

@ -18,10 +18,10 @@ package vm
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"strings"
"time" "time"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
@ -31,8 +31,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/params"
) )
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
// Storage represents a contract's storage. // Storage represents a contract's storage.
type Storage map[common.Hash]common.Hash type Storage map[common.Hash]common.Hash
@ -105,10 +103,10 @@ func (s *StructLog) ErrorString() string {
// Note that reference types are actual VM data structures; make copies // Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call. // if you need to retain them beyond the current call.
type Tracer interface { type Tracer interface {
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error 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, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
} }
// StructLogger is an EVM state logger and implements Tracer. // StructLogger is an EVM state logger and implements Tracer.
@ -137,17 +135,19 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
} }
// CaptureStart implements the Tracer interface to initialize the tracing operation. // CaptureStart implements the Tracer interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
return nil
} }
// CaptureState logs a new structured log message and pushes it out to the environment // CaptureState logs a new structured log message and pushes it out to the environment
// //
// CaptureState also tracks SSTORE ops to track dirty values. // 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, 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 // check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return errTraceLimitReached return
} }
// initialise new changed values storage container for this contract // initialise new changed values storage container for this contract
@ -188,17 +188,15 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err} log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log) l.logs = append(l.logs, log)
return nil
} }
// CaptureFault implements the Tracer interface to trace an execution fault // CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode. // while running an opcode.
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
return nil
} }
// CaptureEnd is called after the call finishes to finalize the tracing. // CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
l.output = output l.output = output
l.err = err l.err = err
if l.cfg.Debug { if l.cfg.Debug {
@ -207,7 +205,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
fmt.Printf(" error: %v\n", err) fmt.Printf(" error: %v\n", err)
} }
} }
return nil
} }
// StructLogs returns the captured log entries. // StructLogs returns the captured log entries.
@ -261,3 +258,65 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
fmt.Fprintln(writer) fmt.Fprintln(writer)
} }
} }
type mdLogger struct {
out io.Writer
cfg *LogConfig
}
// NewMarkdownLogger creates a logger which outputs information in a format adapted
// for human readability, and is also a valid markdown table
func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
l := &mdLogger{writer, cfg}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
return l
}
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
if !create {
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
} else {
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
}
fmt.Fprintf(t.out, `
| Pc | Op | Cost | Stack | RStack | Refund |
|-------|-------------|------|-----------|-----------|---------|
`)
}
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
func (t *mdLogger) CaptureState(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 {
// format stack
var a []string
for _, elem := range stack.data {
a = append(a, fmt.Sprintf("%v", elem.String()))
}
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
fmt.Fprintf(t.out, "%10v |", b)
}
fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
fmt.Fprintln(t.out, "")
if err != nil {
fmt.Fprintf(t.out, "Error: %v\n", err)
}
}
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)
}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}

View file

@ -41,12 +41,16 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
return l return l
} }
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
return nil
} }
func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
// CaptureState outputs state information on the logger. // CaptureState outputs state information on the logger.
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { func (l *JSONLogger) CaptureState(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{ log := StructLog{
Pc: pc, Pc: pc,
Op: op, Op: op,
@ -69,16 +73,11 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
} }
log.Stack = logstack 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, contract *Contract, depth int, err error) error {
return l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err)
} }
// CaptureEnd is triggered at end of execution. // CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
type endLog struct { type endLog struct {
Output string `json:"output"` Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`
@ -89,5 +88,5 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
if err != nil { if err != nil {
errMsg = err.Error() errMsg = err.Error()
} }
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg}) l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
} }

View file

@ -52,14 +52,17 @@ func TestStoreCapture(t *testing.T) {
var ( var (
env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{}) env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
logger = NewStructLogger(nil) logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
scope = &ScopeContext{
Memory: NewMemory(),
Stack: newstack(),
Contract: contract,
}
) )
stack.push(uint256.NewInt(1)) scope.Stack.push(uint256.NewInt(1))
stack.push(new(uint256.Int)) scope.Stack.push(new(uint256.Int))
var index common.Hash var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 { if len(logger.changedValues[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
} }

View file

@ -19,6 +19,7 @@ package runtime
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"os"
"strings" "strings"
"testing" "testing"
@ -329,32 +330,161 @@ func TestBlockhash(t *testing.T) {
} }
} }
// BenchmarkSimpleLoop test a pretty simple loop which loops // benchmarkNonModifyingCode benchmarks code, but if the code modifies the
// 1M (1 048 575) times. // state, this should not be used, since it does not reset the state between runs.
// Takes about 200 ms func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
func BenchmarkSimpleLoop(b *testing.B) { cfg := new(Config)
// 0xfffff = 1048575 loops setDefaults(cfg)
code := []byte{ cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
byte(vm.PUSH3), 0x0f, 0xff, 0xff, cfg.GasLimit = gas
byte(vm.JUMPDEST), // [ count ] var (
byte(vm.PUSH1), 1, // [count, 1] destination = common.BytesToAddress([]byte("contract"))
byte(vm.SWAP1), // [1, count] vmenv = NewEnv(cfg)
byte(vm.SUB), // [ count -1 ] sender = vm.AccountRef(cfg.Origin)
byte(vm.DUP1), // [ count -1 , count-1] )
byte(vm.PUSH1), 4, // [count-1, count -1, label] cfg.State.CreateAccount(destination)
byte(vm.JUMPI), // [ 0 ] eoa := common.HexToAddress("E0")
byte(vm.STOP), {
cfg.State.CreateAccount(eoa)
cfg.State.SetNonce(eoa, 100)
} }
reverting := common.HexToAddress("EE")
{
cfg.State.CreateAccount(reverting)
cfg.State.SetCode(reverting, []byte{
byte(vm.PUSH1), 0x00,
byte(vm.PUSH1), 0x00,
byte(vm.REVERT),
})
}
//cfg.State.CreateAccount(cfg.Origin)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(destination, code)
vmenv.Call(sender, destination, nil, gas, cfg.Value)
b.Run(name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
vmenv.Call(sender, destination, nil, gas, cfg.Value)
}
})
}
// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
// 55 ms
func BenchmarkSimpleLoop(b *testing.B) {
staticCallIdentity := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.STATICCALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callIdentity := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callInexistant := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0xff, // address of existing contract
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callEOA := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0xE0, // address of EOA
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
loopingCode := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP),
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
calllRevertingContractWithInput := []byte{
byte(vm.JUMPDEST), //
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.PUSH1), 0x20, // in size
byte(vm.PUSH1), 0x00, // in offset
byte(vm.PUSH1), 0x00, // value
byte(vm.PUSH1), 0xEE, // address of reverting contract
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
//tracer := vm.NewJSONLogger(nil, os.Stdout) //tracer := vm.NewJSONLogger(nil, os.Stdout)
//Execute(code, nil, &Config{ //Execute(loopingCode, nil, &Config{
// EVMConfig: vm.Config{ // EVMConfig: vm.Config{
// Debug: true, // Debug: true,
// Tracer: tracer, // Tracer: tracer,
// }}) // }})
// 100M gas
benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b)
benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b)
benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b)
benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b)
benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b)
benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b)
for i := 0; i < b.N; i++ { //benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
Execute(code, nil, nil) //benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
}
} }
// TestEip2929Cases contains various testcases that are used for // TestEip2929Cases contains various testcases that are used for
@ -381,7 +511,8 @@ func TestEip2929Cases(t *testing.T) {
code, ops) code, ops)
Execute(code, nil, &Config{ Execute(code, nil, &Config{
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Debug: false, Debug: true,
Tracer: vm.NewMarkdownLogger(nil, os.Stdout),
ExtraEips: []int{2929}, ExtraEips: []int{2929},
}, },
}) })

View file

@ -27,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/log"
@ -286,8 +287,6 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
// Tracer provides an implementation of Tracer that evaluates a Javascript // Tracer provides an implementation of Tracer that evaluates a Javascript
// function for each VM execution step. // function for each VM execution step.
type Tracer struct { type Tracer struct {
inited bool // Flag whether the context was already inited from the EVM
vm *duktape.Context // Javascript VM instance vm *duktape.Context // Javascript VM instance
tracerObject int // Stack index of the tracer JavaScript object tracerObject int // Stack index of the tracer JavaScript object
@ -526,7 +525,7 @@ func wrapError(context string, err error) error {
} }
// CaptureStart implements the Tracer interface to initialize the tracing operation. // CaptureStart implements the Tracer interface to initialize the tracing operation.
func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
jst.ctx["type"] = "CALL" jst.ctx["type"] = "CALL"
if create { if create {
jst.ctx["type"] = "CREATE" jst.ctx["type"] = "CREATE"
@ -537,73 +536,75 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b
jst.ctx["gas"] = gas jst.ctx["gas"] = gas
jst.ctx["value"] = value jst.ctx["value"] = value
return nil // Initialize the context
jst.ctx["block"] = env.Context.BlockNumber.Uint64()
jst.dbWrapper.db = env.StateDB
// Compute intrinsic gas
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead)
if err != nil {
return
}
jst.ctx["intrinsicGas"] = intrinsicGas
} }
// CaptureState implements the Tracer interface to trace a single step of VM execution. // CaptureState implements the Tracer interface to trace a single step of VM execution.
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { 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 { if jst.err != nil {
// Initialize the context if it wasn't done yet return
if !jst.inited {
jst.ctx["block"] = env.BlockNumber.Uint64()
jst.inited = true
}
// If tracing was interrupted, set the error and stop
if atomic.LoadUint32(&jst.interrupt) > 0 {
jst.err = jst.reason
return nil
}
jst.opWrapper.op = op
jst.stackWrapper.stack = stack
jst.memoryWrapper.memory = memory
jst.contractWrapper.contract = contract
jst.dbWrapper.db = env.StateDB
*jst.pcValue = uint(pc)
*jst.gasValue = uint(gas)
*jst.costValue = uint(cost)
*jst.depthValue = uint(depth)
*jst.refundValue = uint(env.StateDB.GetRefund())
jst.errorValue = nil
if err != nil {
jst.errorValue = new(string)
*jst.errorValue = err.Error()
}
_, err := jst.call("step", "log", "db")
if err != nil {
jst.err = wrapError("step", err)
}
} }
return nil // 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
// CaptureFault implements the Tracer interface to trace an execution fault *jst.pcValue = uint(pc)
// while running an opcode. *jst.gasValue = uint(gas)
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { *jst.costValue = uint(cost)
if jst.err == nil { *jst.depthValue = uint(depth)
// Apart from the error, everything matches the previous invocation *jst.refundValue = uint(env.StateDB.GetRefund())
jst.errorValue = nil
if err != nil {
jst.errorValue = new(string) jst.errorValue = new(string)
*jst.errorValue = err.Error() *jst.errorValue = err.Error()
_, err := jst.call("fault", "log", "db")
if err != nil {
jst.err = wrapError("fault", err)
}
} }
return nil
if _, err := jst.call("step", "log", "db"); err != nil {
jst.err = wrapError("step", err)
}
}
// CaptureFault implements the Tracer interface to trace an execution fault
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("fault", "log", "db"); err != nil {
jst.err = wrapError("fault", err)
}
} }
// CaptureEnd is called after the call finishes to finalize the tracing. // CaptureEnd is called after the call finishes to finalize the tracing.
func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
jst.ctx["output"] = output jst.ctx["output"] = output
jst.ctx["gasUsed"] = gasUsed
jst.ctx["time"] = t.String() jst.ctx["time"] = t.String()
jst.ctx["gasUsed"] = gasUsed
if err != nil { if err != nil {
jst.ctx["error"] = err.Error() jst.ctx["error"] = err.Error()
} }
return nil
} }
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error

View file

@ -47,21 +47,150 @@ type dummyStatedb struct {
state.StateDB state.StateDB
} }
func (*dummyStatedb) GetRefund() uint64 { return 1337 } func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) }
func runTrace(tracer *Tracer) (json.RawMessage, error) { func runTrace(tracer *Tracer) (json.RawMessage, error) {
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
var (
startGas uint64 = 10000
value = big.NewInt(0)
)
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Interpreter().Run(contract, []byte{}, false) tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
ret, err := env.Interpreter().Run(contract, []byte{}, false)
tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tracer.GetResult() return tracer.GetResult()
} }
func TestTracer(t *testing.T) {
execTracer := func(code string) []byte {
t.Helper()
tracer, err := New(code)
if err != nil {
t.Fatal(err)
}
ret, err := runTrace(tracer)
if err != nil {
t.Fatal(err)
}
return ret
}
for i, tt := range []struct {
code string
want string
}{
{ // tests that we don't panic on bad arguments to memory access
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
want: `[{},{},{}]`,
}, { // tests that we don't panic on bad arguments to stack peeks
code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
want: `["0","0","0"]`,
}, { // tests that we don't panic on bad arguments to memory getUint
code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
want: `["0","0","0"]`,
}, { // tests some general counting
code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
want: `3`,
}, { // tests that depth is reported correctly
code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}",
want: `[0,1,2]`,
}, { // tests to-string of opcodes
code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}",
want: `["PUSH1","PUSH1","STOP"]`,
}, { // tests intrinsic gas
code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
want: `"6.21000"`,
},
} {
if have := execTracer(tt.code); tt.want != string(have) {
t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code)
}
}
}
func TestHalt(t *testing.T) {
t.Skip("duktape doesn't support abortion")
timeout := errors.New("stahp")
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}")
if err != nil {
t.Fatal(err)
}
go func() {
time.Sleep(1 * time.Second)
tracer.Stop(timeout)
}()
if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" {
t.Errorf("Expected timeout error, got %v", err)
}
}
func TestHaltBetweenSteps(t *testing.T) {
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}")
if err != nil {
t.Fatal(err)
}
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
}
tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
tracer.CaptureState(env, 0, 0, 0, 0, scope, nil, 0, nil)
if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
t.Errorf("Expected timeout error, got %v", err)
}
}
// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
// in 'result'
func TestNoStepExec(t *testing.T) {
runEmptyTrace := func(tracer *Tracer) (json.RawMessage, error) {
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
startGas := uint64(10000)
contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas)
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0))
tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil)
return tracer.GetResult()
}
execTracer := func(code string) []byte {
t.Helper()
tracer, err := New(code)
if err != nil {
t.Fatal(err)
}
ret, err := runEmptyTrace(tracer)
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)
}
}
}
// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory access // TestRegressionPanicSlice tests that we don't panic on bad arguments to memory access
func TestRegressionPanicSlice(t *testing.T) { func TestRegressionPanicSlice(t *testing.T) {
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}") tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}")
@ -139,40 +268,3 @@ func TestOpcodes(t *testing.T) {
t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret)) t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret))
} }
} }
func TestHalt(t *testing.T) {
t.Skip("duktape doesn't support abortion")
timeout := errors.New("stahp")
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}")
if err != nil {
t.Fatal(err)
}
go func() {
time.Sleep(1 * time.Second)
tracer.Stop(timeout)
}()
if _, err = runTrace(tracer); err.Error() != "stahp in server-side tracer function 'step'" {
t.Errorf("Expected timeout error, got %v", err)
}
}
func TestHaltBetweenSteps(t *testing.T) {
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}")
if err != nil {
t.Fatal(err)
}
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
t.Errorf("Expected timeout error, got %v", err)
}
}