core/vm: interpreter loop generator PoC

This commit is contained in:
Ömer Faruk IRMAK 2025-07-21 11:49:21 +03:00
parent f17df6db91
commit ae63a92d08
8 changed files with 705 additions and 302 deletions

View file

@ -18,7 +18,7 @@ package vm
import (
"fmt"
"math"
stdmath "math"
"sort"
"github.com/ethereum/go-ethereum/common"
@ -352,7 +352,7 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = math.MaxUint64
uint64CodeOffset = stdmath.MaxUint64
}
addr := common.Address(a.Bytes20())
code := interpreter.evm.StateDB.GetCode(addr)

View file

@ -64,37 +64,43 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
// MCOPY (stack position 2)
// EXTCODECOPY (stack position 3)
// RETURNDATACOPY (stack position 2)
func memoryCopierGas(stackpos int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// Gas for expanding the memory
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
func memoryCopierGas(stackpos int, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// Gas for expanding the memory
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
var (
gasCallDataCopy = memoryCopierGas(2)
gasCodeCopy = memoryCopierGas(2)
gasMcopy = memoryCopierGas(2)
gasExtCodeCopy = memoryCopierGas(3)
gasReturnDataCopy = memoryCopierGas(2)
)
func gasCallDataCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryCopierGas(2, evm, contract, stack, mem, memorySize)
}
func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryCopierGas(2, evm, contract, stack, mem, memorySize)
}
func gasMcopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryCopierGas(2, evm, contract, stack, mem, memorySize)
}
func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryCopierGas(3, evm, contract, stack, mem, memorySize)
}
func gasReturnDataCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryCopierGas(2, evm, contract, stack, mem, memorySize)
}
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
@ -221,34 +227,48 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
return params.SloadGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
func makeGasLog(n uint64, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasLog0(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeGasLog(0, evm, contract, stack, mem, memorySize)
}
func gasLog1(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeGasLog(1, evm, contract, stack, mem, memorySize)
}
func gasLog2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeGasLog(2, evm, contract, stack, mem, memorySize)
}
func gasLog3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeGasLog(3, evm, contract, stack, mem, memorySize)
}
func gasLog4(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeGasLog(4, evm, contract, stack, mem, memorySize)
}
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {

217
core/vm/generator.go Normal file
View file

@ -0,0 +1,217 @@
package vm
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"os"
"reflect"
"runtime"
"strings"
)
var fSet = token.NewFileSet()
type opFuncNames struct {
memory string
gas string
execution string
}
func functionName(f any) string {
fValue := reflect.ValueOf(f)
if fValue.IsNil() {
return ""
}
rFunc := runtime.FuncForPC(fValue.Pointer())
if rFunc == nil {
panic(fmt.Sprint(f, "is not a function"))
}
ident := rFunc.Name()
arr := strings.Split(ident, ".")
return arr[len(arr)-1]
}
func collectFuncNames(jt JumpTable) [256]opFuncNames {
var funcNames [256]opFuncNames
for opCode, entry := range jt {
funcNames[opCode] = opFuncNames{
memory: functionName(entry.memorySize),
gas: functionName(entry.dynamicGas),
execution: functionName(entry.execute),
}
}
return funcNames
}
type funcDef struct {
decl *ast.FuncDecl
source string
}
type opFuncDefs struct {
memory funcDef
gas funcDef
execution funcDef
}
func definitionTargets(name string, funcNames *[256]opFuncNames, funcDefs *[256]opFuncDefs) []*funcDef {
targets := []*funcDef{}
for op := range funcNames {
if funcNames[op].memory == name {
targets = append(targets, &funcDefs[op].memory)
}
if funcNames[op].gas == name {
targets = append(targets, &funcDefs[op].gas)
}
if funcNames[op].execution == name {
targets = append(targets, &funcDefs[op].execution)
}
}
return targets
}
func extractBody(funcDef string) string {
start := strings.Index(funcDef, "{")
end := strings.LastIndex(funcDef, "}")
return funcDef[start : end+1]
}
func collectFuncDefinitions(funcNames [256]opFuncNames) [256]opFuncDefs {
pkgs, err := parser.ParseDir(fSet, ".", nil, 0)
if err != nil {
panic(fmt.Sprint("failed to parse vm package", err))
}
vmPkg := pkgs["vm"]
if vmPkg == nil {
panic(fmt.Sprint("vm package not found"))
}
var defs [256]opFuncDefs
for _, astFile := range vmPkg.Files {
f, err := os.Open(fSet.File(astFile.FileStart).Name())
if err != nil {
panic(fmt.Sprint("failed to open file package", astFile.Name.Name, err))
}
for _, decl := range astFile.Decls {
if fDecl, isFunc := decl.(*ast.FuncDecl); isFunc {
defTargets := definitionTargets(fDecl.Name.Name, &funcNames, &defs)
if len(defTargets) == 0 {
continue
}
declPos := fSet.Position(fDecl.Pos())
declEndPos := fSet.Position(fDecl.End())
declSize := declEndPos.Offset - declPos.Offset
def := make([]byte, declSize)
_, err := f.ReadAt(def, int64(declPos.Offset))
if err != nil && err != io.EOF {
panic(fmt.Sprint("failed to read function def", fDecl.Name.Name, err))
}
for _, defTarget := range defTargets {
(*defTarget) = funcDef{
source: string(def),
decl: fDecl,
}
}
}
}
}
return defs
}
func outputMemoryCalc(w io.Writer, op *funcDef) {
fmt.Fprintf(w, `
_memorySize, _overflow := func (stack *Stack) (uint64, bool) %s(callContext.Stack)
if _overflow {
return nil, ErrGasUintOverflow
}
`, extractBody(op.source))
}
func outputDynamicGas(w io.Writer, op *funcDef, hasDynamicMemory bool) {
dynamicMemory := "0"
if hasDynamicMemory {
dynamicMemory = "_memorySize"
}
fmt.Fprintf(w, `
_dynamicGas, _gasErr := func (evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) %s(interpreter.evm, callContext.Contract, callContext.Stack, callContext.Memory, %s)
if _gasErr != nil {
return nil, ErrOutOfGas
}
if !callContext.Contract.UseGas(_dynamicGas, nil, tracing.GasChangeIgnored) {
return nil, ErrOutOfGas
}
`, extractBody(op.source), dynamicMemory)
if hasDynamicMemory {
fmt.Fprintf(w, "callContext.Memory.Resize(_memorySize)\n")
}
}
func outputExecution(w io.Writer, op *funcDef) {
fmt.Fprintf(w, `
_, _executionErr := func (pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) %s(&pc, interpreter, callContext)
if _executionErr != nil {
return nil, _executionErr
}
`, extractBody(op.source))
}
func GenerateRun() {
set := newLondonInstructionSet()
funcNames := collectFuncNames(set)
funcDefinitions := collectFuncDefinitions(funcNames)
output, _ := os.Create("out.go")
fmt.Fprintln(output, `package vm
func Run(interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
var pc uint64
for {
op := callContext.Contract.GetOp(pc)
switch op {
`)
for opCode := range funcDefinitions {
fmt.Fprintf(output, `case %d:
if !callContext.Contract.UseGas(%d, nil, tracing.GasChangeIgnored) {
return nil, ErrOutOfGas
}
if sLen := callContext.Stack.len(); sLen < %d {
return nil, &ErrStackUnderflow{stackLen: sLen, required: %d}
} else if sLen > %d {
return nil, &ErrStackOverflow{stackLen: sLen, limit: %d}
}
`, opCode, set[opCode].constantGas, set[opCode].minStack, set[opCode].minStack, set[opCode].maxStack, set[opCode].maxStack)
if funcDefinitions[opCode].memory.decl != nil {
outputMemoryCalc(output, &funcDefinitions[opCode].memory)
} else if len(funcNames[opCode].memory) > 0 {
panic(fmt.Sprint("failed to get memory handler definition for ", opCodeToString[opCode]))
}
if funcDefinitions[opCode].gas.decl != nil {
outputDynamicGas(output, &funcDefinitions[opCode].gas, funcDefinitions[opCode].memory.decl != nil)
} else if len(funcNames[opCode].gas) > 0 {
panic(fmt.Sprint("failed to get gas handler definition for ", opCodeToString[opCode]))
}
if funcDefinitions[opCode].execution.decl != nil {
outputExecution(output, &funcDefinitions[opCode].execution)
} else if len(funcNames[opCode].execution) > 0 {
panic(fmt.Sprint("failed to get exec handler definition for ", opCodeToString[opCode]))
}
}
fmt.Fprintln(output, `}
pc++
}}`)
output.Close()
}

View file

@ -17,7 +17,7 @@
package vm
import (
"math"
stdmath "math"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
@ -296,7 +296,7 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
)
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
if overflow {
dataOffset64 = math.MaxUint64
dataOffset64 = stdmath.MaxUint64
}
// These values are checked for overflow during gas cost calculation
memOffset64 := memOffset.Uint64()
@ -352,7 +352,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = math.MaxUint64
uint64CodeOffset = stdmath.MaxUint64
}
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
@ -370,7 +370,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = math.MaxUint64
uint64CodeOffset = stdmath.MaxUint64
}
addr := common.Address(a.Bytes20())
code := interpreter.evm.StateDB.GetCode(addr)
@ -924,31 +924,45 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
// following functions are used by the instruction jump table
// make log instruction function
func makeLog(size int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
topics := make([]common.Hash, size)
stack := scope.Stack
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
addr := stack.pop()
topics[i] = addr.Bytes32()
}
d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64())
interpreter.evm.StateDB.AddLog(&types.Log{
Address: scope.Contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(),
})
return nil, nil
func doLog(size int, pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
topics := make([]common.Hash, size)
stack := scope.Stack
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
addr := stack.pop()
topics[i] = addr.Bytes32()
}
d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64())
interpreter.evm.StateDB.AddLog(&types.Log{
Address: scope.Contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(),
})
return nil, nil
}
func opLog0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doLog(0, pc, interpreter, scope)
}
func opLog1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doLog(1, pc, interpreter, scope)
}
func opLog2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doLog(2, pc, interpreter, scope)
}
func opLog3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doLog(3, pc, interpreter, scope)
}
func opLog4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doLog(4, pc, interpreter, scope)
}
// opPush1 is a specialized version of pushN
@ -983,30 +997,176 @@ func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
return nil, nil
}
// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
codeLen = len(scope.Contract.Code)
start = min(codeLen, int(*pc+1))
end = min(codeLen, start+pushByteSize)
)
a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end])
func doPush(size uint64, pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
pushByteSize = int(size)
codeLen = len(scope.Contract.Code)
start = min(codeLen, int(*pc+1))
end = min(codeLen, start+pushByteSize)
)
a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end])
// Missing bytes: pushByteSize - len(pushData)
if missing := pushByteSize - (end - start); missing > 0 {
a.Lsh(a, uint(8*missing))
}
scope.Stack.push(a)
*pc += size
return nil, nil
// Missing bytes: pushByteSize - len(pushData)
if missing := pushByteSize - (end - start); missing > 0 {
a.Lsh(a, uint(8*missing))
}
scope.Stack.push(a)
*pc += size
return nil, nil
}
// make dup instruction function
func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(int(size))
return nil, nil
}
func opPush3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(3, pc, interpreter, scope)
}
func opPush4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(4, pc, interpreter, scope)
}
func opPush5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(5, pc, interpreter, scope)
}
func opPush6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(6, pc, interpreter, scope)
}
func opPush7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(7, pc, interpreter, scope)
}
func opPush8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(8, pc, interpreter, scope)
}
func opPush9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(9, pc, interpreter, scope)
}
func opPush10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(10, pc, interpreter, scope)
}
func opPush11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(11, pc, interpreter, scope)
}
func opPush12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(12, pc, interpreter, scope)
}
func opPush13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(13, pc, interpreter, scope)
}
func opPush14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(14, pc, interpreter, scope)
}
func opPush15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(15, pc, interpreter, scope)
}
func opPush16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(16, pc, interpreter, scope)
}
func opPush17(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(17, pc, interpreter, scope)
}
func opPush18(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(18, pc, interpreter, scope)
}
func opPush19(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(19, pc, interpreter, scope)
}
func opPush20(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(20, pc, interpreter, scope)
}
func opPush21(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(21, pc, interpreter, scope)
}
func opPush22(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(22, pc, interpreter, scope)
}
func opPush23(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(23, pc, interpreter, scope)
}
func opPush24(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(24, pc, interpreter, scope)
}
func opPush25(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(25, pc, interpreter, scope)
}
func opPush26(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(26, pc, interpreter, scope)
}
func opPush27(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(27, pc, interpreter, scope)
}
func opPush28(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(28, pc, interpreter, scope)
}
func opPush29(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(29, pc, interpreter, scope)
}
func opPush30(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(30, pc, interpreter, scope)
}
func opPush31(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(31, pc, interpreter, scope)
}
func opPush32(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return doPush(32, pc, interpreter, scope)
}
func opDup1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(1)
return nil, nil
}
func opDup2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(2)
return nil, nil
}
func opDup3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(3)
return nil, nil
}
func opDup4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(4)
return nil, nil
}
func opDup5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(5)
return nil, nil
}
func opDup6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(6)
return nil, nil
}
func opDup7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(7)
return nil, nil
}
func opDup8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(8)
return nil, nil
}
func opDup9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(9)
return nil, nil
}
func opDup10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(10)
return nil, nil
}
func opDup11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(11)
return nil, nil
}
func opDup12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(12)
return nil, nil
}
func opDup13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(13)
return nil, nil
}
func opDup14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(14)
return nil, nil
}
func opDup15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(15)
return nil, nil
}
func opDup16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.dup(16)
return nil, nil
}

View file

@ -906,7 +906,7 @@ func TestOpMCopy(t *testing.T) {
func TestPush(t *testing.T) {
code := common.FromHex("0011223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121")
push32 := makePush(32, 32)
push32 := opPush32
scope := &ScopeContext{
Memory: nil,

View file

@ -88,7 +88,6 @@ func BenchmarkInterpreter(b *testing.B) {
)
stack.push(uint256.NewInt(123))
stack.push(uint256.NewInt(123))
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
b.ResetTimer()
for i := 0; i < b.N; i++ {
gasSStoreEIP3529(evm, contract, stack, mem, 1234)

View file

@ -633,277 +633,277 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(0, 1),
},
PUSH3: {
execute: makePush(3, 3),
execute: opPush3,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH4: {
execute: makePush(4, 4),
execute: opPush4,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH5: {
execute: makePush(5, 5),
execute: opPush5,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH6: {
execute: makePush(6, 6),
execute: opPush6,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH7: {
execute: makePush(7, 7),
execute: opPush7,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH8: {
execute: makePush(8, 8),
execute: opPush8,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH9: {
execute: makePush(9, 9),
execute: opPush9,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH10: {
execute: makePush(10, 10),
execute: opPush10,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH11: {
execute: makePush(11, 11),
execute: opPush11,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH12: {
execute: makePush(12, 12),
execute: opPush12,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH13: {
execute: makePush(13, 13),
execute: opPush13,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH14: {
execute: makePush(14, 14),
execute: opPush14,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH15: {
execute: makePush(15, 15),
execute: opPush15,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH16: {
execute: makePush(16, 16),
execute: opPush16,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH17: {
execute: makePush(17, 17),
execute: opPush17,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH18: {
execute: makePush(18, 18),
execute: opPush18,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH19: {
execute: makePush(19, 19),
execute: opPush19,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH20: {
execute: makePush(20, 20),
execute: opPush20,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH21: {
execute: makePush(21, 21),
execute: opPush21,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH22: {
execute: makePush(22, 22),
execute: opPush22,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH23: {
execute: makePush(23, 23),
execute: opPush23,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH24: {
execute: makePush(24, 24),
execute: opPush24,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH25: {
execute: makePush(25, 25),
execute: opPush25,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH26: {
execute: makePush(26, 26),
execute: opPush26,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH27: {
execute: makePush(27, 27),
execute: opPush27,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH28: {
execute: makePush(28, 28),
execute: opPush28,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH29: {
execute: makePush(29, 29),
execute: opPush29,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH30: {
execute: makePush(30, 30),
execute: opPush30,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH31: {
execute: makePush(31, 31),
execute: opPush31,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH32: {
execute: makePush(32, 32),
execute: opPush32,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
DUP1: {
execute: makeDup(1),
execute: opDup1,
constantGas: GasFastestStep,
minStack: minDupStack(1),
maxStack: maxDupStack(1),
},
DUP2: {
execute: makeDup(2),
execute: opDup2,
constantGas: GasFastestStep,
minStack: minDupStack(2),
maxStack: maxDupStack(2),
},
DUP3: {
execute: makeDup(3),
execute: opDup3,
constantGas: GasFastestStep,
minStack: minDupStack(3),
maxStack: maxDupStack(3),
},
DUP4: {
execute: makeDup(4),
execute: opDup4,
constantGas: GasFastestStep,
minStack: minDupStack(4),
maxStack: maxDupStack(4),
},
DUP5: {
execute: makeDup(5),
execute: opDup5,
constantGas: GasFastestStep,
minStack: minDupStack(5),
maxStack: maxDupStack(5),
},
DUP6: {
execute: makeDup(6),
execute: opDup6,
constantGas: GasFastestStep,
minStack: minDupStack(6),
maxStack: maxDupStack(6),
},
DUP7: {
execute: makeDup(7),
execute: opDup7,
constantGas: GasFastestStep,
minStack: minDupStack(7),
maxStack: maxDupStack(7),
},
DUP8: {
execute: makeDup(8),
execute: opDup8,
constantGas: GasFastestStep,
minStack: minDupStack(8),
maxStack: maxDupStack(8),
},
DUP9: {
execute: makeDup(9),
execute: opDup9,
constantGas: GasFastestStep,
minStack: minDupStack(9),
maxStack: maxDupStack(9),
},
DUP10: {
execute: makeDup(10),
execute: opDup10,
constantGas: GasFastestStep,
minStack: minDupStack(10),
maxStack: maxDupStack(10),
},
DUP11: {
execute: makeDup(11),
execute: opDup11,
constantGas: GasFastestStep,
minStack: minDupStack(11),
maxStack: maxDupStack(11),
},
DUP12: {
execute: makeDup(12),
execute: opDup12,
constantGas: GasFastestStep,
minStack: minDupStack(12),
maxStack: maxDupStack(12),
},
DUP13: {
execute: makeDup(13),
execute: opDup13,
constantGas: GasFastestStep,
minStack: minDupStack(13),
maxStack: maxDupStack(13),
},
DUP14: {
execute: makeDup(14),
execute: opDup14,
constantGas: GasFastestStep,
minStack: minDupStack(14),
maxStack: maxDupStack(14),
},
DUP15: {
execute: makeDup(15),
execute: opDup15,
constantGas: GasFastestStep,
minStack: minDupStack(15),
maxStack: maxDupStack(15),
},
DUP16: {
execute: makeDup(16),
execute: opDup16,
constantGas: GasFastestStep,
minStack: minDupStack(16),
maxStack: maxDupStack(16),
@ -1005,36 +1005,36 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxSwapStack(17),
},
LOG0: {
execute: makeLog(0),
dynamicGas: makeGasLog(0),
execute: opLog0,
dynamicGas: gasLog0,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryLog,
},
LOG1: {
execute: makeLog(1),
dynamicGas: makeGasLog(1),
execute: opLog1,
dynamicGas: gasLog1,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryLog,
},
LOG2: {
execute: makeLog(2),
dynamicGas: makeGasLog(2),
execute: opLog2,
dynamicGas: gasLog2,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryLog,
},
LOG3: {
execute: makeLog(3),
dynamicGas: makeGasLog(3),
execute: opLog3,
dynamicGas: gasLog3,
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
memorySize: memoryLog,
},
LOG4: {
execute: makeLog(4),
dynamicGas: makeGasLog(4),
execute: opLog4,
dynamicGas: gasLog4,
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
memorySize: memoryLog,

View file

@ -26,68 +26,66 @@ import (
"github.com/ethereum/go-ethereum/params"
)
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
value := common.Hash(y.Bytes32())
func gasSStoreFuncEIP(clearingRefund uint64, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
@ -152,95 +150,104 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
return 0, nil
}
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(addressPosition).Bytes20())
// Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(addressPosition).Bytes20())
// Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
if warmAccess || err != nil {
return gas, err
}
// In case of a cold access, we temporarily add the cold charge back, and also
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas += coldCost
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
if warmAccess || err != nil {
return gas, err
}
// In case of a cold access, we temporarily add the cold charge back, and also
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas += coldCost
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
var (
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
func gasCallEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeCallVariantGasCallEIP2929(gasCall, 1, evm, contract, stack, mem, memorySize)
}
func gasDelegateCallEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeCallVariantGasCallEIP2929(gasDelegateCall, 1, evm, contract, stack, mem, memorySize)
}
func gasStaticCallEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeCallVariantGasCallEIP2929(gasStaticCall, 1, evm, contract, stack, mem, memorySize)
}
func gasCallCodeEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeCallVariantGasCallEIP2929(gasCallCode, 1, evm, contract, stack, mem, memorySize)
}
func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeSelfdestructGasFn(true, evm, contract, stack, mem, memorySize)
}
func gasSelfdestructEIP3529(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return makeSelfdestructGasFn(false, evm, contract, stack, mem, memorySize)
}
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
//
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
// Additionally, modify the parameters defined in EIP 2200 as follows:
//
// Parameter Old value New value
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
//
//The other parameters defined in EIP 2200 are unchanged.
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
//
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
// Additionally, modify the parameters defined in EIP 2200 as follows:
//
// Parameter Old value New value
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
//
// The other parameters defined in EIP 2200 are unchanged.
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gasSStoreFuncEIP(params.SstoreClearsScheduleRefundEIP2200, evm, contract, stack, mem, memorySize)
}
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
)
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
func gasSStoreEIP3529(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gasSStoreFuncEIP(params.SstoreClearsScheduleRefundEIP3529, evm, contract, stack, mem, memorySize)
}
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
address = common.Address(stack.peek().Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address)
gas = params.ColdAccountAccessCostEIP2929
}
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += params.CreateBySelfdestructGas
}
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
func makeSelfdestructGasFn(refundsEnabled bool, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
address = common.Address(stack.peek().Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address)
gas = params.ColdAccountAccessCostEIP2929
}
return gasFunc
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += params.CreateBySelfdestructGas
}
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
}
var (