mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
core/vm: generic not-traced EVM implementation
this allows the EVM to follow the not-traced codepaths when it is not configured for tracing and skip the runtime checks
This commit is contained in:
parent
c3ef6c77c2
commit
94fe5c62e4
14 changed files with 260 additions and 238 deletions
|
|
@ -18,7 +18,6 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -125,29 +124,6 @@ func (c *Contract) Caller() common.Address {
|
||||||
return c.caller
|
return c.caller
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
|
||||||
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
|
||||||
if c.Gas < gas {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
|
||||||
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
|
||||||
}
|
|
||||||
c.Gas -= gas
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefundGas refunds gas to the contract
|
|
||||||
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
|
||||||
if gas == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
|
||||||
logger.OnGasChange(c.Gas, c.Gas+gas, reason)
|
|
||||||
}
|
|
||||||
c.Gas += gas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address returns the contracts address
|
// Address returns the contracts address
|
||||||
func (c *Contract) Address() common.Address {
|
func (c *Contract) Address() common.Address {
|
||||||
return c.address
|
return c.address
|
||||||
|
|
|
||||||
|
|
@ -257,12 +257,12 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
// - the returned bytes,
|
// - the returned bytes,
|
||||||
// - the _remaining_ gas,
|
// - the _remaining_ gas,
|
||||||
// - any error that occurred
|
// - any error that occurred
|
||||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
func RunPrecompiledContract[TS TracingSwitch](p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
||||||
gasCost := p.RequiredGas(input)
|
gasCost := p.RequiredGas(input)
|
||||||
if suppliedGas < gasCost {
|
if suppliedGas < gasCost {
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if logger != nil && logger.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && logger.OnGasChange != nil {
|
||||||
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
|
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
|
||||||
}
|
}
|
||||||
suppliedGas -= gasCost
|
suppliedGas -= gasCost
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func FuzzPrecompiledContracts(f *testing.F) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inWant := string(input)
|
inWant := string(input)
|
||||||
RunPrecompiledContract(p, input, gas, nil)
|
RunPrecompiledContract[TracingDisabled](p, input, gas, nil)
|
||||||
if inHave := string(input); inWant != inHave {
|
if inHave := string(input); inWant != inHave {
|
||||||
t.Errorf("Precompiled %v modified input data", a)
|
t.Errorf("Precompiled %v modified input data", a)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
||||||
in := common.Hex2Bytes(test.Input)
|
in := common.Hex2Bytes(test.Input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
||||||
if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil {
|
if res, _, err := RunPrecompiledContract[TracingDisabled](p, in, gas, nil); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if common.Bytes2Hex(res) != test.Expected {
|
} else if common.Bytes2Hex(res) != test.Expected {
|
||||||
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
|
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
|
||||||
|
|
@ -121,7 +121,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
|
||||||
gas := p.RequiredGas(in) - 1
|
gas := p.RequiredGas(in) - 1
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
||||||
_, _, err := RunPrecompiledContract(p, in, gas, nil)
|
_, _, err := RunPrecompiledContract[TracingDisabled](p, in, gas, nil)
|
||||||
if err.Error() != "out of gas" {
|
if err.Error() != "out of gas" {
|
||||||
t.Errorf("Expected error [out of gas], got [%v]", err)
|
t.Errorf("Expected error [out of gas], got [%v]", err)
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
|
||||||
in := common.Hex2Bytes(test.Input)
|
in := common.Hex2Bytes(test.Input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
_, _, err := RunPrecompiledContract(p, in, gas, nil)
|
_, _, err := RunPrecompiledContract[TracingDisabled](p, in, gas, nil)
|
||||||
if err.Error() != test.ExpectedError {
|
if err.Error() != test.ExpectedError {
|
||||||
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
|
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +170,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
|
||||||
bench.ResetTimer()
|
bench.ResetTimer()
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
copy(data, in)
|
copy(data, in)
|
||||||
res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
|
res, _, err = RunPrecompiledContract[TracingDisabled](p, data, reqGas, nil)
|
||||||
}
|
}
|
||||||
bench.StopTimer()
|
bench.StopTimer()
|
||||||
elapsed := uint64(time.Since(start))
|
elapsed := uint64(time.Since(start))
|
||||||
|
|
|
||||||
|
|
@ -27,32 +27,36 @@ import (
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var activators = map[int]func(*JumpTable){
|
var activators = map[int][2]func(*JumpTable){
|
||||||
5656: enable5656,
|
5656: {enable5656},
|
||||||
6780: enable6780,
|
6780: {enable6780},
|
||||||
3855: enable3855,
|
3855: {enable3855},
|
||||||
3860: enable3860,
|
3860: {enable3860},
|
||||||
3529: enable3529,
|
3529: {enable3529},
|
||||||
3198: enable3198,
|
3198: {enable3198},
|
||||||
2929: enable2929,
|
2929: {enable2929[TracingEnabled], enable2929[TracingDisabled]},
|
||||||
2200: enable2200,
|
2200: {enable2200},
|
||||||
1884: enable1884,
|
1884: {enable1884},
|
||||||
1344: enable1344,
|
1344: {enable1344},
|
||||||
1153: enable1153,
|
1153: {enable1153},
|
||||||
4762: enable4762,
|
4762: {enable4762[TracingEnabled], enable4762[TracingDisabled]},
|
||||||
7702: enable7702,
|
7702: {enable7702[TracingEnabled], enable7702[TracingDisabled]},
|
||||||
7939: enable7939,
|
7939: {enable7939},
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableEIP enables the given EIP on the config.
|
// EnableEIP enables the given EIP on the config.
|
||||||
// This operation writes in-place, and callers need to ensure that the globally
|
// This operation writes in-place, and callers need to ensure that the globally
|
||||||
// defined jump tables are not polluted.
|
// defined jump tables are not polluted.
|
||||||
func EnableEIP(eipNum int, jt *JumpTable) error {
|
func EnableEIP(eipNum int, jt *JumpTable, traced bool) error {
|
||||||
enablerFn, ok := activators[eipNum]
|
enablerFns, ok := activators[eipNum]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("undefined eip %d", eipNum)
|
return fmt.Errorf("undefined eip %d", eipNum)
|
||||||
}
|
}
|
||||||
enablerFn(jt)
|
if traced || enablerFns[1] == nil {
|
||||||
|
enablerFns[0](jt)
|
||||||
|
} else {
|
||||||
|
enablerFns[1](jt)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +126,7 @@ func enable2200(jt *JumpTable) {
|
||||||
|
|
||||||
// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
|
// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
|
||||||
// https://eips.ethereum.org/EIPS/eip-2929
|
// https://eips.ethereum.org/EIPS/eip-2929
|
||||||
func enable2929(jt *JumpTable) {
|
func enable2929[TS TracingSwitch](jt *JumpTable) {
|
||||||
jt[SSTORE].dynamicGas = gasSStoreEIP2929
|
jt[SSTORE].dynamicGas = gasSStoreEIP2929
|
||||||
|
|
||||||
jt[SLOAD].constantGas = 0
|
jt[SLOAD].constantGas = 0
|
||||||
|
|
@ -141,16 +145,16 @@ func enable2929(jt *JumpTable) {
|
||||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[CALL].dynamicGas = gasCallEIP2929
|
jt[CALL].dynamicGas = makeCallVariantGasCallEIP2929[TS](gasCall, 1)
|
||||||
|
|
||||||
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
jt[CALLCODE].dynamicGas = makeCallVariantGasCallEIP2929[TS](gasCallCode, 1)
|
||||||
|
|
||||||
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
jt[STATICCALL].dynamicGas = makeCallVariantGasCallEIP2929[TS](gasStaticCall, 1)
|
||||||
|
|
||||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
jt[DELEGATECALL].dynamicGas = makeCallVariantGasCallEIP2929[TS](gasDelegateCall, 1)
|
||||||
|
|
||||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
||||||
// factor here
|
// factor here
|
||||||
|
|
@ -342,7 +346,7 @@ func enable6780(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opExtCodeCopyEIP4762[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
stack = scope.Stack
|
stack = scope.Stack
|
||||||
a = stack.pop()
|
a = stack.pop()
|
||||||
|
|
@ -358,7 +362,7 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
||||||
code := evm.StateDB.GetCode(addr)
|
code := evm.StateDB.GetCode(addr)
|
||||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
useGas[TS](scope.Contract, consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -370,7 +374,7 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
||||||
// opPush1EIP4762 handles the special case of PUSH1 opcode for EIP-4762, which
|
// opPush1EIP4762 handles the special case of PUSH1 opcode for EIP-4762, which
|
||||||
// need not worry about the adjusted bound logic when adding the PUSHDATA to
|
// need not worry about the adjusted bound logic when adding the PUSHDATA to
|
||||||
// the list of access events.
|
// the list of access events.
|
||||||
func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPush1EIP4762[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
codeLen = uint64(len(scope.Contract.Code))
|
codeLen = uint64(len(scope.Contract.Code))
|
||||||
integer = new(uint256.Int)
|
integer = new(uint256.Int)
|
||||||
|
|
@ -384,7 +388,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// advanced past this boundary.
|
// advanced past this boundary.
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
scope.Contract.UseGas(wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
useGas[TS](scope.Contract, wanted, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -395,7 +399,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
func makePushEIP4762[TS TracingSwitch](size uint64, pushByteSize int) executionFunc {
|
||||||
return func(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
return func(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
codeLen = len(scope.Contract.Code)
|
codeLen = len(scope.Contract.Code)
|
||||||
|
|
@ -412,7 +416,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
scope.Contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
useGas[TS](scope.Contract, consumed, evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +427,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enable4762(jt *JumpTable) {
|
func enable4762[TS TracingSwitch](jt *JumpTable) {
|
||||||
jt[SSTORE] = &operation{
|
jt[SSTORE] = &operation{
|
||||||
dynamicGas: gasSStore4762,
|
dynamicGas: gasSStore4762,
|
||||||
execute: opSstore,
|
execute: opSstore,
|
||||||
|
|
@ -459,7 +463,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[EXTCODECOPY] = &operation{
|
jt[EXTCODECOPY] = &operation{
|
||||||
execute: opExtCodeCopyEIP4762,
|
execute: opExtCodeCopyEIP4762[TS],
|
||||||
dynamicGas: gasExtCodeCopyEIP4762,
|
dynamicGas: gasExtCodeCopyEIP4762,
|
||||||
minStack: minStack(4, 0),
|
minStack: minStack(4, 0),
|
||||||
maxStack: maxStack(4, 0),
|
maxStack: maxStack(4, 0),
|
||||||
|
|
@ -484,7 +488,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[CREATE] = &operation{
|
jt[CREATE] = &operation{
|
||||||
execute: opCreate,
|
execute: opCreate[TS],
|
||||||
constantGas: params.CreateNGasEip4762,
|
constantGas: params.CreateNGasEip4762,
|
||||||
dynamicGas: gasCreateEip3860,
|
dynamicGas: gasCreateEip3860,
|
||||||
minStack: minStack(3, 1),
|
minStack: minStack(3, 1),
|
||||||
|
|
@ -493,7 +497,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[CREATE2] = &operation{
|
jt[CREATE2] = &operation{
|
||||||
execute: opCreate2,
|
execute: opCreate2[TS],
|
||||||
constantGas: params.CreateNGasEip4762,
|
constantGas: params.CreateNGasEip4762,
|
||||||
dynamicGas: gasCreate2Eip3860,
|
dynamicGas: gasCreate2Eip3860,
|
||||||
minStack: minStack(4, 1),
|
minStack: minStack(4, 1),
|
||||||
|
|
@ -502,7 +506,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[CALL] = &operation{
|
jt[CALL] = &operation{
|
||||||
execute: opCall,
|
execute: opCall[TS],
|
||||||
dynamicGas: gasCallEIP4762,
|
dynamicGas: gasCallEIP4762,
|
||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
maxStack: maxStack(7, 1),
|
maxStack: maxStack(7, 1),
|
||||||
|
|
@ -510,7 +514,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[CALLCODE] = &operation{
|
jt[CALLCODE] = &operation{
|
||||||
execute: opCallCode,
|
execute: opCallCode[TS],
|
||||||
dynamicGas: gasCallCodeEIP4762,
|
dynamicGas: gasCallCodeEIP4762,
|
||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
maxStack: maxStack(7, 1),
|
maxStack: maxStack(7, 1),
|
||||||
|
|
@ -518,7 +522,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[STATICCALL] = &operation{
|
jt[STATICCALL] = &operation{
|
||||||
execute: opStaticCall,
|
execute: opStaticCall[TS],
|
||||||
dynamicGas: gasStaticCallEIP4762,
|
dynamicGas: gasStaticCallEIP4762,
|
||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
|
|
@ -526,7 +530,7 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[DELEGATECALL] = &operation{
|
jt[DELEGATECALL] = &operation{
|
||||||
execute: opDelegateCall,
|
execute: opDelegateCall[TS],
|
||||||
dynamicGas: gasDelegateCallEIP4762,
|
dynamicGas: gasDelegateCallEIP4762,
|
||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
maxStack: maxStack(6, 1),
|
maxStack: maxStack(6, 1),
|
||||||
|
|
@ -534,14 +538,14 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
jt[PUSH1] = &operation{
|
jt[PUSH1] = &operation{
|
||||||
execute: opPush1EIP4762,
|
execute: opPush1EIP4762[TS],
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minStack(0, 1),
|
minStack: minStack(0, 1),
|
||||||
maxStack: maxStack(0, 1),
|
maxStack: maxStack(0, 1),
|
||||||
}
|
}
|
||||||
for i := 1; i < 32; i++ {
|
for i := 1; i < 32; i++ {
|
||||||
jt[PUSH1+OpCode(i)] = &operation{
|
jt[PUSH1+OpCode(i)] = &operation{
|
||||||
execute: makePushEIP4762(uint64(i+1), i+1),
|
execute: makePushEIP4762[TS](uint64(i+1), i+1),
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
minStack: minStack(0, 1),
|
minStack: minStack(0, 1),
|
||||||
maxStack: maxStack(0, 1),
|
maxStack: maxStack(0, 1),
|
||||||
|
|
@ -550,9 +554,9 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable7702 the EIP-7702 changes to support delegation designators.
|
// enable7702 the EIP-7702 changes to support delegation designators.
|
||||||
func enable7702(jt *JumpTable) {
|
func enable7702[TS TracingSwitch](jt *JumpTable) {
|
||||||
jt[CALL].dynamicGas = gasCallEIP7702
|
jt[CALL].dynamicGas = makeCallVariantGasCallEIP7702[TS](gasCall)
|
||||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP7702
|
jt[CALLCODE].dynamicGas = makeCallVariantGasCallEIP7702[TS](gasCallCode)
|
||||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
|
jt[STATICCALL].dynamicGas = makeCallVariantGasCallEIP7702[TS](gasStaticCall)
|
||||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
|
jt[DELEGATECALL].dynamicGas = makeCallVariantGasCallEIP7702[TS](gasDelegateCall)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
114
core/vm/evm.go
114
core/vm/evm.go
|
|
@ -148,38 +148,44 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
|
||||||
}
|
}
|
||||||
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
||||||
|
|
||||||
|
var table, tableNT *JumpTable
|
||||||
switch {
|
switch {
|
||||||
case evm.chainRules.IsOsaka:
|
case evm.chainRules.IsOsaka:
|
||||||
evm.table = &osakaInstructionSet
|
table, tableNT = &osakaInstructionSet, &osakaInstructionSetNT
|
||||||
case evm.chainRules.IsVerkle:
|
case evm.chainRules.IsVerkle:
|
||||||
// TODO replace with proper instruction set when fork is specified
|
// TODO replace with proper instruction set when fork is specified
|
||||||
evm.table = &verkleInstructionSet
|
table, tableNT = &verkleInstructionSet, &verkleInstructionSetNT
|
||||||
case evm.chainRules.IsPrague:
|
case evm.chainRules.IsPrague:
|
||||||
evm.table = &pragueInstructionSet
|
table, tableNT = &pragueInstructionSet, &pragueInstructionSetNT
|
||||||
case evm.chainRules.IsCancun:
|
case evm.chainRules.IsCancun:
|
||||||
evm.table = &cancunInstructionSet
|
table, tableNT = &cancunInstructionSet, &cancunInstructionSetNT
|
||||||
case evm.chainRules.IsShanghai:
|
case evm.chainRules.IsShanghai:
|
||||||
evm.table = &shanghaiInstructionSet
|
table, tableNT = &shanghaiInstructionSet, &shanghaiInstructionSetNT
|
||||||
case evm.chainRules.IsMerge:
|
case evm.chainRules.IsMerge:
|
||||||
evm.table = &mergeInstructionSet
|
table, tableNT = &mergeInstructionSet, &mergeInstructionSetNT
|
||||||
case evm.chainRules.IsLondon:
|
case evm.chainRules.IsLondon:
|
||||||
evm.table = &londonInstructionSet
|
table, tableNT = &londonInstructionSet, &londonInstructionSetNT
|
||||||
case evm.chainRules.IsBerlin:
|
case evm.chainRules.IsBerlin:
|
||||||
evm.table = &berlinInstructionSet
|
table, tableNT = &berlinInstructionSet, &berlinInstructionSetNT
|
||||||
case evm.chainRules.IsIstanbul:
|
case evm.chainRules.IsIstanbul:
|
||||||
evm.table = &istanbulInstructionSet
|
table, tableNT = &istanbulInstructionSet, &istanbulInstructionSetNT
|
||||||
case evm.chainRules.IsConstantinople:
|
case evm.chainRules.IsConstantinople:
|
||||||
evm.table = &constantinopleInstructionSet
|
table, tableNT = &constantinopleInstructionSet, &constantinopleInstructionSetNT
|
||||||
case evm.chainRules.IsByzantium:
|
case evm.chainRules.IsByzantium:
|
||||||
evm.table = &byzantiumInstructionSet
|
table, tableNT = &byzantiumInstructionSet, &byzantiumInstructionSetNT
|
||||||
case evm.chainRules.IsEIP158:
|
case evm.chainRules.IsEIP158:
|
||||||
evm.table = &spuriousDragonInstructionSet
|
table, tableNT = &spuriousDragonInstructionSet, &spuriousDragonInstructionSetNT
|
||||||
case evm.chainRules.IsEIP150:
|
case evm.chainRules.IsEIP150:
|
||||||
evm.table = &tangerineWhistleInstructionSet
|
table, tableNT = &tangerineWhistleInstructionSet, &tangerineWhistleInstructionSetNT
|
||||||
case evm.chainRules.IsHomestead:
|
case evm.chainRules.IsHomestead:
|
||||||
evm.table = &homesteadInstructionSet
|
table, tableNT = &homesteadInstructionSet, &homesteadInstructionSetNT
|
||||||
default:
|
default:
|
||||||
evm.table = &frontierInstructionSet
|
table, tableNT = &frontierInstructionSet, &frontierInstructionSetNT
|
||||||
|
}
|
||||||
|
if config.Tracer != nil {
|
||||||
|
evm.table = table
|
||||||
|
} else {
|
||||||
|
evm.table = tableNT
|
||||||
}
|
}
|
||||||
var extraEips []int
|
var extraEips []int
|
||||||
if len(evm.Config.ExtraEips) > 0 {
|
if len(evm.Config.ExtraEips) > 0 {
|
||||||
|
|
@ -187,7 +193,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
|
||||||
evm.table = copyJumpTable(evm.table)
|
evm.table = copyJumpTable(evm.table)
|
||||||
}
|
}
|
||||||
for _, eip := range evm.Config.ExtraEips {
|
for _, eip := range evm.Config.ExtraEips {
|
||||||
if err := EnableEIP(eip, evm.table); err != nil {
|
if err := EnableEIP(eip, evm.table, evm.Config.Tracer != nil); err != nil {
|
||||||
// Disable it, so caller can check if it's activated or not
|
// Disable it, so caller can check if it's activated or not
|
||||||
log.Error("EIP activation failed", "eip", eip, "error", err)
|
log.Error("EIP activation failed", "eip", eip, "error", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -239,8 +245,15 @@ func isSystemCall(caller common.Address) bool {
|
||||||
// the necessary steps to create accounts and reverses the state in case of an
|
// the necessary steps to create accounts and reverses the state in case of an
|
||||||
// execution error or failed value transfer.
|
// execution error or failed value transfer.
|
||||||
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
// Capture the tracer start/end events in debug mode
|
|
||||||
if evm.Config.Tracer != nil {
|
if evm.Config.Tracer != nil {
|
||||||
|
return call[TracingEnabled](evm, caller, addr, input, gas, value)
|
||||||
|
}
|
||||||
|
return call[TracingDisabled](evm, caller, addr, input, gas, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func call[TS TracingSwitch](evm *EVM, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
// Capture the tracer start/end events in debug mode
|
||||||
|
if tracingIsEnabled[TS]() {
|
||||||
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
|
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
|
@ -283,7 +296,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
evm.Context.Transfer(evm.StateDB, caller, addr, value)
|
evm.Context.Transfer(evm.StateDB, caller, addr, value)
|
||||||
|
|
||||||
if isPrecompile {
|
if isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
ret, gas, err = RunPrecompiledContract[TS](p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
code := evm.resolveCode(addr)
|
code := evm.resolveCode(addr)
|
||||||
|
|
@ -294,7 +307,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 = Run[TS](evm, contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +317,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,9 +337,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
//
|
//
|
||||||
// CallCode differs from Call in the sense that it executes the given address'
|
// CallCode differs from Call in the sense that it executes the given address'
|
||||||
// code with the caller as context.
|
// code with the caller as context.
|
||||||
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
func callCode[TS TracingSwitch](evm *EVM, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.Config.Tracer != nil {
|
if tracingIsEnabled[TS]() {
|
||||||
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
|
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
|
@ -347,19 +360,19 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
ret, gas, err = RunPrecompiledContract[TS](p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// 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 = Run[TS](evm, contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas = 0
|
gas = 0
|
||||||
|
|
@ -373,9 +386,9 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
//
|
//
|
||||||
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
||||||
// code with the caller as context and the caller is set to the caller of the caller.
|
// code with the caller as context and the caller is set to the caller of the caller.
|
||||||
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
func delegateCall[TS TracingSwitch](evm *EVM, originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.Config.Tracer != nil {
|
if tracingIsEnabled[TS]() {
|
||||||
// DELEGATECALL inherits value from parent call
|
// DELEGATECALL inherits value from parent call
|
||||||
evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig())
|
evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
|
|
@ -390,20 +403,20 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
ret, gas, err = RunPrecompiledContract[TS](p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and make initialise the delegate values
|
// Initialise a new contract and make initialise the delegate values
|
||||||
//
|
//
|
||||||
// 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 = Run[TS](evm, contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas = 0
|
gas = 0
|
||||||
|
|
@ -416,9 +429,9 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
// as parameters while disallowing any modifications to the state during the call.
|
// as parameters while disallowing any modifications to the state during the call.
|
||||||
// Opcodes that attempt to perform such modifications will result in exceptions
|
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||||
// instead of performing the modifications.
|
// instead of performing the modifications.
|
||||||
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
func staticCall[TS TracingSwitch](evm *EVM, caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.Config.Tracer != nil {
|
if tracingIsEnabled[TS]() {
|
||||||
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
|
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
|
@ -442,7 +455,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
||||||
|
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
ret, gas, err = RunPrecompiledContract[TS](p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
|
|
@ -452,13 +465,13 @@ 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 = Run[TS](evm, contract, input, true)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,8 +482,8 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
}
|
}
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
func create[TS TracingSwitch](evm *EVM, caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
||||||
if evm.Config.Tracer != nil {
|
if tracingIsEnabled[TS]() {
|
||||||
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
|
@ -496,7 +509,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
if statelessGas > gas {
|
if statelessGas > gas {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
|
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
|
||||||
}
|
}
|
||||||
gas = gas - statelessGas
|
gas = gas - statelessGas
|
||||||
|
|
@ -517,7 +530,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
if evm.StateDB.GetNonce(address) != 0 ||
|
if evm.StateDB.GetNonce(address) != 0 ||
|
||||||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||||
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||||
|
|
@ -544,7 +557,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if tracingIsEnabled[TS]() && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
||||||
}
|
}
|
||||||
gas = gas - consumed
|
gas = gas - consumed
|
||||||
|
|
@ -560,11 +573,11 @@ 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 = initNewContract[TS](evm, 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 {
|
||||||
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
useGas[TS](contract, contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret, address, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
|
|
@ -572,8 +585,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
|
|
||||||
// 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) ([]byte, error) {
|
func initNewContract[TS TracingSwitch](evm *EVM, contract *Contract, address common.Address) ([]byte, error) {
|
||||||
ret, err := evm.Run(contract, nil, false)
|
ret, err := Run[TS](evm, contract, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
@ -590,12 +603,12 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
||||||
|
|
||||||
if !evm.chainRules.IsEIP4762 {
|
if !evm.chainRules.IsEIP4762 {
|
||||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||||
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
if !useGas[TS](contract, createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
useGas[TS](contract, consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
if len(ret) > 0 && (consumed < wanted) {
|
if len(ret) > 0 && (consumed < wanted) {
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -608,17 +621,20 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
||||||
// Create creates a new contract using code as deployment code.
|
// Create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
|
contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
|
||||||
return evm.create(caller, code, gas, value, contractAddr, CREATE)
|
if evm.Config.Tracer != nil {
|
||||||
|
return create[TracingEnabled](evm, caller, code, gas, value, contractAddr, CREATE)
|
||||||
|
}
|
||||||
|
return create[TracingDisabled](evm, caller, code, gas, value, contractAddr, CREATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create2 creates a new contract using code as deployment code.
|
// Create2 creates a new contract using code as deployment code.
|
||||||
//
|
//
|
||||||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func create2[TS TracingSwitch](evm *EVM, caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
inithash := crypto.HashData(evm.hasher, code)
|
inithash := crypto.HashData(evm.hasher, code)
|
||||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
return create[TS](evm, caller, code, gas, endowment, contractAddr, CREATE2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveCode returns the code associated with the provided account. After
|
// resolveCode returns the code associated with the provided account. After
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -652,7 +653,7 @@ func opSwap16(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCreate[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if evm.readOnly {
|
if evm.readOnly {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
}
|
}
|
||||||
|
|
@ -669,9 +670,11 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
|
|
||||||
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
useGas[TS](scope.Contract, gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||||
|
|
||||||
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, gas, &value)
|
caller := scope.Contract.Address()
|
||||||
|
contractAddr := crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
|
||||||
|
res, addr, returnGas, suberr := create[TS](evm, caller, input, gas, &value, contractAddr, CREATE)
|
||||||
// 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
|
||||||
|
|
@ -685,7 +688,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
evm.returnData = res // set REVERT data to return data buffer
|
evm.returnData = res // set REVERT data to return data buffer
|
||||||
|
|
@ -695,7 +698,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCreate2[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if evm.readOnly {
|
if evm.readOnly {
|
||||||
return nil, ErrWriteProtection
|
return nil, ErrWriteProtection
|
||||||
}
|
}
|
||||||
|
|
@ -709,10 +712,10 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
|
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
useGas[TS](scope.Contract, gas, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, gas,
|
res, addr, returnGas, suberr := create2[TS](evm, scope.Contract.Address(), input, gas,
|
||||||
&endowment, &salt)
|
&endowment, &salt)
|
||||||
// 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 {
|
||||||
|
|
@ -721,7 +724,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
stackvalue.SetBytes(addr.Bytes())
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
evm.returnData = res // set REVERT data to return data buffer
|
evm.returnData = res // set REVERT data to return data buffer
|
||||||
|
|
@ -731,7 +734,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCall[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
stack := scope.Stack
|
stack := scope.Stack
|
||||||
// Pop gas. The actual gas in evm.callGasTemp.
|
// Pop gas. The actual gas in evm.callGasTemp.
|
||||||
// We can use this as a temporary value
|
// We can use this as a temporary value
|
||||||
|
|
@ -749,7 +752,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
gas += params.CallStipend
|
||||||
}
|
}
|
||||||
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, gas, &value)
|
ret, returnGas, err := call[TS](evm, scope.Contract.Address(), toAddr, args, gas, &value)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
|
|
@ -761,13 +764,13 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCallCode[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||||
stack := scope.Stack
|
stack := scope.Stack
|
||||||
// We use it as a temporary value
|
// We use it as a temporary value
|
||||||
|
|
@ -783,7 +786,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
gas += params.CallStipend
|
gas += params.CallStipend
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value)
|
ret, returnGas, err := callCode[TS](evm, scope.Contract.Address(), toAddr, args, gas, &value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -794,13 +797,13 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opDelegateCall[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
stack := scope.Stack
|
stack := scope.Stack
|
||||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||||
// We use it as a temporary value
|
// We use it as a temporary value
|
||||||
|
|
@ -812,7 +815,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value)
|
ret, returnGas, err := delegateCall[TS](evm, scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, gas, scope.Contract.value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -823,13 +826,13 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opStaticCall[TS TracingSwitch](pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Pop gas. The actual gas is in evm.callGasTemp.
|
// Pop gas. The actual gas is in evm.callGasTemp.
|
||||||
stack := scope.Stack
|
stack := scope.Stack
|
||||||
// We use it as a temporary value
|
// We use it as a temporary value
|
||||||
|
|
@ -841,7 +844,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
|
||||||
|
|
||||||
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, gas)
|
ret, returnGas, err := staticCall[TS](evm, scope.Contract.Address(), toAddr, args, gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
temp.Clear()
|
temp.Clear()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -852,7 +855,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
refundGas[TS](scope.Contract, returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -25,6 +26,17 @@ import (
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TracingEnabled uint8
|
||||||
|
type TracingDisabled uint16
|
||||||
|
|
||||||
|
type TracingSwitch interface {
|
||||||
|
TracingDisabled | TracingEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func tracingIsEnabled[TS TracingSwitch]() bool {
|
||||||
|
return unsafe.Sizeof(*new(TS)) == 1
|
||||||
|
}
|
||||||
|
|
||||||
// Config are the configuration options for the Interpreter
|
// Config are the configuration options for the Interpreter
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Tracer *tracing.Hooks
|
Tracer *tracing.Hooks
|
||||||
|
|
@ -93,7 +105,7 @@ func (ctx *ScopeContext) ContractCode() []byte {
|
||||||
// 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 (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
func Run[TS TracingSwitch](evm *EVM, contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
|
||||||
// Increment the call depth which is restricted to 1024
|
// Increment the call depth which is restricted to 1024
|
||||||
evm.depth++
|
evm.depth++
|
||||||
defer func() { evm.depth-- }()
|
defer func() { evm.depth-- }()
|
||||||
|
|
@ -134,7 +146,6 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
gasCopy uint64 // for EVMLogger to log gas remaining before execution
|
gasCopy uint64 // for EVMLogger to log gas remaining before execution
|
||||||
logged bool // deferred EVMLogger should ignore already logged steps
|
logged bool // deferred EVMLogger should ignore already logged steps
|
||||||
res []byte // result of the opcode execution function
|
res []byte // result of the opcode execution function
|
||||||
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,
|
// Don't move this deferred function, it's placed before the OnOpcode-deferred method,
|
||||||
|
|
@ -146,7 +157,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
}()
|
}()
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
if debug {
|
if tracingIsEnabled[TS]() {
|
||||||
defer func() { // this deferred method handles exit-with-error
|
defer func() { // this deferred method handles exit-with-error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
|
|
@ -165,7 +176,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
// parent context.
|
// parent context.
|
||||||
_ = jumpTable[0] // nil-check the jumpTable out of the loop
|
_ = jumpTable[0] // nil-check the jumpTable out of the loop
|
||||||
for {
|
for {
|
||||||
if debug {
|
if tracingIsEnabled[TS]() {
|
||||||
// Capture pre-execution values for tracing.
|
// Capture pre-execution values for tracing.
|
||||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +186,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
// 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, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||||
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
useGas[TS](contract, consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -234,7 +245,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do tracing before potential memory expansion
|
// Do tracing before potential memory expansion
|
||||||
if debug {
|
if tracingIsEnabled[TS]() {
|
||||||
if evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
||||||
}
|
}
|
||||||
|
|
@ -261,3 +272,26 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// useGas attempts the use gas and subtracts it and returns true on success
|
||||||
|
func useGas[TS TracingSwitch](c *Contract, gas uint64, hooks *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||||
|
if c.Gas < gas {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if tracingIsEnabled[TS]() && hooks.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||||
|
hooks.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||||
|
}
|
||||||
|
c.Gas -= gas
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// refundGas refunds gas to the contract
|
||||||
|
func refundGas[TS TracingSwitch](c *Contract, gas uint64, hooks *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
|
if gas == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tracingIsEnabled[TS]() && hooks.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||||
|
hooks.OnGasChange(c.Gas, c.Gas+gas, reason)
|
||||||
|
}
|
||||||
|
c.Gas += gas
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,21 +48,21 @@ type operation struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
frontierInstructionSet = newFrontierInstructionSet()
|
frontierInstructionSet, frontierInstructionSetNT = newFrontierInstructionSet[TracingEnabled](), newFrontierInstructionSet[TracingDisabled]()
|
||||||
homesteadInstructionSet = newHomesteadInstructionSet()
|
homesteadInstructionSet, homesteadInstructionSetNT = newHomesteadInstructionSet[TracingEnabled](), newHomesteadInstructionSet[TracingDisabled]()
|
||||||
tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
|
tangerineWhistleInstructionSet, tangerineWhistleInstructionSetNT = newTangerineWhistleInstructionSet[TracingEnabled](), newTangerineWhistleInstructionSet[TracingDisabled]()
|
||||||
spuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
|
spuriousDragonInstructionSet, spuriousDragonInstructionSetNT = newSpuriousDragonInstructionSet[TracingEnabled](), newSpuriousDragonInstructionSet[TracingDisabled]()
|
||||||
byzantiumInstructionSet = newByzantiumInstructionSet()
|
byzantiumInstructionSet, byzantiumInstructionSetNT = newByzantiumInstructionSet[TracingEnabled](), newByzantiumInstructionSet[TracingDisabled]()
|
||||||
constantinopleInstructionSet = newConstantinopleInstructionSet()
|
constantinopleInstructionSet, constantinopleInstructionSetNT = newConstantinopleInstructionSet[TracingEnabled](), newConstantinopleInstructionSet[TracingDisabled]()
|
||||||
istanbulInstructionSet = newIstanbulInstructionSet()
|
istanbulInstructionSet, istanbulInstructionSetNT = newIstanbulInstructionSet[TracingEnabled](), newIstanbulInstructionSet[TracingDisabled]()
|
||||||
berlinInstructionSet = newBerlinInstructionSet()
|
berlinInstructionSet, berlinInstructionSetNT = newBerlinInstructionSet[TracingEnabled](), newBerlinInstructionSet[TracingDisabled]()
|
||||||
londonInstructionSet = newLondonInstructionSet()
|
londonInstructionSet, londonInstructionSetNT = newLondonInstructionSet[TracingEnabled](), newLondonInstructionSet[TracingDisabled]()
|
||||||
mergeInstructionSet = newMergeInstructionSet()
|
mergeInstructionSet, mergeInstructionSetNT = newMergeInstructionSet[TracingEnabled](), newMergeInstructionSet[TracingDisabled]()
|
||||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
shanghaiInstructionSet, shanghaiInstructionSetNT = newShanghaiInstructionSet[TracingEnabled](), newShanghaiInstructionSet[TracingDisabled]()
|
||||||
cancunInstructionSet = newCancunInstructionSet()
|
cancunInstructionSet, cancunInstructionSetNT = newCancunInstructionSet[TracingEnabled](), newCancunInstructionSet[TracingDisabled]()
|
||||||
verkleInstructionSet = newVerkleInstructionSet()
|
verkleInstructionSet, verkleInstructionSetNT = newVerkleInstructionSet[TracingEnabled](), newVerkleInstructionSet[TracingDisabled]()
|
||||||
pragueInstructionSet = newPragueInstructionSet()
|
pragueInstructionSet, pragueInstructionSetNT = newPragueInstructionSet[TracingEnabled](), newPragueInstructionSet[TracingDisabled]()
|
||||||
osakaInstructionSet = newOsakaInstructionSet()
|
osakaInstructionSet, osakaInstructionSetNT = newOsakaInstructionSet[TracingEnabled](), newOsakaInstructionSet[TracingDisabled]()
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
|
|
@ -86,26 +86,26 @@ func validate(jt JumpTable) JumpTable {
|
||||||
return jt
|
return jt
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVerkleInstructionSet() JumpTable {
|
func newVerkleInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newShanghaiInstructionSet()
|
instructionSet := newShanghaiInstructionSet[TS]()
|
||||||
enable4762(&instructionSet)
|
enable4762[TS](&instructionSet)
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOsakaInstructionSet() JumpTable {
|
func newOsakaInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newPragueInstructionSet()
|
instructionSet := newPragueInstructionSet[TS]()
|
||||||
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
|
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPragueInstructionSet() JumpTable {
|
func newPragueInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newCancunInstructionSet()
|
instructionSet := newCancunInstructionSet[TS]()
|
||||||
enable7702(&instructionSet) // EIP-7702 Setcode transaction type
|
enable7702[TS](&instructionSet) // EIP-7702 Setcode transaction type
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCancunInstructionSet() JumpTable {
|
func newCancunInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newShanghaiInstructionSet()
|
instructionSet := newShanghaiInstructionSet[TS]()
|
||||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
||||||
enable7516(&instructionSet) // EIP-7516 (BLOBBASEFEE opcode)
|
enable7516(&instructionSet) // EIP-7516 (BLOBBASEFEE opcode)
|
||||||
enable1153(&instructionSet) // EIP-1153 "Transient Storage"
|
enable1153(&instructionSet) // EIP-1153 "Transient Storage"
|
||||||
|
|
@ -114,16 +114,16 @@ func newCancunInstructionSet() JumpTable {
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShanghaiInstructionSet() JumpTable {
|
func newShanghaiInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newMergeInstructionSet()
|
instructionSet := newMergeInstructionSet[TS]()
|
||||||
enable3855(&instructionSet) // PUSH0 instruction
|
enable3855(&instructionSet) // PUSH0 instruction
|
||||||
enable3860(&instructionSet) // Limit and meter initcode
|
enable3860(&instructionSet) // Limit and meter initcode
|
||||||
|
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMergeInstructionSet() JumpTable {
|
func newMergeInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newLondonInstructionSet()
|
instructionSet := newLondonInstructionSet[TS]()
|
||||||
instructionSet[PREVRANDAO] = &operation{
|
instructionSet[PREVRANDAO] = &operation{
|
||||||
execute: opRandom,
|
execute: opRandom,
|
||||||
constantGas: GasQuickStep,
|
constantGas: GasQuickStep,
|
||||||
|
|
@ -135,8 +135,8 @@ func newMergeInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// constantinople, istanbul, petersburg, berlin and london instructions.
|
// constantinople, istanbul, petersburg, berlin and london instructions.
|
||||||
func newLondonInstructionSet() JumpTable {
|
func newLondonInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newBerlinInstructionSet()
|
instructionSet := newBerlinInstructionSet[TS]()
|
||||||
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
||||||
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
|
|
@ -144,16 +144,16 @@ func newLondonInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// constantinople, istanbul, petersburg and berlin instructions.
|
// constantinople, istanbul, petersburg and berlin instructions.
|
||||||
func newBerlinInstructionSet() JumpTable {
|
func newBerlinInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newIstanbulInstructionSet()
|
instructionSet := newIstanbulInstructionSet[TS]()
|
||||||
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
|
enable2929[TS](&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// constantinople, istanbul and petersburg instructions.
|
// constantinople, istanbul and petersburg instructions.
|
||||||
func newIstanbulInstructionSet() JumpTable {
|
func newIstanbulInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newConstantinopleInstructionSet()
|
instructionSet := newConstantinopleInstructionSet[TS]()
|
||||||
|
|
||||||
enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344
|
enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344
|
||||||
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
|
||||||
|
|
@ -164,8 +164,8 @@ func newIstanbulInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newConstantinopleInstructionSet returns the frontier, homestead,
|
// newConstantinopleInstructionSet returns the frontier, homestead,
|
||||||
// byzantium and constantinople instructions.
|
// byzantium and constantinople instructions.
|
||||||
func newConstantinopleInstructionSet() JumpTable {
|
func newConstantinopleInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newByzantiumInstructionSet()
|
instructionSet := newByzantiumInstructionSet[TS]()
|
||||||
instructionSet[SHL] = &operation{
|
instructionSet[SHL] = &operation{
|
||||||
execute: opSHL,
|
execute: opSHL,
|
||||||
constantGas: GasFastestStep,
|
constantGas: GasFastestStep,
|
||||||
|
|
@ -191,7 +191,7 @@ func newConstantinopleInstructionSet() JumpTable {
|
||||||
maxStack: maxStack(1, 1),
|
maxStack: maxStack(1, 1),
|
||||||
}
|
}
|
||||||
instructionSet[CREATE2] = &operation{
|
instructionSet[CREATE2] = &operation{
|
||||||
execute: opCreate2,
|
execute: opCreate2[TS],
|
||||||
constantGas: params.Create2Gas,
|
constantGas: params.Create2Gas,
|
||||||
dynamicGas: gasCreate2,
|
dynamicGas: gasCreate2,
|
||||||
minStack: minStack(4, 1),
|
minStack: minStack(4, 1),
|
||||||
|
|
@ -203,10 +203,10 @@ func newConstantinopleInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newByzantiumInstructionSet returns the frontier, homestead and
|
// newByzantiumInstructionSet returns the frontier, homestead and
|
||||||
// byzantium instructions.
|
// byzantium instructions.
|
||||||
func newByzantiumInstructionSet() JumpTable {
|
func newByzantiumInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newSpuriousDragonInstructionSet()
|
instructionSet := newSpuriousDragonInstructionSet[TS]()
|
||||||
instructionSet[STATICCALL] = &operation{
|
instructionSet[STATICCALL] = &operation{
|
||||||
execute: opStaticCall,
|
execute: opStaticCall[TS],
|
||||||
constantGas: params.CallGasEIP150,
|
constantGas: params.CallGasEIP150,
|
||||||
dynamicGas: gasStaticCall,
|
dynamicGas: gasStaticCall,
|
||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
|
|
@ -238,15 +238,15 @@ func newByzantiumInstructionSet() JumpTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP 158 a.k.a Spurious Dragon
|
// EIP 158 a.k.a Spurious Dragon
|
||||||
func newSpuriousDragonInstructionSet() JumpTable {
|
func newSpuriousDragonInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newTangerineWhistleInstructionSet()
|
instructionSet := newTangerineWhistleInstructionSet[TS]()
|
||||||
instructionSet[EXP].dynamicGas = gasExpEIP158
|
instructionSet[EXP].dynamicGas = gasExpEIP158
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP 150 a.k.a Tangerine Whistle
|
// EIP 150 a.k.a Tangerine Whistle
|
||||||
func newTangerineWhistleInstructionSet() JumpTable {
|
func newTangerineWhistleInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newHomesteadInstructionSet()
|
instructionSet := newHomesteadInstructionSet[TS]()
|
||||||
instructionSet[BALANCE].constantGas = params.BalanceGasEIP150
|
instructionSet[BALANCE].constantGas = params.BalanceGasEIP150
|
||||||
instructionSet[EXTCODESIZE].constantGas = params.ExtcodeSizeGasEIP150
|
instructionSet[EXTCODESIZE].constantGas = params.ExtcodeSizeGasEIP150
|
||||||
instructionSet[SLOAD].constantGas = params.SloadGasEIP150
|
instructionSet[SLOAD].constantGas = params.SloadGasEIP150
|
||||||
|
|
@ -259,10 +259,10 @@ func newTangerineWhistleInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newHomesteadInstructionSet returns the frontier and homestead
|
// newHomesteadInstructionSet returns the frontier and homestead
|
||||||
// instructions that can be executed during the homestead phase.
|
// instructions that can be executed during the homestead phase.
|
||||||
func newHomesteadInstructionSet() JumpTable {
|
func newHomesteadInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
instructionSet := newFrontierInstructionSet()
|
instructionSet := newFrontierInstructionSet[TS]()
|
||||||
instructionSet[DELEGATECALL] = &operation{
|
instructionSet[DELEGATECALL] = &operation{
|
||||||
execute: opDelegateCall,
|
execute: opDelegateCall[TS],
|
||||||
dynamicGas: gasDelegateCall,
|
dynamicGas: gasDelegateCall,
|
||||||
constantGas: params.CallGasFrontier,
|
constantGas: params.CallGasFrontier,
|
||||||
minStack: minStack(6, 1),
|
minStack: minStack(6, 1),
|
||||||
|
|
@ -274,7 +274,7 @@ func newHomesteadInstructionSet() JumpTable {
|
||||||
|
|
||||||
// newFrontierInstructionSet returns the frontier instructions
|
// newFrontierInstructionSet returns the frontier instructions
|
||||||
// that can be executed during the frontier phase.
|
// that can be executed during the frontier phase.
|
||||||
func newFrontierInstructionSet() JumpTable {
|
func newFrontierInstructionSet[TS TracingSwitch]() JumpTable {
|
||||||
tbl := JumpTable{
|
tbl := JumpTable{
|
||||||
STOP: {
|
STOP: {
|
||||||
execute: opStop,
|
execute: opStop,
|
||||||
|
|
@ -1040,7 +1040,7 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
memorySize: memoryLog,
|
memorySize: memoryLog,
|
||||||
},
|
},
|
||||||
CREATE: {
|
CREATE: {
|
||||||
execute: opCreate,
|
execute: opCreate[TS],
|
||||||
constantGas: params.CreateGas,
|
constantGas: params.CreateGas,
|
||||||
dynamicGas: gasCreate,
|
dynamicGas: gasCreate,
|
||||||
minStack: minStack(3, 1),
|
minStack: minStack(3, 1),
|
||||||
|
|
@ -1048,7 +1048,7 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
memorySize: memoryCreate,
|
memorySize: memoryCreate,
|
||||||
},
|
},
|
||||||
CALL: {
|
CALL: {
|
||||||
execute: opCall,
|
execute: opCall[TS],
|
||||||
constantGas: params.CallGasFrontier,
|
constantGas: params.CallGasFrontier,
|
||||||
dynamicGas: gasCall,
|
dynamicGas: gasCall,
|
||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
|
|
@ -1056,7 +1056,7 @@ func newFrontierInstructionSet() JumpTable {
|
||||||
memorySize: memoryCall,
|
memorySize: memoryCall,
|
||||||
},
|
},
|
||||||
CALLCODE: {
|
CALLCODE: {
|
||||||
execute: opCallCode,
|
execute: opCallCode[TS],
|
||||||
constantGas: params.CallGasFrontier,
|
constantGas: params.CallGasFrontier,
|
||||||
dynamicGas: gasCallCode,
|
dynamicGas: gasCallCode,
|
||||||
minStack: minStack(7, 1),
|
minStack: minStack(7, 1),
|
||||||
|
|
|
||||||
|
|
@ -24,38 +24,38 @@ import (
|
||||||
|
|
||||||
// LookupInstructionSet returns the instruction set for the fork configured by
|
// LookupInstructionSet returns the instruction set for the fork configured by
|
||||||
// the rules.
|
// the rules.
|
||||||
func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
|
func LookupInstructionSet[TS TracingSwitch](rules params.Rules) (JumpTable, error) {
|
||||||
switch {
|
switch {
|
||||||
case rules.IsVerkle:
|
case rules.IsVerkle:
|
||||||
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
|
return newCancunInstructionSet[TS](), errors.New("verkle-fork not defined yet")
|
||||||
case rules.IsOsaka:
|
case rules.IsOsaka:
|
||||||
return newOsakaInstructionSet(), nil
|
return newOsakaInstructionSet[TS](), nil
|
||||||
case rules.IsPrague:
|
case rules.IsPrague:
|
||||||
return newPragueInstructionSet(), nil
|
return newPragueInstructionSet[TS](), nil
|
||||||
case rules.IsCancun:
|
case rules.IsCancun:
|
||||||
return newCancunInstructionSet(), nil
|
return newCancunInstructionSet[TS](), nil
|
||||||
case rules.IsShanghai:
|
case rules.IsShanghai:
|
||||||
return newShanghaiInstructionSet(), nil
|
return newShanghaiInstructionSet[TS](), nil
|
||||||
case rules.IsMerge:
|
case rules.IsMerge:
|
||||||
return newMergeInstructionSet(), nil
|
return newMergeInstructionSet[TS](), nil
|
||||||
case rules.IsLondon:
|
case rules.IsLondon:
|
||||||
return newLondonInstructionSet(), nil
|
return newLondonInstructionSet[TS](), nil
|
||||||
case rules.IsBerlin:
|
case rules.IsBerlin:
|
||||||
return newBerlinInstructionSet(), nil
|
return newBerlinInstructionSet[TS](), nil
|
||||||
case rules.IsIstanbul:
|
case rules.IsIstanbul:
|
||||||
return newIstanbulInstructionSet(), nil
|
return newIstanbulInstructionSet[TS](), nil
|
||||||
case rules.IsConstantinople:
|
case rules.IsConstantinople:
|
||||||
return newConstantinopleInstructionSet(), nil
|
return newConstantinopleInstructionSet[TS](), nil
|
||||||
case rules.IsByzantium:
|
case rules.IsByzantium:
|
||||||
return newByzantiumInstructionSet(), nil
|
return newByzantiumInstructionSet[TS](), nil
|
||||||
case rules.IsEIP158:
|
case rules.IsEIP158:
|
||||||
return newSpuriousDragonInstructionSet(), nil
|
return newSpuriousDragonInstructionSet[TS](), nil
|
||||||
case rules.IsEIP150:
|
case rules.IsEIP150:
|
||||||
return newTangerineWhistleInstructionSet(), nil
|
return newTangerineWhistleInstructionSet[TS](), nil
|
||||||
case rules.IsHomestead:
|
case rules.IsHomestead:
|
||||||
return newHomesteadInstructionSet(), nil
|
return newHomesteadInstructionSet[TS](), nil
|
||||||
}
|
}
|
||||||
return newFrontierInstructionSet(), nil
|
return newFrontierInstructionSet[TS](), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stack returns the minimum and maximum stack requirements.
|
// Stack returns the minimum and maximum stack requirements.
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
|
|
||||||
// TestJumpTableCopy tests that deep copy is necessary to prevent modify shared jump table
|
// TestJumpTableCopy tests that deep copy is necessary to prevent modify shared jump table
|
||||||
func TestJumpTableCopy(t *testing.T) {
|
func TestJumpTableCopy(t *testing.T) {
|
||||||
tbl := newMergeInstructionSet()
|
tbl := newMergeInstructionSet[TracingEnabled]()
|
||||||
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
|
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
|
||||||
|
|
||||||
// a deep copy won't modify the shared jump table
|
// a deep copy won't modify the shared jump table
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
func makeCallVariantGasCallEIP2929[TS TracingSwitch](oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
|
|
@ -164,7 +164,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
if !useGas[TS](contract, coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,10 +192,6 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
|
|
||||||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
|
|
||||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
|
|
||||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
|
|
||||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||||
|
|
@ -243,14 +239,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
return gasFunc
|
return gasFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func makeCallVariantGasCallEIP7702[TS TracingSwitch](oldCalculator gasFunc) gasFunc {
|
||||||
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
|
|
||||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
|
|
||||||
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
|
|
||||||
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
var (
|
var (
|
||||||
total uint64 // total dynamic gas used
|
total uint64 // total dynamic gas used
|
||||||
|
|
@ -265,7 +254,7 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
if !useGas[TS](contract, coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
total += coldCost
|
total += coldCost
|
||||||
|
|
@ -280,7 +269,7 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
evm.StateDB.AddAddressToAccessList(target)
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
cost = params.ColdAccountAccessCostEIP2929
|
cost = params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
if !useGas[TS](contract, cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
total += cost
|
total += cost
|
||||||
|
|
|
||||||
|
|
@ -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 := vm.Run[vm.TracingEnabled](evm, 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)
|
||||||
|
|
|
||||||
|
|
@ -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 := vm.Run[vm.TracingEnabled](evm, contract, []byte{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue