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