forked from cerc-io/plugeth
manual geth v1.11.1 merge
This commit is contained in:
+19
-11
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto/bls12381"
|
||||
"github.com/ethereum/go-ethereum/crypto/bn256"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
big2 "github.com/holiman/big"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
@@ -235,7 +236,7 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 {
|
||||
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
|
||||
}
|
||||
func (c *dataCopy) Run(in []byte) ([]byte, error) {
|
||||
return in, nil
|
||||
return common.CopyBytes(in), nil
|
||||
}
|
||||
|
||||
// bigModExp implements a native big integer exponential modular operation.
|
||||
@@ -263,10 +264,10 @@ var (
|
||||
|
||||
// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
|
||||
//
|
||||
// def mult_complexity(x):
|
||||
// if x <= 64: return x ** 2
|
||||
// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
|
||||
// else: return x ** 2 // 16 + 480 * x - 199680
|
||||
// def mult_complexity(x):
|
||||
// if x <= 64: return x ** 2
|
||||
// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
|
||||
// else: return x ** 2 // 16 + 480 * x - 199680
|
||||
//
|
||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||
func modexpMultComplexity(x *big.Int) *big.Int {
|
||||
@@ -377,15 +378,22 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||
}
|
||||
// Retrieve the operands and execute the exponentiation
|
||||
var (
|
||||
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
||||
exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
|
||||
mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
|
||||
base = new(big2.Int).SetBytes(getData(input, 0, baseLen))
|
||||
exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen))
|
||||
mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen))
|
||||
v []byte
|
||||
)
|
||||
if mod.BitLen() == 0 {
|
||||
switch {
|
||||
case mod.BitLen() == 0:
|
||||
// Modulo 0 is undefined, return zero
|
||||
return common.LeftPadBytes([]byte{}, int(modLen)), nil
|
||||
case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
|
||||
//If base == 1, then we can just return base % mod (if mod >= 1, which it is)
|
||||
v = base.Mod(base, mod).Bytes()
|
||||
default:
|
||||
v = base.Exp(base, exp, mod).Bytes()
|
||||
}
|
||||
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
|
||||
return common.LeftPadBytes(v, int(modLen)), nil
|
||||
}
|
||||
|
||||
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
|
||||
@@ -935,7 +943,7 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
|
||||
return nil, errBLS12381G2PointSubgroup
|
||||
}
|
||||
|
||||
// Update pairing engine with G1 and G2 ponits
|
||||
// Update pairing engine with G1 and G2 points
|
||||
e.AddPair(p1, p2)
|
||||
}
|
||||
// Prepare 32 byte output
|
||||
|
||||
@@ -20,18 +20,21 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
3855: enable3855,
|
||||
3860: enable3860,
|
||||
3529: enable3529,
|
||||
3198: enable3198,
|
||||
2929: enable2929,
|
||||
2200: enable2200,
|
||||
1884: enable1884,
|
||||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
}
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
@@ -169,6 +172,45 @@ func enable3198(jt *JumpTable) {
|
||||
}
|
||||
}
|
||||
|
||||
// enable1153 applies EIP-1153 "Transient Storage"
|
||||
// - Adds TLOAD that reads from transient storage
|
||||
// - Adds TSTORE that writes to transient storage
|
||||
func enable1153(jt *JumpTable) {
|
||||
jt[TLOAD] = &operation{
|
||||
execute: opTload,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
minStack: minStack(1, 1),
|
||||
maxStack: maxStack(1, 1),
|
||||
}
|
||||
|
||||
jt[TSTORE] = &operation{
|
||||
execute: opTstore,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
minStack: minStack(2, 0),
|
||||
maxStack: maxStack(2, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// opTload implements TLOAD opcode
|
||||
func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.peek()
|
||||
hash := common.Hash(loc.Bytes32())
|
||||
val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
|
||||
loc.SetBytes(val.Bytes())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opTstore implements TSTORE opcode
|
||||
func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
loc := scope.Stack.pop()
|
||||
val := scope.Stack.pop()
|
||||
interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
|
||||
@@ -192,3 +234,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||
scope.Stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ebnable3860 enables "EIP-3860: Limit and meter initcode"
|
||||
// https://eips.ethereum.org/EIPS/eip-3860
|
||||
func enable3860(jt *JumpTable) {
|
||||
jt[CREATE].dynamicGas = gasCreateEip3860
|
||||
jt[CREATE2].dynamicGas = gasCreate2Eip3860
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ var (
|
||||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||
ErrContractAddressCollision = errors.New("contract address collision")
|
||||
ErrExecutionReverted = errors.New("execution reverted")
|
||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||
ErrInvalidJump = errors.New("invalid jump destination")
|
||||
ErrWriteProtection = errors.New("write protection")
|
||||
|
||||
+22
-13
@@ -19,7 +19,6 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -72,10 +71,10 @@ type BlockContext struct {
|
||||
Coinbase common.Address // Provides information for COINBASE
|
||||
GasLimit uint64 // Provides information for GASLIMIT
|
||||
BlockNumber *big.Int // Provides information for NUMBER
|
||||
Time *big.Int // Provides information for TIME
|
||||
Time uint64 // Provides information for TIME
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
BaseFee *big.Int // Provides information for BASEFEE
|
||||
Random *common.Hash // Provides information for RANDOM
|
||||
Random *common.Hash // Provides information for PREVRANDAO
|
||||
}
|
||||
|
||||
// TxContext provides the EVM with information about a transaction.
|
||||
@@ -132,9 +131,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
|
||||
StateDB: statedb,
|
||||
Config: config,
|
||||
chainConfig: chainConfig,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
||||
}
|
||||
evm.interpreter = NewEVMInterpreter(evm, config)
|
||||
evm.interpreter = NewEVMInterpreter(evm)
|
||||
return evm
|
||||
}
|
||||
|
||||
@@ -161,6 +160,14 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
|
||||
return evm.interpreter
|
||||
}
|
||||
|
||||
// SetBlockContext updates the block context of the EVM.
|
||||
func (evm *EVM) SetBlockContext(blockCtx BlockContext) {
|
||||
evm.Context = blockCtx
|
||||
num := blockCtx.BlockNumber
|
||||
timestamp := blockCtx.Time
|
||||
evm.chainRules = evm.chainConfig.Rules(num, blockCtx.Random != nil, timestamp)
|
||||
}
|
||||
|
||||
// Call executes the contract associated with the addr with the given input as
|
||||
// parameters. It also handles any necessary value transfer required and takes
|
||||
// the necessary steps to create accounts and reverses the state in case of an
|
||||
@@ -183,7 +190,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
|
||||
} else {
|
||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
||||
@@ -199,9 +206,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
defer func(startGas uint64) { // Lazy evaluation of the parameters
|
||||
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
} else {
|
||||
// Handle tracer events for entering and exiting a call frame
|
||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
@@ -308,7 +315,11 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.Config.Debug {
|
||||
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
|
||||
// NOTE: caller must, at all times be a contract. It should never happen
|
||||
// that caller is something other than a Contract.
|
||||
parent := caller.(*Contract)
|
||||
// DELEGATECALL inherits value from parent call
|
||||
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
|
||||
defer func(startGas uint64) {
|
||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
@@ -448,8 +459,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ret, err := evm.interpreter.Run(contract, nil, false)
|
||||
|
||||
// Check whether the max code size has been exceeded, assign err if the case.
|
||||
@@ -487,7 +496,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
|
||||
} else {
|
||||
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
|
||||
}
|
||||
|
||||
+61
-25
@@ -117,20 +117,21 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
||||
return params.SstoreResetGas, nil
|
||||
}
|
||||
}
|
||||
|
||||
// The new gas metering is based on net gas costs (EIP-1283):
|
||||
//
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
// 2. If current value does not equal new value
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
// 2.1.1. If original value is 0, 20000 gas is deducted.
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
// 2.2.1. If original value is not 0
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
// (1.) If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
// (2.) If current value does not equal new value
|
||||
// (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||
// (2.1.1.) If original value is 0, 20000 gas is deducted.
|
||||
// (2.1.2.) Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
|
||||
// (2.2.) If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
// (2.2.1.) If original value is not 0
|
||||
// (2.2.1.1.) If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
|
||||
// (2.2.1.2.) If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
// (2.2.2.) If original value equals new value (this storage slot is reset)
|
||||
// (2.2.2.1.) If original value is 0, add 19800 gas to refund counter.
|
||||
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
|
||||
value := common.Hash(y.Bytes32())
|
||||
if current == value { // noop (1)
|
||||
return params.NetSstoreNoopGas, nil
|
||||
@@ -162,19 +163,21 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
||||
return params.NetSstoreDirtyGas, nil
|
||||
}
|
||||
|
||||
// 0. If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
|
||||
// 2. If current value does not equal new value:
|
||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
|
||||
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
|
||||
// 2.2.1. If original value is not 0:
|
||||
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset):
|
||||
// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
// Here come the EIP220 rules:
|
||||
//
|
||||
// (0.) If *gasleft* is less than or equal to 2300, fail the current call.
|
||||
// (1.) If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
|
||||
// (2.) If current value does not equal new value:
|
||||
// (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context):
|
||||
// (2.1.1.) If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
|
||||
// (2.1.2.) Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
|
||||
// (2.2.) If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
|
||||
// (2.2.1.) If original value is not 0:
|
||||
// (2.2.1.1.) If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
|
||||
// (2.2.1.2.) If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
|
||||
// (2.2.2.) If original value equals new value (this storage slot is reset):
|
||||
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
|
||||
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
|
||||
func gasSStoreEIP2200(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 {
|
||||
@@ -299,6 +302,39 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow || size > params.MaxInitCodeSize {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow || size > params.MaxInitCodeSize {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -105,3 +107,73 @@ func TestEIP2200(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var createGasTests = []struct {
|
||||
code string
|
||||
eip3860 bool
|
||||
gasUsed uint64
|
||||
minimumGas uint64
|
||||
}{
|
||||
// legacy create(0, 0, 0xc000) without 3860 used
|
||||
{"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237},
|
||||
// legacy create(0, 0, 0xc000) _with_ 3860
|
||||
{"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309},
|
||||
// create2(0, 0, 0xc001, 0) without 3860
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 50471},
|
||||
// create2(0, 0, 0xc001, 0) (too large), with 3860
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000},
|
||||
// create2(0, 0, 0xc000, 0)
|
||||
// This case is trying to deploy code at (within) the limit
|
||||
{"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 53528},
|
||||
// create2(0, 0, 0xc001, 0)
|
||||
// This case is trying to deploy code exceeding the limit
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100000},
|
||||
}
|
||||
|
||||
func TestCreateGas(t *testing.T) {
|
||||
for i, tt := range createGasTests {
|
||||
var gasUsed = uint64(0)
|
||||
doCheck := func(testGas int) bool {
|
||||
address := common.BytesToAddress([]byte("contract"))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
statedb.CreateAccount(address)
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.code))
|
||||
statedb.Finalise(true)
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
|
||||
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
|
||||
BlockNumber: big.NewInt(0),
|
||||
}
|
||||
config := Config{}
|
||||
if tt.eip3860 {
|
||||
config.ExtraEips = []int{3860}
|
||||
}
|
||||
|
||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
|
||||
var startGas = uint64(testGas)
|
||||
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
gasUsed = startGas - gas
|
||||
if len(ret) != 32 {
|
||||
t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
|
||||
}
|
||||
if bytes.Equal(ret, make([]byte, 32)) {
|
||||
// Failure
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
minGas := sort.Search(100_000, doCheck)
|
||||
if uint64(minGas) != tt.minimumGas {
|
||||
t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas)
|
||||
}
|
||||
// If the deployment succeeded, we also check the gas used
|
||||
if minGas < 100_000 {
|
||||
if gasUsed != tt.gasUsed {
|
||||
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-30
@@ -21,9 +21,9 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
@@ -238,7 +238,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
||||
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
|
||||
interpreter.hasher = crypto.NewKeccakState()
|
||||
} else {
|
||||
interpreter.hasher.Reset()
|
||||
}
|
||||
@@ -392,29 +392,29 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||
// opExtCodeHash returns the code hash of a specified account.
|
||||
// There are several cases when the function is called, while we can relay everything
|
||||
// to `state.GetCodeHash` function to ensure the correctness.
|
||||
// (1) Caller tries to get the code hash of a normal contract account, state
|
||||
// should return the relative code hash and set it as the result.
|
||||
//
|
||||
// (2) Caller tries to get the code hash of a non-existent account, state should
|
||||
// return common.Hash{} and zero will be set as the result.
|
||||
// 1. Caller tries to get the code hash of a normal contract account, state
|
||||
// should return the relative code hash and set it as the result.
|
||||
//
|
||||
// (3) Caller tries to get the code hash for an account without contract code,
|
||||
// state should return emptyCodeHash(0xc5d246...) as the result.
|
||||
// 2. Caller tries to get the code hash of a non-existent account, state should
|
||||
// return common.Hash{} and zero will be set as the result.
|
||||
//
|
||||
// (4) Caller tries to get the code hash of a precompiled account, the result
|
||||
// should be zero or emptyCodeHash.
|
||||
// 3. Caller tries to get the code hash for an account without contract code, state
|
||||
// should return emptyCodeHash(0xc5d246...) as the result.
|
||||
//
|
||||
// It is worth noting that in order to avoid unnecessary create and clean,
|
||||
// all precompile accounts on mainnet have been transferred 1 wei, so the return
|
||||
// here should be emptyCodeHash.
|
||||
// If the precompile account is not transferred any amount on a private or
|
||||
// 4. Caller tries to get the code hash of a precompiled account, the result should be
|
||||
// zero or emptyCodeHash.
|
||||
//
|
||||
// It is worth noting that in order to avoid unnecessary create and clean, all precompile
|
||||
// accounts on mainnet have been transferred 1 wei, so the return here should be
|
||||
// emptyCodeHash. If the precompile account is not transferred any amount on a private or
|
||||
// customized chain, the return value will be zero.
|
||||
//
|
||||
// (5) Caller tries to get the code hash for an account which is marked as suicided
|
||||
// in the current transaction, the code hash of this account should be returned.
|
||||
// 5. Caller tries to get the code hash for an account which is marked as suicided
|
||||
// in the current transaction, the code hash of this account should be returned.
|
||||
//
|
||||
// (6) Caller tries to get the code hash for an account which is marked as deleted,
|
||||
// this account should be regarded as a non-existent account and zero should be returned.
|
||||
// 6. Caller tries to get the code hash for an account which is marked as deleted, this
|
||||
// account should be regarded as a non-existent account and zero should be returned.
|
||||
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
slot := scope.Stack.peek()
|
||||
address := common.Address(slot.Bytes20())
|
||||
@@ -460,8 +460,7 @@ func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||
}
|
||||
|
||||
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(interpreter.evm.Context.Time)
|
||||
scope.Stack.push(v)
|
||||
scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -527,8 +526,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||
}
|
||||
loc := scope.Stack.pop()
|
||||
val := scope.Stack.pop()
|
||||
interpreter.evm.StateDB.SetState(scope.Contract.Address(),
|
||||
loc.Bytes32(), val.Bytes32())
|
||||
interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -634,7 +632,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(gas)
|
||||
@@ -697,7 +694,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -733,7 +729,6 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -762,7 +757,6 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -791,7 +785,6 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -831,9 +824,9 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
|
||||
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
|
||||
if interpreter.cfg.Debug {
|
||||
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
|
||||
if interpreter.evm.Config.Debug {
|
||||
interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil)
|
||||
}
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
@@ -45,6 +47,14 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff
|
||||
var commonParams []*twoOperandParams
|
||||
var twoOpMethods map[string]executionFunc
|
||||
|
||||
type contractRef struct {
|
||||
addr common.Address
|
||||
}
|
||||
|
||||
func (c contractRef) Address() common.Address {
|
||||
return c.addr
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Params is a list of common edgecases that should be used for some common tests
|
||||
params := []string{
|
||||
@@ -193,7 +203,7 @@ func TestAddMod(t *testing.T) {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
pc = uint64(0)
|
||||
)
|
||||
tests := []struct {
|
||||
@@ -283,7 +293,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
@@ -524,7 +534,7 @@ func TestOpMstore(t *testing.T) {
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
@@ -550,7 +560,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
@@ -567,12 +577,55 @@ func BenchmarkOpMstore(bench *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpTstore(t *testing.T) {
|
||||
var (
|
||||
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
caller = common.Address{}
|
||||
to = common.Address{1}
|
||||
contractRef = contractRef{caller}
|
||||
contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
|
||||
scopeContext = ScopeContext{mem, stack, contract}
|
||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||
)
|
||||
|
||||
// Add a stateObject for the caller and the contract being called
|
||||
statedb.CreateAccount(caller)
|
||||
statedb.CreateAccount(to)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
pc := uint64(0)
|
||||
// push the value to the stack
|
||||
stack.push(new(uint256.Int).SetBytes(value))
|
||||
// push the location to the stack
|
||||
stack.push(new(uint256.Int))
|
||||
opTstore(&pc, evmInterpreter, &scopeContext)
|
||||
// there should be no elements on the stack after TSTORE
|
||||
if stack.len() != 0 {
|
||||
t.Fatal("stack wrong size")
|
||||
}
|
||||
// push the location to the stack
|
||||
stack.push(new(uint256.Int))
|
||||
opTload(&pc, evmInterpreter, &scopeContext)
|
||||
// there should be one element on the stack after TLOAD
|
||||
if stack.len() != 1 {
|
||||
t.Fatal("stack wrong size")
|
||||
}
|
||||
val := stack.peek()
|
||||
if !bytes.Equal(val.Bytes(), value) {
|
||||
t.Fatal("incorrect element read from transient storage")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOpKeccak256(bench *testing.B) {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
evmInterpreter = NewEVMInterpreter(env)
|
||||
)
|
||||
env.interpreter = evmInterpreter
|
||||
mem.Resize(32)
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// StateDB is an EVM database for full state querying.
|
||||
@@ -47,6 +48,9 @@ type StateDB interface {
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
GetTransientState(addr common.Address, key common.Hash) common.Hash
|
||||
SetTransientState(addr common.Address, key, value common.Hash)
|
||||
|
||||
Suicide(common.Address) bool
|
||||
HasSuicided(common.Address) bool
|
||||
|
||||
@@ -57,7 +61,6 @@ type StateDB interface {
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
|
||||
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||
AddressInAccessList(addr common.Address) bool
|
||||
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
|
||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||
@@ -66,6 +69,7 @@ type StateDB interface {
|
||||
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||
// even if the feature/fork is not active yet
|
||||
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
||||
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||
|
||||
RevertToSnapshot(int)
|
||||
Snapshot() int
|
||||
@@ -79,12 +83,12 @@ type StateDB interface {
|
||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
||||
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
|
||||
type CallContext interface {
|
||||
// Call another contract
|
||||
// Call calls another contract.
|
||||
Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Take another's contract code and execute within our own context
|
||||
// CallCode takes another contracts code and execute within our own context
|
||||
CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||
// DelegateCall is same as CallCode except sender and value is propagated from parent to child scope
|
||||
DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||
// Create a new contract
|
||||
// Create creates a new contract
|
||||
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||
}
|
||||
|
||||
+54
-64
@@ -17,10 +17,9 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
@@ -30,10 +29,7 @@ type Config struct {
|
||||
Tracer EVMLogger // Opcode logger
|
||||
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||
|
||||
JumpTable *JumpTable // EVM instruction table, automatically populated if unset
|
||||
|
||||
ExtraEips []int // Additional EIPS that are to be enabled
|
||||
ExtraEips []int // Additional EIPS that are to be enabled
|
||||
}
|
||||
|
||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||
@@ -44,67 +40,61 @@ type ScopeContext struct {
|
||||
Contract *Contract
|
||||
}
|
||||
|
||||
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
|
||||
// Read to get a variable amount of data from the hash state. Read is faster than Sum
|
||||
// because it doesn't copy the internal state, but also modifies the internal state.
|
||||
type keccakState interface {
|
||||
hash.Hash
|
||||
Read([]byte) (int, error)
|
||||
}
|
||||
|
||||
// EVMInterpreter represents an EVM interpreter
|
||||
type EVMInterpreter struct {
|
||||
evm *EVM
|
||||
cfg Config
|
||||
evm *EVM
|
||||
table *JumpTable
|
||||
|
||||
hasher keccakState // Keccak256 hasher instance shared across opcodes
|
||||
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
|
||||
hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
|
||||
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
|
||||
|
||||
readOnly bool // Whether to throw on stateful modifications
|
||||
returnData []byte // Last CALL's return data for subsequent reuse
|
||||
}
|
||||
|
||||
// NewEVMInterpreter returns a new instance of the Interpreter.
|
||||
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||
func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
||||
// If jump table was not initialised we set the default one.
|
||||
if cfg.JumpTable == nil {
|
||||
switch {
|
||||
case evm.chainRules.IsMerge:
|
||||
cfg.JumpTable = &mergeInstructionSet
|
||||
case evm.chainRules.IsLondon:
|
||||
cfg.JumpTable = &londonInstructionSet
|
||||
case evm.chainRules.IsBerlin:
|
||||
cfg.JumpTable = &berlinInstructionSet
|
||||
case evm.chainRules.IsIstanbul:
|
||||
cfg.JumpTable = &istanbulInstructionSet
|
||||
case evm.chainRules.IsConstantinople:
|
||||
cfg.JumpTable = &constantinopleInstructionSet
|
||||
case evm.chainRules.IsByzantium:
|
||||
cfg.JumpTable = &byzantiumInstructionSet
|
||||
case evm.chainRules.IsEIP158:
|
||||
cfg.JumpTable = &spuriousDragonInstructionSet
|
||||
case evm.chainRules.IsEIP150:
|
||||
cfg.JumpTable = &tangerineWhistleInstructionSet
|
||||
case evm.chainRules.IsHomestead:
|
||||
cfg.JumpTable = &homesteadInstructionSet
|
||||
default:
|
||||
cfg.JumpTable = &frontierInstructionSet
|
||||
}
|
||||
for i, eip := range cfg.ExtraEips {
|
||||
copy := *cfg.JumpTable
|
||||
if err := EnableEIP(eip, ©); err != nil {
|
||||
// Disable it, so caller can check if it's activated or not
|
||||
cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
|
||||
log.Error("EIP activation failed", "eip", eip, "error", err)
|
||||
}
|
||||
cfg.JumpTable = ©
|
||||
var table *JumpTable
|
||||
switch {
|
||||
case evm.chainRules.IsShanghai:
|
||||
table = &shanghaiInstructionSet
|
||||
case evm.chainRules.IsMerge:
|
||||
table = &mergeInstructionSet
|
||||
case evm.chainRules.IsLondon:
|
||||
table = &londonInstructionSet
|
||||
case evm.chainRules.IsBerlin:
|
||||
table = &berlinInstructionSet
|
||||
case evm.chainRules.IsIstanbul:
|
||||
table = &istanbulInstructionSet
|
||||
case evm.chainRules.IsConstantinople:
|
||||
table = &constantinopleInstructionSet
|
||||
case evm.chainRules.IsByzantium:
|
||||
table = &byzantiumInstructionSet
|
||||
case evm.chainRules.IsEIP158:
|
||||
table = &spuriousDragonInstructionSet
|
||||
case evm.chainRules.IsEIP150:
|
||||
table = &tangerineWhistleInstructionSet
|
||||
case evm.chainRules.IsHomestead:
|
||||
table = &homesteadInstructionSet
|
||||
default:
|
||||
table = &frontierInstructionSet
|
||||
}
|
||||
var extraEips []int
|
||||
if len(evm.Config.ExtraEips) > 0 {
|
||||
// Deep-copy jumptable to prevent modification of opcodes in other tables
|
||||
table = copyJumpTable(table)
|
||||
}
|
||||
for _, eip := range evm.Config.ExtraEips {
|
||||
if err := EnableEIP(eip, table); err != nil {
|
||||
// Disable it, so caller can check if it's activated or not
|
||||
log.Error("EIP activation failed", "eip", eip, "error", err)
|
||||
} else {
|
||||
extraEips = append(extraEips, eip)
|
||||
}
|
||||
}
|
||||
|
||||
return &EVMInterpreter{
|
||||
evm: evm,
|
||||
cfg: cfg,
|
||||
}
|
||||
evm.Config.ExtraEips = extraEips
|
||||
return &EVMInterpreter{evm: evm, table: table}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
@@ -162,13 +152,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
}()
|
||||
contract.Input = input
|
||||
|
||||
if in.cfg.Debug {
|
||||
if in.evm.Config.Debug {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
} else {
|
||||
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
|
||||
in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -178,14 +168,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
// the execution of one of the operations or until the done flag is set by the
|
||||
// parent context.
|
||||
for {
|
||||
if in.cfg.Debug {
|
||||
if in.evm.Config.Debug {
|
||||
// Capture pre-execution values for tracing.
|
||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||
}
|
||||
// Get the operation from the jump table and validate the stack to ensure there are
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
operation := in.cfg.JumpTable[op]
|
||||
operation := in.table[op]
|
||||
cost = operation.constantGas // For tracing
|
||||
// Validate stack
|
||||
if sLen := stack.len(); sLen < operation.minStack {
|
||||
@@ -223,15 +213,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
// Do tracing before memory expansion
|
||||
if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
if in.evm.Config.Debug {
|
||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
logged = true
|
||||
}
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
}
|
||||
} else if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
} else if in.evm.Config.Debug {
|
||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
logged = true
|
||||
}
|
||||
// execute the operation
|
||||
|
||||
+20
-1
@@ -55,6 +55,7 @@ var (
|
||||
berlinInstructionSet = newBerlinInstructionSet()
|
||||
londonInstructionSet = newLondonInstructionSet()
|
||||
mergeInstructionSet = newMergeInstructionSet()
|
||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||
)
|
||||
|
||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||
@@ -78,9 +79,16 @@ func validate(jt JumpTable) JumpTable {
|
||||
return jt
|
||||
}
|
||||
|
||||
func newShanghaiInstructionSet() JumpTable {
|
||||
instructionSet := newMergeInstructionSet()
|
||||
enable3855(&instructionSet) // PUSH0 instruction
|
||||
enable3860(&instructionSet) // Limit and meter initcode
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func newMergeInstructionSet() JumpTable {
|
||||
instructionSet := newLondonInstructionSet()
|
||||
instructionSet[RANDOM] = &operation{
|
||||
instructionSet[PREVRANDAO] = &operation{
|
||||
execute: opRandom,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
@@ -1043,3 +1051,14 @@ func newFrontierInstructionSet() JumpTable {
|
||||
|
||||
return validate(tbl)
|
||||
}
|
||||
|
||||
func copyJumpTable(source *JumpTable) *JumpTable {
|
||||
dest := *source
|
||||
for i, op := range source {
|
||||
if op != nil {
|
||||
opCopy := *op
|
||||
dest[i] = &opCopy
|
||||
}
|
||||
}
|
||||
return &dest
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table
|
||||
func TestJumpTableCopy(t *testing.T) {
|
||||
tbl := newMergeInstructionSet()
|
||||
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
|
||||
|
||||
// a deep copy won't modify the shared jump table
|
||||
deepCopy := copyJumpTable(&tbl)
|
||||
deepCopy[SLOAD].constantGas = 100
|
||||
require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas)
|
||||
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
|
||||
}
|
||||
+1
-2
@@ -18,7 +18,6 @@ package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ type EVMLogger interface {
|
||||
CaptureTxEnd(restGas uint64)
|
||||
// Top call frame
|
||||
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
|
||||
CaptureEnd(output []byte, gasUsed uint64, err error)
|
||||
// Rest of call frames
|
||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||
CaptureExit(output []byte, gasUsed uint64, err error)
|
||||
|
||||
+15
-6
@@ -25,11 +25,7 @@ type OpCode byte
|
||||
|
||||
// IsPush specifies if an opcode is a PUSH opcode.
|
||||
func (op OpCode) IsPush() bool {
|
||||
switch op {
|
||||
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return PUSH1 <= op && op <= PUSH32
|
||||
}
|
||||
|
||||
// 0x0 range - arithmetic ops.
|
||||
@@ -99,6 +95,7 @@ const (
|
||||
NUMBER OpCode = 0x43
|
||||
DIFFICULTY OpCode = 0x44
|
||||
RANDOM OpCode = 0x44 // Same as DIFFICULTY
|
||||
PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
|
||||
GASLIMIT OpCode = 0x45
|
||||
CHAINID OpCode = 0x46
|
||||
SELFBALANCE OpCode = 0x47
|
||||
@@ -222,6 +219,12 @@ const (
|
||||
SELFDESTRUCT OpCode = 0xff
|
||||
)
|
||||
|
||||
// 0xb0 range.
|
||||
const (
|
||||
TLOAD OpCode = 0xb3
|
||||
TSTORE OpCode = 0xb4
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice.
|
||||
var opCodeToString = map[OpCode]string{
|
||||
// 0x0 range - arithmetic ops.
|
||||
@@ -280,7 +283,7 @@ var opCodeToString = map[OpCode]string{
|
||||
COINBASE: "COINBASE",
|
||||
TIMESTAMP: "TIMESTAMP",
|
||||
NUMBER: "NUMBER",
|
||||
DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to RANDOM post merge
|
||||
DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to PREVRANDAO post merge
|
||||
GASLIMIT: "GASLIMIT",
|
||||
CHAINID: "CHAINID",
|
||||
SELFBALANCE: "SELFBALANCE",
|
||||
@@ -376,6 +379,10 @@ var opCodeToString = map[OpCode]string{
|
||||
LOG3: "LOG3",
|
||||
LOG4: "LOG4",
|
||||
|
||||
// 0xb0 range.
|
||||
TLOAD: "TLOAD",
|
||||
TSTORE: "TSTORE",
|
||||
|
||||
// 0xf0 range.
|
||||
CREATE: "CREATE",
|
||||
CALL: "CALL",
|
||||
@@ -466,6 +473,8 @@ var stringToOp = map[string]OpCode{
|
||||
"GAS": GAS,
|
||||
"JUMPDEST": JUMPDEST,
|
||||
"PUSH0": PUSH0,
|
||||
"TLOAD": TLOAD,
|
||||
"TSTORE": TSTORE,
|
||||
"PUSH1": PUSH1,
|
||||
"PUSH2": PUSH2,
|
||||
"PUSH3": PUSH3,
|
||||
|
||||
+21
-19
@@ -19,7 +19,6 @@ package runtime
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@@ -37,7 +36,7 @@ type Config struct {
|
||||
Origin common.Address
|
||||
Coinbase common.Address
|
||||
BlockNumber *big.Int
|
||||
Time *big.Int
|
||||
Time uint64
|
||||
GasLimit uint64
|
||||
GasPrice *big.Int
|
||||
Value *big.Int
|
||||
@@ -74,9 +73,6 @@ func setDefaults(cfg *Config) {
|
||||
if cfg.Difficulty == nil {
|
||||
cfg.Difficulty = new(big.Int)
|
||||
}
|
||||
if cfg.Time == nil {
|
||||
cfg.Time = big.NewInt(time.Now().Unix())
|
||||
}
|
||||
if cfg.GasLimit == 0 {
|
||||
cfg.GasLimit = math.MaxUint64
|
||||
}
|
||||
@@ -117,10 +113,12 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
address = common.BytesToAddress([]byte("contract"))
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
|
||||
)
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
// Execute the preparatory steps for state transition which includes:
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
cfg.State.CreateAccount(address)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code)
|
||||
@@ -132,7 +130,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
cfg.GasLimit,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
return ret, cfg.State, err
|
||||
}
|
||||
|
||||
@@ -149,10 +146,12 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
var (
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
|
||||
)
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
// Execute the preparatory steps for state transition which includes:
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||
// Call the code with the given configuration.
|
||||
code, address, leftOverGas, err := vmenv.Create(
|
||||
sender,
|
||||
@@ -171,14 +170,17 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) {
|
||||
setDefaults(cfg)
|
||||
|
||||
vmenv := NewEnv(cfg)
|
||||
var (
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
statedb = cfg.State
|
||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
|
||||
)
|
||||
// Execute the preparatory steps for state transition which includes:
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
statedb := cfg.State
|
||||
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil); rules.IsBerlin {
|
||||
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
sender,
|
||||
|
||||
@@ -48,9 +48,6 @@ func TestDefaults(t *testing.T) {
|
||||
t.Error("expected difficulty to be non nil")
|
||||
}
|
||||
|
||||
if cfg.Time == nil {
|
||||
t.Error("expected time to be non nil")
|
||||
}
|
||||
if cfg.GasLimit == 0 {
|
||||
t.Error("didn't expect gaslimit to be zero")
|
||||
}
|
||||
@@ -174,7 +171,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
|
||||
State: statedb,
|
||||
GasLimit: 10000000,
|
||||
Difficulty: big.NewInt(0x200000),
|
||||
Time: new(big.Int).SetUint64(0),
|
||||
Time: 0,
|
||||
Coinbase: common.Address{},
|
||||
BlockNumber: new(big.Int).SetUint64(1),
|
||||
ChainConfig: ¶ms.ChainConfig{
|
||||
@@ -333,7 +330,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
cfg.GasLimit = gas
|
||||
if len(tracerCode) > 0 {
|
||||
tracer, err := tracers.New(tracerCode, new(tracers.Context), nil)
|
||||
tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -832,7 +829,7 @@ func TestRuntimeJSTracer(t *testing.T) {
|
||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
||||
statedb.SetCode(common.HexToAddress("0xff"), depressedCode)
|
||||
|
||||
tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
|
||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -868,7 +865,7 @@ func TestJSTracerCreateTx(t *testing.T) {
|
||||
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
||||
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
|
||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -124,5 +124,12 @@
|
||||
"Name": "cdetrio15",
|
||||
"Gas": 6000,
|
||||
"NoBenchmark": true
|
||||
},
|
||||
{
|
||||
"Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000000",
|
||||
"Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"Name": "zeroScalar",
|
||||
"Gas": 6000,
|
||||
"NoBenchmark": true
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user