core/vm: reduce allocations on function call path

This commit is contained in:
Ömer Faruk IRMAK 2025-07-23 14:30:28 +03:00
parent c3ef6c77c2
commit bdcaeb299c
7 changed files with 74 additions and 68 deletions

View file

@ -47,12 +47,12 @@ type Contract struct {
} }
// NewContract returns a new contract environment for the execution of EVM. // NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract { func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) Contract {
// Initialize the jump analysis cache if it's nil, mostly for tests // Initialize the jump analysis cache if it's nil, mostly for tests
if jumpDests == nil { if jumpDests == nil {
jumpDests = newMapJumpDests() jumpDests = newMapJumpDests()
} }
return &Contract{ return Contract{
caller: caller, caller: caller,
address: address, address: address,
jumpDests: jumpDests, jumpDests: jumpDests,

View file

@ -130,6 +130,8 @@ type EVM struct {
readOnly bool // Whether to throw on stateful modifications readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse returnData []byte // Last CALL's return data for subsequent reuse
ScopeContext // Current Run context
} }
// NewEVM constructs an EVM instance with the supplied block context, state // NewEVM constructs an EVM instance with the supplied block context, state
@ -294,7 +296,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
contract := NewContract(caller, addr, value, gas, evm.jumpDests) contract := NewContract(caller, addr, value, gas, evm.jumpDests)
contract.IsSystemCall = isSystemCall(caller) contract.IsSystemCall = isSystemCall(caller)
contract.SetCallCode(evm.resolveCodeHash(addr), code) contract.SetCallCode(evm.resolveCodeHash(addr), code)
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(&contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
} }
@ -353,7 +355,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, caller, value, gas, evm.jumpDests) contract := NewContract(caller, caller, value, gas, evm.jumpDests)
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(&contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -397,7 +399,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// Note: The value refers to the original value from the parent call. // Note: The value refers to the original value from the parent call.
contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) contract := NewContract(originCaller, caller, value, gas, evm.jumpDests)
contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr))
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(&contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -452,7 +454,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// 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.Run(contract, input, true) ret, err = evm.Run(&contract, input, true)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -560,7 +562,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
contract.SetCallCode(common.Hash{}, code) contract.SetCallCode(common.Hash{}, code)
contract.IsDeployment = true contract.IsDeployment = true
ret, err = evm.initNewContract(contract, address) ret, err = evm.initNewContract(&contract, address)
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {

View file

@ -107,7 +107,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, evm, &ScopeContext{nil, stack, nil}) opFn(&pc, evm, &ScopeContext{Stack: stack})
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))
} }
@ -221,7 +221,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, evm, &ScopeContext{nil, stack, nil}) opAddmod(&pc, evm, &ScopeContext{Stack: stack})
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)
@ -247,7 +247,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, evm, &ScopeContext{nil, stack, nil}) opFn(&pc, evm, &ScopeContext{Stack: stack})
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)}
} }
@ -283,7 +283,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var ( var (
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack() stack = newstack()
scope = &ScopeContext{nil, stack, nil} scope = &ScopeContext{Stack: stack}
) )
// convert args // convert args
intArgs := make([]*uint256.Int, len(args)) intArgs := make([]*uint256.Int, len(args))
@ -528,13 +528,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, evm, &ScopeContext{mem, stack, nil}) opMstore(&pc, evm, &ScopeContext{Memory: mem, Stack: stack})
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, evm, &ScopeContext{mem, stack, nil}) opMstore(&pc, evm, &ScopeContext{Memory: mem, Stack: stack})
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")
} }
@ -555,7 +555,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, evm, &ScopeContext{mem, stack, nil}) opMstore(&pc, evm, &ScopeContext{Memory: mem, Stack: stack})
} }
} }
@ -568,7 +568,7 @@ func TestOpTstore(t *testing.T) {
caller = common.Address{} caller = common.Address{}
to = common.Address{1} to = common.Address{1}
contract = NewContract(caller, to, new(uint256.Int), 0, nil) contract = NewContract(caller, to, new(uint256.Int), 0, nil)
scopeContext = ScopeContext{mem, stack, contract} scopeContext = ScopeContext{Memory: mem, Stack: stack, Contract: contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
) )
@ -613,7 +613,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, evm, &ScopeContext{mem, stack, nil}) opKeccak256(&pc, evm, &ScopeContext{Memory: mem, Stack: stack})
} }
} }
@ -707,7 +707,7 @@ func TestRandom(t *testing.T) {
stack = newstack() stack = newstack()
pc = uint64(0) pc = uint64(0)
) )
opRandom(&pc, evm, &ScopeContext{nil, stack, nil}) opRandom(&pc, evm, &ScopeContext{Stack: stack})
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))
} }
@ -749,7 +749,7 @@ func TestBlobHash(t *testing.T) {
) )
evm.SetTxContext(TxContext{BlobHashes: tt.hashes}) evm.SetTxContext(TxContext{BlobHashes: tt.hashes})
stack.push(uint256.NewInt(tt.idx)) stack.push(uint256.NewInt(tt.idx))
opBlobHash(&pc, evm, &ScopeContext{nil, stack, nil}) opBlobHash(&pc, evm, &ScopeContext{Stack: stack})
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))
} }
@ -889,7 +889,7 @@ func TestOpMCopy(t *testing.T) {
mem.Resize(memorySize) mem.Resize(memorySize)
} }
// Do the copy // Do the copy
opMcopy(&pc, evm, &ScopeContext{mem, stack, nil}) opMcopy(&pc, evm, &ScopeContext{Memory: mem, Stack: stack})
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)
@ -911,7 +911,7 @@ func TestPush(t *testing.T) {
scope := &ScopeContext{ scope := &ScopeContext{
Memory: nil, Memory: nil,
Stack: newstack(), Stack: newstack(),
Contract: &Contract{ Contract: Contract{
Code: code, Code: code,
}, },
} }

View file

@ -35,12 +35,13 @@ type Config struct {
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
} }
// ScopeContext contains the things that are per-call, such as stack and memory, // ScopeContext contains the things that are per-call
// but not transients like pc and gas
type ScopeContext struct { type ScopeContext struct {
pc uint64
Memory *Memory Memory *Memory
Stack *Stack Stack *Stack
Contract *Contract Contract Contract
} }
// MemoryData returns the underlying memory slice. Callers must not modify the contents // MemoryData returns the underlying memory slice. Callers must not modify the contents
@ -94,9 +95,30 @@ func (ctx *ScopeContext) ContractCode() []byte {
// 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 (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return nil, nil
}
// Increment the call depth which is restricted to 1024 // Increment the call depth which is restricted to 1024
prevContext := evm.ScopeContext
evm.ScopeContext = ScopeContext{
pc: 0,
Memory: NewMemory(),
Stack: newstack(),
Contract: *contract,
}
evm.depth++ evm.depth++
defer func() { evm.depth-- }() defer func() {
evm.depth--
*contract = evm.Contract
// Don't move this deferred function, it's placed before the OnOpcode-deferred method,
// so that it gets executed _after_: the OnOpcode needs the stacks before
// they are returned to the pools
returnStack(evm.Stack)
evm.Memory.Free()
evm.ScopeContext = prevContext
}()
// Make sure the readOnly is only set if we aren't in readOnly yet. // Make sure the readOnly is only set if we aren't in readOnly yet.
// This also makes sure that the readOnly flag isn't removed for child calls. // This also makes sure that the readOnly flag isn't removed for child calls.
@ -109,25 +131,12 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// as every returning call will return new data anyway. // as every returning call will return new data anyway.
evm.returnData = nil evm.returnData = nil
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return nil, nil
}
var ( var (
op OpCode // current opcode op OpCode // current opcode
jumpTable *JumpTable = evm.table jumpTable *JumpTable = evm.table
mem = NewMemory() // bound memory
stack = newstack() // local stack
callContext = &ScopeContext{
Memory: mem,
Stack: stack,
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
// to be uint256. Practically much less so feasible. // to be uint256. Practically much less so feasible.
pc = uint64(0) // program counter
cost uint64 cost uint64
// copies used by tracer // copies used by tracer
pcCopy uint64 // needed for the deferred EVMLogger pcCopy uint64 // needed for the deferred EVMLogger
@ -137,14 +146,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
debug = evm.Config.Tracer != nil debug = evm.Config.Tracer != nil
isEIP4762 = evm.chainRules.IsEIP4762 isEIP4762 = evm.chainRules.IsEIP4762
) )
// Don't move this deferred function, it's placed before the OnOpcode-deferred method, evm.Contract.Input = input
// so that it gets executed _after_: the OnOpcode needs the stacks before
// they are returned to the pools
defer func() {
returnStack(stack)
mem.Free()
}()
contract.Input = input
if debug { if debug {
defer func() { // this deferred method handles exit-with-error defer func() { // this deferred method handles exit-with-error
@ -152,10 +154,10 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
return return
} }
if !logged && evm.Config.Tracer.OnOpcode != nil { if !logged && evm.Config.Tracer.OnOpcode != nil {
evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err)) evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, &evm.ScopeContext, evm.returnData, evm.depth, VMErrorFromErr(err))
} }
if logged && evm.Config.Tracer.OnFault != nil { if logged && evm.Config.Tracer.OnFault != nil {
evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, evm.depth, VMErrorFromErr(err)) evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, &evm.ScopeContext, evm.depth, VMErrorFromErr(err))
} }
}() }()
} }
@ -167,14 +169,14 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
for { for {
if debug { if debug {
// Capture pre-execution values for tracing. // Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas logged, pcCopy, gasCopy = false, evm.pc, evm.Contract.Gas
} }
if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall { if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
// if the PC ends up in a new "chunk" of verkleized code, charge the // if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs. // associated costs.
contractAddr := contract.Address() contractAddr := contract.Address()
consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas) consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, evm.pc, 1, uint64(len(contract.Code)), false, contract.Gas)
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
if consumed < wanted { if consumed < wanted {
return nil, ErrOutOfGas return nil, ErrOutOfGas
@ -183,20 +185,20 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// 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 = evm.Contract.GetOp(evm.pc)
operation := jumpTable[op] operation := jumpTable[op]
cost = operation.constantGas // For tracing cost = operation.constantGas // For tracing
// Validate stack // Validate stack
if sLen := stack.len(); sLen < operation.minStack { if sLen := evm.Stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack { } else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
} }
// for tracing: this gas consumption event is emitted below in the debug section. // for tracing: this gas consumption event is emitted below in the debug section.
if contract.Gas < cost { if evm.Contract.Gas < cost {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} else { } else {
contract.Gas -= cost evm.Contract.Gas -= cost
} }
// All ops with a dynamic memory usage also has a dynamic gas cost. // All ops with a dynamic memory usage also has a dynamic gas cost.
@ -207,7 +209,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// Memory check needs to be done prior to evaluating the dynamic gas portion, // Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows // to detect calculation overflows
if operation.memorySize != nil { if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack) memSize, overflow := operation.memorySize(evm.Stack)
if overflow { if overflow {
return nil, ErrGasUintOverflow return nil, ErrGasUintOverflow
} }
@ -220,16 +222,16 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
// Consume the gas and return an error if not enough gas is available. // Consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost // cost is explicitly set so that the capture state defer method can get the proper cost
var dynamicCost uint64 var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize) dynamicCost, err = operation.dynamicGas(evm, &evm.Contract, evm.Stack, evm.Memory, memorySize)
cost += dynamicCost // for tracing cost += dynamicCost // for tracing
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
} }
// for tracing: this gas consumption event is emitted below in the debug section. // for tracing: this gas consumption event is emitted below in the debug section.
if contract.Gas < dynamicCost { if evm.Contract.Gas < dynamicCost {
return nil, ErrOutOfGas return nil, ErrOutOfGas
} else { } else {
contract.Gas -= dynamicCost evm.Contract.Gas -= dynamicCost
} }
} }
@ -239,20 +241,20 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
} }
if evm.Config.Tracer.OnOpcode != nil { if evm.Config.Tracer.OnOpcode != nil {
evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err)) evm.Config.Tracer.OnOpcode(evm.pc, byte(op), gasCopy, cost, &evm.ScopeContext, evm.returnData, evm.depth, VMErrorFromErr(err))
logged = true logged = true
} }
} }
if memorySize > 0 { if memorySize > 0 {
mem.Resize(memorySize) evm.Memory.Resize(memorySize)
} }
// execute the operation // execute the operation
res, err = operation.execute(&pc, evm, callContext) res, err = operation.execute(&evm.pc, evm, &evm.ScopeContext)
if err != nil { if err != nil {
break break
} }
pc++ evm.pc++
} }
if err == errStopToken { if err == errStopToken {

View file

@ -91,6 +91,6 @@ func BenchmarkInterpreter(b *testing.B) {
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529) gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
gasSStoreEIP3529(evm, contract, stack, mem, 1234) gasSStoreEIP3529(evm, &contract, stack, mem, 1234)
} }
} }

View file

@ -65,7 +65,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller()) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), 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 := evm.Run(contract, []byte{}, false) ret, err := evm.Run(&contract, []byte{}, 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)
@ -182,8 +182,9 @@ func TestHaltBetweenSteps(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
contract := vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil)
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), Contract: contract,
} }
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)})
@ -280,8 +281,9 @@ func TestEnterExit(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
contract := vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil)
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), Contract: contract,
} }
tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit(1, []byte{}, 400, nil, false) tracer.OnExit(1, []byte{}, 400, nil, false)

View file

@ -52,7 +52,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(evm.GetVMContext(), nil, common.Address{}) logger.OnTxStart(evm.GetVMContext(), nil, common.Address{})
_, err := evm.Run(contract, []byte{}, false) _, err := evm.Run(&contract, []byte{}, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }