core/vm: add ReturnStack etc to ScopeContext

pull/30511/head
Marius van der Wijden 2 months ago
parent d23c5e5e62
commit 2297ef65dd
  1. 17
      core/vm/evm.go
  2. 26
      core/vm/instructions_test.go
  3. 45
      core/vm/interpreter.go
  4. 2
      eth/tracers/js/tracer_test.go
  5. 2
      eth/tracers/logger/logger_test.go

@ -226,7 +226,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// The depth-check is already done, and precompiles handled above // The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false, false)
gas = contract.Gas gas = contract.Gas
} }
} }
@ -290,7 +290,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
} }
code := evm.StateDB.GetCode(addrCopy) code := evm.StateDB.GetCode(addrCopy)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false, false)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -341,7 +341,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
} }
code := evm.StateDB.GetCode(addrCopy) code := evm.StateDB.GetCode(addrCopy)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false, false)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -403,7 +403,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors. // when we're in Homestead this also counts for code storage gas errors.
ret, err = evm.interpreter.Run(contract, input, true) ret, err = evm.interpreter.Run(contract, input, true, false)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -533,7 +533,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// initNewContract runs a new contract's creation code, performs checks on the // initNewContract runs a new contract's creation code, performs checks on the
// resulting code that is to be deployed, and consumes necessary gas. // resulting code that is to be deployed, and consumes necessary gas.
func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) { func (evm *EVM) initNewContract(contract *Contract, address common.Address, value *uint256.Int) ([]byte, error) {
ret, err := evm.interpreter.Run(contract, nil, false) // Charge the contract creation init gas in verkle mode
if evm.chainRules.IsEIP4762 {
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
return nil, ErrOutOfGas
}
}
ret, err := evm.interpreter.Run(contract, nil, false, true)
if err != nil { if err != nil {
return ret, err return ret, err
} }

@ -116,7 +116,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, &ScopeContext{nil, stack, nil}) opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
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))
} }
@ -231,7 +231,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, &ScopeContext{nil, stack, nil}) opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
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)
@ -258,7 +258,7 @@ func TestWriteExpectedValues(t *testing.T) {
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)
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) opFn(&pc, interpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
actual := stack.pop() actual := stack.pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
} }
@ -294,7 +294,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var ( var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack() stack = newstack()
scope = &ScopeContext{nil, stack, nil} scope = &ScopeContext{nil, stack, nil, 0, nil, false}
evmInterpreter = NewEVMInterpreter(env) evmInterpreter = NewEVMInterpreter(env)
) )
@ -545,13 +545,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, &ScopeContext{mem, stack, nil}) opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
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, &ScopeContext{mem, stack, nil}) opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
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")
} }
@ -575,7 +575,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, &ScopeContext{mem, stack, nil}) opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
} }
} }
@ -590,7 +590,7 @@ func TestOpTstore(t *testing.T) {
to = common.Address{1} to = common.Address{1}
contractRef = contractRef{caller} contractRef = contractRef{caller}
contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
scopeContext = ScopeContext{mem, stack, contract} scopeContext = ScopeContext{mem, stack, contract, 0, nil, false}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
) )
@ -638,7 +638,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, &ScopeContext{mem, stack, nil}) opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
} }
} }
@ -705,7 +705,7 @@ func TestCreate2Addresses(t *testing.T) {
stack.push(big.NewInt(int64(len(code)))) //size stack.push(big.NewInt(int64(len(code)))) //size
stack.push(big.NewInt(0)) // memstart stack.push(big.NewInt(0)) // memstart
stack.push(big.NewInt(0)) // value stack.push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0, nil, false, 0)
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/ */
expected := common.BytesToAddress(common.FromHex(tt.expected)) expected := common.BytesToAddress(common.FromHex(tt.expected))
@ -733,7 +733,7 @@ func TestRandom(t *testing.T) {
pc = uint64(0) pc = uint64(0)
evmInterpreter = env.interpreter evmInterpreter = env.interpreter
) )
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
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))
} }
@ -775,7 +775,7 @@ func TestBlobHash(t *testing.T) {
evmInterpreter = env.interpreter evmInterpreter = env.interpreter
) )
stack.push(uint256.NewInt(tt.idx)) stack.push(uint256.NewInt(tt.idx))
opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
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))
} }
@ -916,7 +916,7 @@ func TestOpMCopy(t *testing.T) {
mem.Resize(memorySize) mem.Resize(memorySize)
} }
// Do the copy // Do the copy
opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
want := common.FromHex(strings.ReplaceAll(tc.want, " ", "")) want := common.FromHex(strings.ReplaceAll(tc.want, " ", ""))
if have := mem.store; !bytes.Equal(want, have) { if have := mem.store; !bytes.Equal(want, have) {
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have) t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have)

@ -43,6 +43,10 @@ type ScopeContext struct {
Memory *Memory Memory *Memory
Stack *Stack Stack *Stack
Contract *Contract Contract *Contract
CodeSection uint64
ReturnStack ReturnStack
InitCodeMode bool
} }
// MemoryData returns the underlying memory slice. Callers must not modify the contents // MemoryData returns the underlying memory slice. Callers must not modify the contents
@ -89,10 +93,33 @@ func (ctx *ScopeContext) ContractCode() []byte {
return ctx.Contract.Code return ctx.Contract.Code
} }
type ReturnStack []*ReturnContext
// Pop removes an element from the return stack
// Panics if the return stack is empty, which should
// never happen, since EOF code is verified for that.
func (ctx *ReturnStack) Pop() *ReturnContext {
item := (*ctx)[ctx.Len()-1]
*ctx = (*ctx)[:ctx.Len()-1]
return item
}
// Len returns the length of the return stack
func (ctx *ReturnStack) Len() int {
return len(*ctx)
}
type ReturnContext struct {
Section uint64
Pc uint64
StackHeight int
}
// EVMInterpreter represents an EVM interpreter // EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct { type EVMInterpreter struct {
evm *EVM evm *EVM
table *JumpTable table *JumpTable
tableEOF *JumpTable
hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes
@ -148,7 +175,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
} }
} }
evm.Config.ExtraEips = extraEips evm.Config.ExtraEips = extraEips
return &EVMInterpreter{evm: evm, table: table} return &EVMInterpreter{evm: evm, table: table, tableEOF: &pragueEOFInstructionSet}
} }
// Run loops and evaluates the contract's code with the given input data and returns // Run loops and evaluates the contract's code with the given input data and returns
@ -157,7 +184,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// It's important to note that any errors returned by the interpreter should be // It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for // considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left. // ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, isInitCode bool) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024 // Increment the call depth which is restricted to 1024
in.evm.depth++ in.evm.depth++
defer func() { in.evm.depth-- }() defer func() { in.evm.depth-- }()
@ -179,6 +206,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
var ( var (
jt *JumpTable // current jump table
op OpCode // current opcode op OpCode // current opcode
mem = NewMemory() // bound memory mem = NewMemory() // bound memory
stack = newstack() // local stack stack = newstack() // local stack
@ -186,6 +214,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
Memory: mem, Memory: mem,
Stack: stack, Stack: stack,
Contract: contract, Contract: contract,
CodeSection: 0,
ReturnStack: []*ReturnContext{{Section: 0, Pc: 0, StackHeight: 0}},
InitCodeMode: isInitCode,
} }
// 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
@ -208,6 +239,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}() }()
contract.Input = input contract.Input = input
if contract.IsEOF() {
jt = in.tableEOF
} else {
jt = in.table
}
if debug { if debug {
defer func() { // this deferred method handles exit-with-error defer func() { // this deferred method handles exit-with-error
if err == nil { if err == nil {
@ -240,8 +277,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Get the operation from the jump table and validate the stack to ensure there are // Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation. // enough stack items available to perform the operation.
op = contract.GetOp(pc) op = contract.GetOp(pc, callContext.CodeSection)
operation := in.table[op] operation := jt[op]
cost = operation.constantGas // For tracing cost = operation.constantGas // For tracing
// Validate stack // Validate stack
if sLen := stack.len(); sLen < operation.minStack { if sLen := stack.len(); sLen < operation.minStack {

@ -77,7 +77,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := env.Interpreter().Run(contract, []byte{}, false) ret, err := env.Interpreter().Run(contract, []byte{}, false, false)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true) tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
// Rest gas assumes no refund // Rest gas assumes no refund
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)

@ -62,7 +62,7 @@ func TestStoreCapture(t *testing.T) {
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash var index common.Hash
logger.OnTxStart(env.GetVMContext(), nil, common.Address{}) logger.OnTxStart(env.GetVMContext(), nil, common.Address{})
_, err := env.Interpreter().Run(contract, []byte{}, false) _, err := env.Interpreter().Run(contract, []byte{}, false, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

Loading…
Cancel
Save