core/vm: add ReturnStack etc to ScopeContext
This commit is contained in:
parent
d23c5e5e62
commit
2297ef65dd
|
@ -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
|
||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
}
|
||||
code := evm.StateDB.GetCode(addrCopy)
|
||||
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
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -341,7 +341,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||
}
|
||||
code := evm.StateDB.GetCode(addrCopy)
|
||||
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
|
||||
}
|
||||
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
|
||||
// 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.
|
||||
ret, err = evm.interpreter.Run(contract, input, true)
|
||||
ret, err = evm.interpreter.Run(contract, input, true, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
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
|
||||
// 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) {
|
||||
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 {
|
||||
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))
|
||||
stack.push(x)
|
||||
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 {
|
||||
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(y)
|
||||
stack.push(x)
|
||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
|
||||
actual := stack.pop()
|
||||
if actual.Cmp(expected) != 0 {
|
||||
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))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
||||
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil, 0, nil, false})
|
||||
actual := stack.pop()
|
||||
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 (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
scope = &ScopeContext{nil, stack, nil, 0, nil, false}
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
|
||||
|
@ -545,13 +545,13 @@ func TestOpMstore(t *testing.T) {
|
|||
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
|
||||
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
|
||||
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
|
||||
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
|
||||
}
|
||||
stack.push(new(uint256.Int).SetUint64(0x1))
|
||||
stack.push(new(uint256.Int))
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
|
||||
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil, 0, nil, false})
|
||||
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
|
||||
t.Fatalf("Mstore failed to overwrite previous value")
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(value)
|
||||
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}
|
||||
contractRef = contractRef{caller}
|
||||
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")
|
||||
)
|
||||
|
||||
|
@ -638,7 +638,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
|||
for i := 0; i < bench.N; i++ {
|
||||
stack.push(uint256.NewInt(32))
|
||||
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(0)) // memstart
|
||||
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())
|
||||
*/
|
||||
expected := common.BytesToAddress(common.FromHex(tt.expected))
|
||||
|
@ -733,7 +733,7 @@ func TestRandom(t *testing.T) {
|
|||
pc = uint64(0)
|
||||
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 {
|
||||
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
|
||||
)
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
// 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, " ", ""))
|
||||
if have := mem.store; !bytes.Equal(want, have) {
|
||||
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have)
|
||||
|
|
|
@ -43,6 +43,10 @@ type ScopeContext struct {
|
|||
Memory *Memory
|
||||
Stack *Stack
|
||||
Contract *Contract
|
||||
|
||||
CodeSection uint64
|
||||
ReturnStack ReturnStack
|
||||
InitCodeMode bool
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
type EVMInterpreter struct {
|
||||
evm *EVM
|
||||
table *JumpTable
|
||||
tableEOF *JumpTable
|
||||
|
||||
hasher crypto.KeccakState // Keccak256 hasher instance 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
|
||||
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
|
||||
|
@ -157,7 +184,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
|||
// It's important to note that any errors returned by the interpreter should be
|
||||
// considered a revert-and-consume-all-gas operation except for
|
||||
// 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
|
||||
in.evm.depth++
|
||||
defer func() { in.evm.depth-- }()
|
||||
|
@ -179,6 +206,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
}
|
||||
|
||||
var (
|
||||
jt *JumpTable // current jump table
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
|
@ -186,6 +214,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
Memory: mem,
|
||||
Stack: stack,
|
||||
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.
|
||||
// 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
|
||||
|
||||
if contract.IsEOF() {
|
||||
jt = in.tableEOF
|
||||
} else {
|
||||
jt = in.table
|
||||
}
|
||||
|
||||
if debug {
|
||||
defer func() { // this deferred method handles exit-with-error
|
||||
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
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
operation := in.table[op]
|
||||
op = contract.GetOp(pc, callContext.CodeSection)
|
||||
operation := jt[op]
|
||||
cost = operation.constantGas // For tracing
|
||||
// Validate stack
|
||||
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.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)
|
||||
// Rest gas assumes no refund
|
||||
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)}
|
||||
var index common.Hash
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue