mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
core, eth/tracer: initialize tracer statedb in CaptureStart (#22333)
This commit is contained in:
parent
5edfd8f9eb
commit
5cb014b60f
13 changed files with 711 additions and 415 deletions
|
|
@ -33,15 +33,23 @@ type JSONLogger struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(*vm.EVM, uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, int, error) {
|
||||
}
|
||||
|
||||
// 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{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
|
|
@ -63,24 +71,20 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
|
|||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
return nil
|
||||
l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
Time time.Duration `json:"time"`
|
||||
Err string `json:"error,omitempty"`
|
||||
}
|
||||
var errMsg string
|
||||
if err != nil {
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
|
||||
errMsg = err.Error()
|
||||
}
|
||||
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
|
||||
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,9 +67,9 @@ func enable1884(jt *JumpTable) {
|
|||
}
|
||||
}
|
||||
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
|
||||
callContext.stack.push(balance)
|
||||
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
|
||||
scope.Stack.push(balance)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -86,9 +86,9 @@ func enable1344(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opChainID implements CHAINID opcode
|
||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId)
|
||||
callContext.stack.push(chainId)
|
||||
scope.Stack.push(chainId)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -149,9 +149,9 @@ func enable3198(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
|
||||
callContext.stack.push(baseFee)
|
||||
callContext.Stack.push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ func enable3855(jt *JumpTable) {
|
|||
}
|
||||
|
||||
// opPush0 implements the PUSH0 opcode
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int))
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
callContext.Stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||
// Calling a non existing account, don't do anything, but ping the tracer
|
||||
if evm.vmConfig.Debug && 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)
|
||||
}
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -26,68 +26,68 @@ import (
|
|||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Add(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Sub(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Mul(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Div(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.SDiv(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Mod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.SMod(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
base, exponent := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
base, exponent := scope.Stack.pop(), scope.Stack.peek()
|
||||
exponent.Exp(&base, exponent)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
back, num := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
back, num := scope.Stack.pop(), scope.Stack.peek()
|
||||
num.ExtendSign(num, &back)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
x.Not(x)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Lt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -96,8 +96,8 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Gt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -106,8 +106,8 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Slt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -116,8 +116,8 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Sgt(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -126,8 +126,8 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
if x.Eq(y) {
|
||||
y.SetOne()
|
||||
} else {
|
||||
|
|
@ -136,8 +136,8 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
if x.IsZero() {
|
||||
x.SetOne()
|
||||
} else {
|
||||
|
|
@ -146,32 +146,32 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.And(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Or(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y := scope.Stack.pop(), scope.Stack.peek()
|
||||
y.Xor(&x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
th, val := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
th, val := scope.Stack.pop(), scope.Stack.peek()
|
||||
val.Byte(&th)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
|
||||
func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
|
||||
if z.IsZero() {
|
||||
z.Clear()
|
||||
} else {
|
||||
|
|
@ -180,8 +180,8 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
|
||||
func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
|
||||
z.MulMod(&x, &y, z)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -189,9 +189,9 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
// opSHL implements Shift Left
|
||||
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
|
||||
func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Lsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
|
|
@ -203,9 +203,9 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
// opSHR implements Logical Shift Right
|
||||
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
|
||||
func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.LtUint64(256) {
|
||||
value.Rsh(value, uint(shift.Uint64()))
|
||||
} else {
|
||||
|
|
@ -217,8 +217,8 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
// opSAR implements Arithmetic Shift Right
|
||||
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
|
||||
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
|
||||
func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
shift, value := callContext.stack.pop(), callContext.stack.peek()
|
||||
func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
shift, value := scope.Stack.pop(), scope.Stack.peek()
|
||||
if shift.GtUint64(256) {
|
||||
if value.Sign() >= 0 {
|
||||
value.Clear()
|
||||
|
|
@ -233,9 +233,9 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.peek()
|
||||
data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
|
||||
|
|
@ -253,37 +253,37 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
size.SetBytes(interpreter.hasherBuf[:])
|
||||
return nil, nil
|
||||
}
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(callContext.contract.value)
|
||||
callContext.stack.push(v)
|
||||
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(scope.Contract.value)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
x := callContext.stack.peek()
|
||||
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
x := scope.Stack.peek()
|
||||
if offset, overflow := x.Uint64WithOverflow(); !overflow {
|
||||
data := getData(callContext.contract.Input, offset, 32)
|
||||
data := getData(scope.Contract.Input, offset, 32)
|
||||
x.SetBytes(data)
|
||||
} else {
|
||||
x.Clear()
|
||||
|
|
@ -291,16 +291,16 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
|
||||
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
dataOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
dataOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
|
|
@ -309,21 +309,21 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
// These values are checked for overflow during gas cost calculation
|
||||
memOffset64 := memOffset.Uint64()
|
||||
length64 := length.Uint64()
|
||||
callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
|
||||
scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
dataOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
dataOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
|
||||
offset64, overflow := dataOffset.Uint64WithOverflow()
|
||||
|
|
@ -337,42 +337,42 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
|
|||
if overflow || uint64(len(interpreter.returnData)) < end64 {
|
||||
return nil, ErrReturnDataOutOfBounds
|
||||
}
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
l := new(uint256.Int)
|
||||
l.SetUint64(uint64(len(callContext.contract.Code)))
|
||||
callContext.stack.push(l)
|
||||
l.SetUint64(uint64(len(scope.Contract.Code)))
|
||||
scope.Stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
memOffset = callContext.stack.pop()
|
||||
codeOffset = callContext.stack.pop()
|
||||
length = callContext.stack.pop()
|
||||
memOffset = scope.Stack.pop()
|
||||
codeOffset = scope.Stack.pop()
|
||||
length = scope.Stack.pop()
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
if overflow {
|
||||
uint64CodeOffset = 0xffffffffffffffff
|
||||
}
|
||||
codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
|
||||
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
stack = callContext.stack
|
||||
stack = scope.Stack
|
||||
a = stack.pop()
|
||||
memOffset = stack.pop()
|
||||
codeOffset = stack.pop()
|
||||
|
|
@ -384,7 +384,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
}
|
||||
addr := common.Address(a.Bytes20())
|
||||
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
|
||||
}
|
||||
|
|
@ -392,16 +392,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
// opExtCodeHash returns the code hash of a specified account.
|
||||
// There are several cases when the function is called, while we can relay everything
|
||||
// to `state.GetCodeHash` function to ensure the correctness.
|
||||
// (1) Caller tries to get the code hash of a normal contract account, state
|
||||
//
|
||||
// (1) Caller tries to get the code hash of a normal contract account, state
|
||||
//
|
||||
// should return the relative code hash and set it as the result.
|
||||
//
|
||||
// (2) Caller tries to get the code hash of a non-existent account, state should
|
||||
// (2) Caller tries to get the code hash of a non-existent account, state should
|
||||
//
|
||||
// return common.Hash{} and zero will be set as the result.
|
||||
//
|
||||
// (3) Caller tries to get the code hash for an account without contract code,
|
||||
// (3) Caller tries to get the code hash for an account without contract code,
|
||||
//
|
||||
// state should return emptyCodeHash(0xc5d246...) as the result.
|
||||
//
|
||||
// (4) Caller tries to get the code hash of a precompiled account, the result
|
||||
// (4) Caller tries to get the code hash of a precompiled account, the result
|
||||
//
|
||||
// should be zero or emptyCodeHash.
|
||||
//
|
||||
// It is worth noting that in order to avoid unnecessary create and clean,
|
||||
|
|
@ -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
|
||||
// customized chain, the return value will be zero.
|
||||
//
|
||||
// (5) Caller tries to get the code hash for an account which is marked as suicided
|
||||
// (5) Caller tries to get the code hash for an account which is marked as suicided
|
||||
//
|
||||
// in the current transaction, the code hash of this account should be returned.
|
||||
//
|
||||
// (6) Caller tries to get the code hash for an account which is marked as deleted,
|
||||
// (6) Caller tries to get the code hash for an account which is marked as deleted,
|
||||
//
|
||||
// this account should be regarded as a non-existent account and zero should be returned.
|
||||
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
slot := callContext.stack.peek()
|
||||
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
if interpreter.evm.StateDB.Empty(address) {
|
||||
slot.Clear()
|
||||
|
|
@ -426,14 +433,14 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.GasPrice)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
num := callContext.stack.peek()
|
||||
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
num := scope.Stack.peek()
|
||||
num64, overflow := num.Uint64WithOverflow()
|
||||
if overflow {
|
||||
num.Clear()
|
||||
|
|
@ -454,108 +461,108 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.Time)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.Difficulty)
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
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
|
||||
if interpreter.evm.Context.Random != nil {
|
||||
v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes()))
|
||||
} else { // if context random is not set, use emptyCodeHash as default
|
||||
v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes())
|
||||
}
|
||||
callContext.stack.push(v)
|
||||
scope.Stack.push(v)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
|
||||
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.pop()
|
||||
func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.pop()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
v := callContext.stack.peek()
|
||||
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v := scope.Stack.peek()
|
||||
offset := int64(v.Uint64())
|
||||
v.SetBytes(callContext.memory.GetPtr(offset, 32))
|
||||
v.SetBytes(scope.Memory.GetPtr(offset, 32))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// pop value of the stack
|
||||
mStart, val := callContext.stack.pop(), callContext.stack.pop()
|
||||
callContext.memory.Set32(mStart.Uint64(), &val)
|
||||
mStart, val := scope.Stack.pop(), scope.Stack.pop()
|
||||
scope.Memory.Set32(mStart.Uint64(), &val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
off, val := callContext.stack.pop(), callContext.stack.pop()
|
||||
callContext.memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
off, val := scope.Stack.pop(), scope.Stack.pop()
|
||||
scope.Memory.store[off.Uint64()] = byte(val.Uint64())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
loc := callContext.stack.peek()
|
||||
func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.peek()
|
||||
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())
|
||||
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 {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
loc := callContext.stack.pop()
|
||||
val := callContext.stack.pop()
|
||||
interpreter.evm.StateDB.SetState(callContext.contract.Address(),
|
||||
loc := scope.Stack.pop()
|
||||
val := scope.Stack.pop()
|
||||
interpreter.evm.StateDB.SetState(scope.Contract.Address(),
|
||||
common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
|
||||
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 {
|
||||
return nil, errStopToken
|
||||
}
|
||||
pos := callContext.stack.pop()
|
||||
if !callContext.contract.validJumpdest(&pos) {
|
||||
pos := scope.Stack.pop()
|
||||
if !scope.Contract.validJumpdest(&pos) {
|
||||
return nil, ErrInvalidJump
|
||||
}
|
||||
*pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
|
||||
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 {
|
||||
return nil, errStopToken
|
||||
}
|
||||
pos, cond := callContext.stack.pop(), callContext.stack.pop()
|
||||
pos, cond := scope.Stack.pop(), scope.Stack.pop()
|
||||
if !cond.IsZero() {
|
||||
if !callContext.contract.validJumpdest(&pos) {
|
||||
if !scope.Contract.validJumpdest(&pos) {
|
||||
return nil, ErrInvalidJump
|
||||
}
|
||||
*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
|
||||
}
|
||||
|
||||
func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(*pc))
|
||||
func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(*pc))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
|
||||
func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
|
||||
func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
value = callContext.stack.pop()
|
||||
offset, size = callContext.stack.pop(), callContext.stack.pop()
|
||||
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = callContext.contract.Gas
|
||||
value = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
if interpreter.evm.chainRules.IsEIP150 {
|
||||
gas -= gas / 64
|
||||
|
|
@ -598,8 +605,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
|
|||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
|
||||
callContext.contract.UseGas(gas)
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
|
||||
scope.Contract.UseGas(gas)
|
||||
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
|
||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||
// 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 {
|
||||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
callContext.stack.push(&stackvalue)
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Stack.push(&stackvalue)
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
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
|
||||
}
|
||||
|
||||
func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
endowment = callContext.stack.pop()
|
||||
offset, size = callContext.stack.pop(), callContext.stack.pop()
|
||||
salt = callContext.stack.pop()
|
||||
input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = callContext.contract.Gas
|
||||
endowment = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
salt = scope.Stack.pop()
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
callContext.contract.UseGas(gas)
|
||||
scope.Contract.UseGas(gas)
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
|
||||
endowment.ToBig(), salt.ToBig())
|
||||
// Push item on the stack based on the returned error.
|
||||
if suberr != nil {
|
||||
|
|
@ -647,8 +654,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
|
|||
} else {
|
||||
stackvalue.SetBytes(addr.Bytes())
|
||||
}
|
||||
callContext.stack.push(&stackvalue)
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Stack.push(&stackvalue)
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
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
|
||||
}
|
||||
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
|
||||
// We can use this as a temporary value
|
||||
temp := stack.pop()
|
||||
|
|
@ -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()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// 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() {
|
||||
return nil, ErrWriteProtection
|
||||
|
|
@ -676,7 +683,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
|||
if !value.IsZero() {
|
||||
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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -685,17 +692,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
|
|
@ -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()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// 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() {
|
||||
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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -717,16 +724,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
stack := callContext.stack
|
||||
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
stack := scope.Stack
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
|
|
@ -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()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// 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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -746,17 +753,17 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
// We use it as a temporary value
|
||||
temp := stack.pop()
|
||||
gas := interpreter.evm.callGasTemp
|
||||
|
|
@ -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()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
// 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 {
|
||||
temp.Clear()
|
||||
} else {
|
||||
|
|
@ -775,45 +782,45 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
|
|||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
callContext.contract.Gas += returnGas
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.pop()
|
||||
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
return ret, errStopToken
|
||||
}
|
||||
|
||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
offset, size := callContext.stack.pop(), callContext.stack.pop()
|
||||
ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, ErrExecutionReverted
|
||||
}
|
||||
|
||||
func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])}
|
||||
func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
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
|
||||
}
|
||||
|
||||
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
beneficiary := callContext.stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
|
||||
beneficiary := scope.Stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -821,21 +828,21 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
|
|||
|
||||
// make log instruction function
|
||||
func makeLog(size int) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
topics := make([]common.Hash, size)
|
||||
stack := callContext.stack
|
||||
stack := scope.Stack
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < size; i++ {
|
||||
addr := stack.pop()
|
||||
topics[i] = common.Hash(addr.Bytes32())
|
||||
}
|
||||
|
||||
d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
||||
d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
|
||||
interpreter.evm.StateDB.AddLog(&types.Log{
|
||||
Address: callContext.contract.Address(),
|
||||
Address: scope.Contract.Address(),
|
||||
Topics: topics,
|
||||
Data: d,
|
||||
// This is a non-consensus field, but assigned here because
|
||||
|
|
@ -848,24 +855,24 @@ func makeLog(size int) executionFunc {
|
|||
}
|
||||
|
||||
// opPush1 is a specialized version of pushN
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
codeLen = uint64(len(callContext.contract.Code))
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
)
|
||||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
|
||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||
} else {
|
||||
callContext.stack.push(integer.Clear())
|
||||
scope.Stack.push(integer.Clear())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, pushByteSize int) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
codeLen := len(callContext.contract.Code)
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
codeLen := len(scope.Contract.Code)
|
||||
|
||||
startMin := codeLen
|
||||
if int(*pc+1) < startMin {
|
||||
|
|
@ -878,8 +885,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
}
|
||||
|
||||
integer := new(uint256.Int)
|
||||
callContext.stack.push(integer.SetBytes(common.RightPadBytes(
|
||||
callContext.contract.Code[startMin:endMin], pushByteSize)))
|
||||
scope.Stack.push(integer.SetBytes(common.RightPadBytes(
|
||||
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
||||
|
||||
*pc += size
|
||||
return nil, nil
|
||||
|
|
@ -888,8 +895,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
|
||||
// make dup instruction function
|
||||
func makeDup(size int64) executionFunc {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.dup(int(size))
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.dup(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -898,8 +905,8 @@ func makeDup(size int64) executionFunc {
|
|||
func makeSwap(size int64) executionFunc {
|
||||
// switch n + 1 otherwise n would be swapped with n
|
||||
size++
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
|
||||
callContext.stack.swap(int(size))
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.swap(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
|||
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
||||
}
|
||||
|
|
@ -222,7 +222,7 @@ func TestAddMod(t *testing.T) {
|
|||
stack.push(z)
|
||||
stack.push(y)
|
||||
stack.push(x)
|
||||
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
actual := stack.pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
|
||||
|
|
@ -244,7 +244,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
|
|||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
_, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil})
|
||||
_, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
a.SetBytes(arg)
|
||||
stack.push(a)
|
||||
}
|
||||
op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -535,13 +535,13 @@ func TestOpMstore(t *testing.T) {
|
|||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||
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 {
|
||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||
}
|
||||
stack.push(new(uint256.Int).SetUint64(0x1))
|
||||
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" {
|
||||
t.Fatalf("Mstore failed to overwrite previous value")
|
||||
}
|
||||
|
|
@ -565,7 +565,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(value)
|
||||
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++ {
|
||||
stack.push(uint256.NewInt(32))
|
||||
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)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
)
|
||||
opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil})
|
||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,12 +60,12 @@ type Interpreter interface {
|
|||
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
|
||||
type callCtx struct {
|
||||
memory *Memory
|
||||
stack *Stack
|
||||
contract *Contract
|
||||
type ScopeContext struct {
|
||||
Memory *Memory
|
||||
Stack *Stack
|
||||
Contract *Contract
|
||||
}
|
||||
|
||||
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
||||
|
|
@ -170,10 +170,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
callContext = &callCtx{
|
||||
memory: mem,
|
||||
stack: stack,
|
||||
contract: contract,
|
||||
callContext = &ScopeContext{
|
||||
Memory: mem,
|
||||
Stack: stack,
|
||||
Contract: contract,
|
||||
}
|
||||
// For optimisation reason we're using uint64 as the program counter.
|
||||
// It's theoretically possible to go above 2^64. The YP defines the PC
|
||||
|
|
@ -192,9 +192,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
} else {
|
||||
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
|
||||
in.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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ package vm
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
|
|
@ -31,8 +31,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||
|
||||
// Storage represents a contract's storage.
|
||||
type Storage map[common.Hash]common.Hash
|
||||
|
||||
|
|
@ -105,10 +103,10 @@ func (s *StructLog) ErrorString() string {
|
|||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
|
||||
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, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
||||
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
contract := scope.Contract
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return errTraceLimitReached
|
||||
return
|
||||
}
|
||||
|
||||
// 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}
|
||||
|
||||
l.logs = append(l.logs, log)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
// while running an opcode.
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return nil
|
||||
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (l *StructLogger) CaptureEnd(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.err = err
|
||||
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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructLogs returns the captured log entries.
|
||||
|
|
@ -261,3 +258,65 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,16 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
|||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
|
||||
|
||||
// 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{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
|
|
@ -69,16 +73,11 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
|||
}
|
||||
log.Stack = logstack
|
||||
}
|
||||
return l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureFault outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
return l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err)
|
||||
l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
Output string `json:"output"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
|
|
@ -89,5 +88,5 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
|
|||
if err != nil {
|
||||
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})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,14 +52,17 @@ func TestStoreCapture(t *testing.T) {
|
|||
var (
|
||||
env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||
scope = &ScopeContext{
|
||||
Memory: NewMemory(),
|
||||
Stack: newstack(),
|
||||
Contract: contract,
|
||||
}
|
||||
)
|
||||
stack.push(uint256.NewInt(1))
|
||||
stack.push(new(uint256.Int))
|
||||
scope.Stack.push(uint256.NewInt(1))
|
||||
scope.Stack.push(new(uint256.Int))
|
||||
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 {
|
||||
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package runtime
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -329,32 +330,161 @@ func TestBlockhash(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// BenchmarkSimpleLoop test a pretty simple loop which loops
|
||||
// 1M (1 048 575) times.
|
||||
// Takes about 200 ms
|
||||
func BenchmarkSimpleLoop(b *testing.B) {
|
||||
// 0xfffff = 1048575 loops
|
||||
code := []byte{
|
||||
byte(vm.PUSH3), 0x0f, 0xff, 0xff,
|
||||
byte(vm.JUMPDEST), // [ count ]
|
||||
byte(vm.PUSH1), 1, // [count, 1]
|
||||
byte(vm.SWAP1), // [1, count]
|
||||
byte(vm.SUB), // [ count -1 ]
|
||||
byte(vm.DUP1), // [ count -1 , count-1]
|
||||
byte(vm.PUSH1), 4, // [count-1, count -1, label]
|
||||
byte(vm.JUMPI), // [ 0 ]
|
||||
byte(vm.STOP),
|
||||
// benchmarkNonModifyingCode benchmarks code, but if the code modifies the
|
||||
// state, this should not be used, since it does not reset the state between runs.
|
||||
func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
|
||||
cfg := new(Config)
|
||||
setDefaults(cfg)
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
cfg.GasLimit = gas
|
||||
var (
|
||||
destination = common.BytesToAddress([]byte("contract"))
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
)
|
||||
cfg.State.CreateAccount(destination)
|
||||
eoa := common.HexToAddress("E0")
|
||||
{
|
||||
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)
|
||||
//Execute(code, nil, &Config{
|
||||
//Execute(loopingCode, nil, &Config{
|
||||
// EVMConfig: vm.Config{
|
||||
// Debug: true,
|
||||
// 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++ {
|
||||
Execute(code, nil, nil)
|
||||
}
|
||||
//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
|
||||
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
|
||||
}
|
||||
|
||||
// TestEip2929Cases contains various testcases that are used for
|
||||
|
|
@ -381,7 +511,8 @@ func TestEip2929Cases(t *testing.T) {
|
|||
code, ops)
|
||||
Execute(code, nil, &Config{
|
||||
EVMConfig: vm.Config{
|
||||
Debug: false,
|
||||
Debug: true,
|
||||
Tracer: vm.NewMarkdownLogger(nil, os.Stdout),
|
||||
ExtraEips: []int{2929},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"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
|
||||
// function for each VM execution step.
|
||||
type Tracer struct {
|
||||
inited bool // Flag whether the context was already inited from the EVM
|
||||
|
||||
vm *duktape.Context // Javascript VM instance
|
||||
|
||||
tracerObject int // Stack index of the tracer JavaScript object
|
||||
|
|
@ -526,7 +525,7 @@ func wrapError(context string, err error) error {
|
|||
}
|
||||
|
||||
// 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"
|
||||
if 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["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.
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
// Initialize the context if it wasn't done yet
|
||||
if !jst.inited {
|
||||
jst.ctx["block"] = env.BlockNumber.Uint64()
|
||||
jst.inited = true
|
||||
}
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if atomic.LoadUint32(&jst.interrupt) > 0 {
|
||||
jst.err = jst.reason
|
||||
return nil
|
||||
}
|
||||
jst.opWrapper.op = op
|
||||
jst.stackWrapper.stack = stack
|
||||
jst.memoryWrapper.memory = memory
|
||||
jst.contractWrapper.contract = contract
|
||||
jst.dbWrapper.db = env.StateDB
|
||||
|
||||
*jst.pcValue = uint(pc)
|
||||
*jst.gasValue = uint(gas)
|
||||
*jst.costValue = uint(cost)
|
||||
*jst.depthValue = uint(depth)
|
||||
*jst.refundValue = uint(env.StateDB.GetRefund())
|
||||
|
||||
jst.errorValue = nil
|
||||
if err != nil {
|
||||
jst.errorValue = new(string)
|
||||
*jst.errorValue = err.Error()
|
||||
}
|
||||
_, err := jst.call("step", "log", "db")
|
||||
if err != nil {
|
||||
jst.err = wrapError("step", err)
|
||||
}
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
if jst.err != nil {
|
||||
return
|
||||
}
|
||||
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
|
||||
// while running an opcode.
|
||||
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||
if jst.err == nil {
|
||||
// Apart from the error, everything matches the previous invocation
|
||||
*jst.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("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.
|
||||
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["gasUsed"] = gasUsed
|
||||
jst.ctx["time"] = t.String()
|
||||
jst.ctx["gasUsed"] = gasUsed
|
||||
|
||||
if err != nil {
|
||||
jst.ctx["error"] = err.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||
|
|
|
|||
|
|
@ -47,21 +47,150 @@ type dummyStatedb struct {
|
|||
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) {
|
||||
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.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 {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
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; }}")
|
||||
|
|
@ -139,40 +268,3 @@ func TestOpcodes(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue