mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-13 23:43:47 +00:00
* core/vm: implement EIP-3860: Limit and meter initcode (#23847) Implementation of https://eips.ethereum.org/EIPS/eip-3860, limit and meter initcode. This PR enables EIP-3860 as part of the Shanghai fork. Co-authored-by: lightclient@protonmail.com <lightclient@protonmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * add ishanghai * fix test * fix --------- Co-authored-by: Andrei Maiboroda <andrei@ethereum.org> Co-authored-by: lightclient@protonmail.com <lightclient@protonmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
parent
e1fb1955a0
commit
c5da652c29
17 changed files with 256 additions and 15 deletions
|
|
@ -141,7 +141,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Check intrinsic gas
|
// Check intrinsic gas
|
||||||
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
|
||||||
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil {
|
chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil {
|
||||||
r.Error = err
|
r.Error = err
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
continue
|
continue
|
||||||
|
|
@ -172,6 +172,10 @@ func Transaction(ctx *cli.Context) error {
|
||||||
case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
|
||||||
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
|
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
|
||||||
}
|
}
|
||||||
|
// Check whether the init code size has been exceeded.
|
||||||
|
if chainConfig.IsShanghai(new(big.Int)) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
|
||||||
|
r.Error = errors.New("max initcode size exceeded")
|
||||||
|
}
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
}
|
}
|
||||||
out, err := json.MarshalIndent(results, "", " ")
|
out, err := json.MarshalIndent(results, "", " ")
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
toaddr := common.Address{}
|
toaddr := common.Address{}
|
||||||
data := make([]byte, nbytes)
|
data := make([]byte, nbytes)
|
||||||
gas, _ := IntrinsicGas(data, nil, false, false, false)
|
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
|
||||||
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
|
||||||
gasPrice := big.NewInt(0)
|
gasPrice := big.NewInt(0)
|
||||||
if gen.header.BaseFee != nil {
|
if gen.header.BaseFee != nil {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,10 @@ var (
|
||||||
// have enough funds for transfer(topmost call only).
|
// have enough funds for transfer(topmost call only).
|
||||||
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||||
|
|
||||||
|
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
|
||||||
|
// than init code size limit.
|
||||||
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
|
|
||||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||||
// is higher than the balance of the user's account.
|
// is higher than the balance of the user's account.
|
||||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
BerlinBlock: big.NewInt(0),
|
BerlinBlock: big.NewInt(0),
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
|
ShanghaiBlock: big.NewInt(0),
|
||||||
Ethash: new(params.EthashConfig),
|
Ethash: new(params.EthashConfig),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(config)
|
signer = types.LatestSigner(config)
|
||||||
|
|
@ -76,6 +77,17 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
}), signer, key1)
|
}), signer, key1)
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction {
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
|
||||||
|
Nonce: nonce,
|
||||||
|
GasTipCap: gasTipCap,
|
||||||
|
GasFeeCap: gasFeeCap,
|
||||||
|
Gas: gasLimit,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: data,
|
||||||
|
}), signer, key1)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
{ // Tests against a 'recent' chain definition
|
{ // Tests against a 'recent' chain definition
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
|
@ -298,6 +310,69 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrMaxInitCodeSizeExceeded, for this we need extra Shanghai (EIP-3860) enabled.
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
|
ArchimedesBlock: big.NewInt(0),
|
||||||
|
ShanghaiBlock: big.NewInt(0),
|
||||||
|
},
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||||
|
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||||
|
Nonce: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genesis = gspec.MustCommit(db)
|
||||||
|
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||||
|
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||||
|
smallInitCode = [320]byte{}
|
||||||
|
)
|
||||||
|
defer blockchain.Stop()
|
||||||
|
for i, tt := range []struct {
|
||||||
|
txs []*types.Transaction
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{ // ErrMaxInitCodeSizeExceeded
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0x7b33776d375660694a23ef992c090265682f3687607e0099b14503fdb65d73e3]: max initcode size exceeded: code size 49153 limit 49152",
|
||||||
|
},
|
||||||
|
{ // ErrIntrinsicGas: Not enough gas to cover init code
|
||||||
|
txs: []*types.Transaction{
|
||||||
|
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
|
||||||
|
},
|
||||||
|
want: "could not apply tx 0 [0x98e54c5ecfa7986a66480d65ba32f2c6a2a6aedc3a67abb91b1e118b0717ed2d]: intrinsic gas too low: have 54299, want 54300",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("block imported without errors")
|
||||||
|
}
|
||||||
|
if have, want := err.Error(), tt.want; have != want {
|
||||||
|
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
|
// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func (result *ExecutionResult) Revert() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
var gas uint64
|
var gas uint64
|
||||||
if isContractCreation && isHomestead {
|
if isContractCreation && isHomestead {
|
||||||
|
|
@ -129,8 +129,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||||
} else {
|
} else {
|
||||||
gas = params.TxGas
|
gas = params.TxGas
|
||||||
}
|
}
|
||||||
|
dataLen := uint64(len(data))
|
||||||
// Bump the required gas by the amount of transactional data
|
// Bump the required gas by the amount of transactional data
|
||||||
if len(data) > 0 {
|
if dataLen > 0 {
|
||||||
// Zero and non-zero bytes are priced differently
|
// Zero and non-zero bytes are priced differently
|
||||||
var nz uint64
|
var nz uint64
|
||||||
for _, byt := range data {
|
for _, byt := range data {
|
||||||
|
|
@ -148,11 +149,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||||
}
|
}
|
||||||
gas += nz * nonZeroGas
|
gas += nz * nonZeroGas
|
||||||
|
|
||||||
z := uint64(len(data)) - nz
|
z := dataLen - nz
|
||||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += z * params.TxDataZeroGas
|
gas += z * params.TxDataZeroGas
|
||||||
|
|
||||||
|
if isContractCreation && isEIP3860 {
|
||||||
|
lenWords := toWordSize(dataLen)
|
||||||
|
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
gas += lenWords * params.InitCodeWordGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if accessList != nil {
|
if accessList != nil {
|
||||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||||
|
|
@ -161,6 +170,15 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toWordSize returns the ceiled word size required for init code payment calculation.
|
||||||
|
func toWordSize(size uint64) uint64 {
|
||||||
|
if size > math.MaxUint64-31 {
|
||||||
|
return math.MaxUint64/32 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size + 31) / 32
|
||||||
|
}
|
||||||
|
|
||||||
// NewStateTransition initialises and returns a new state transition object.
|
// NewStateTransition initialises and returns a new state transition object.
|
||||||
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||||
l1Fee := new(big.Int)
|
l1Fee := new(big.Int)
|
||||||
|
|
@ -318,11 +336,12 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
sender := vm.AccountRef(msg.From())
|
sender := vm.AccountRef(msg.From())
|
||||||
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
|
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
|
||||||
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
|
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
|
||||||
|
shanghai := st.evm.ChainConfig().IsShanghai(st.evm.Context.BlockNumber)
|
||||||
london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
|
london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
|
||||||
contractCreation := msg.To() == nil
|
contractCreation := msg.To() == nil
|
||||||
|
|
||||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
|
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul, shanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -336,10 +355,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
|
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber)
|
||||||
|
|
||||||
|
// Check whether the init code size has been exceeded.
|
||||||
|
if rules.IsShanghai && contractCreation && len(st.data) > params.MaxInitCodeSize {
|
||||||
|
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize)
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the initial access list.
|
// Set up the initial access list.
|
||||||
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsBerlin {
|
if rules.IsBerlin {
|
||||||
st.state.PrepareAccessList(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
st.state.PrepareAccessList(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ret []byte
|
ret []byte
|
||||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,7 @@ type TxPool struct {
|
||||||
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
||||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||||
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
|
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
|
||||||
|
shanghai bool // Fork indicator whether we are in the Shanghai stage.
|
||||||
|
|
||||||
currentState *state.StateDB // Current state in the blockchain head
|
currentState *state.StateDB // Current state in the blockchain head
|
||||||
pendingNonces *txNoncer // Pending state tracking virtual nonces
|
pendingNonces *txNoncer // Pending state tracking virtual nonces
|
||||||
|
|
@ -653,7 +654,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
return ErrInsufficientFunds
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
// Ensure the transaction has more gas than the basic tx fee.
|
// Ensure the transaction has more gas than the basic tx fee.
|
||||||
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
|
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1332,6 +1333,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
||||||
|
|
||||||
pool.eip2718 = pool.chainconfig.Scroll.EnableEIP2718 && pool.chainconfig.IsBerlin(next)
|
pool.eip2718 = pool.chainconfig.Scroll.EnableEIP2718 && pool.chainconfig.IsBerlin(next)
|
||||||
pool.eip1559 = pool.chainconfig.Scroll.EnableEIP1559 && pool.chainconfig.IsLondon(next)
|
pool.eip1559 = pool.chainconfig.Scroll.EnableEIP1559 && pool.chainconfig.IsLondon(next)
|
||||||
|
pool.shanghai = pool.chainconfig.IsShanghai(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// promoteExecutables moves transactions that have become processable from the
|
// promoteExecutables moves transactions that have become processable from the
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
|
|
||||||
var activators = map[int]func(*JumpTable){
|
var activators = map[int]func(*JumpTable){
|
||||||
3855: enable3855,
|
3855: enable3855,
|
||||||
|
3860: enable3860,
|
||||||
3529: enable3529,
|
3529: enable3529,
|
||||||
3198: enable3198,
|
3198: enable3198,
|
||||||
2929: enable2929,
|
2929: enable2929,
|
||||||
|
|
@ -194,3 +195,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||||
scope.Stack.push(new(uint256.Int))
|
scope.Stack.push(new(uint256.Int))
|
||||||
return nil, nil
|
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")
|
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||||
ErrContractAddressCollision = errors.New("contract address collision")
|
ErrContractAddressCollision = errors.New("contract address collision")
|
||||||
ErrExecutionReverted = errors.New("execution reverted")
|
ErrExecutionReverted = errors.New("execution reverted")
|
||||||
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||||
ErrInvalidJump = errors.New("invalid jump destination")
|
ErrInvalidJump = errors.New("invalid jump destination")
|
||||||
ErrWriteProtection = errors.New("write protection")
|
ErrWriteProtection = errors.New("write protection")
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,40 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
||||||
return gas, nil
|
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) {
|
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -612,7 +612,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas)
|
scope.Contract.UseGas(gas)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||||
if cfg.JumpTable[STOP] == nil {
|
if cfg.JumpTable[STOP] == nil {
|
||||||
var jt JumpTable
|
var jt JumpTable
|
||||||
switch {
|
switch {
|
||||||
|
case evm.chainRules.IsShanghai:
|
||||||
|
jt = shanghaiInstructionSet
|
||||||
case evm.chainRules.IsLondon:
|
case evm.chainRules.IsLondon:
|
||||||
jt = londonInstructionSet
|
jt = londonInstructionSet
|
||||||
case evm.chainRules.IsBerlin:
|
case evm.chainRules.IsBerlin:
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,18 @@ var (
|
||||||
istanbulInstructionSet = newIstanbulInstructionSet()
|
istanbulInstructionSet = newIstanbulInstructionSet()
|
||||||
berlinInstructionSet = newBerlinInstructionSet()
|
berlinInstructionSet = newBerlinInstructionSet()
|
||||||
londonInstructionSet = newLondonInstructionSet()
|
londonInstructionSet = newLondonInstructionSet()
|
||||||
|
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
type JumpTable [256]*operation
|
type JumpTable [256]*operation
|
||||||
|
|
||||||
|
func newShanghaiInstructionSet() JumpTable {
|
||||||
|
instructionSet := newLondonInstructionSet()
|
||||||
|
enable3860(&instructionSet)
|
||||||
|
return instructionSet
|
||||||
|
}
|
||||||
|
|
||||||
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
// newLondonInstructionSet returns the frontier, homestead, byzantium,
|
||||||
// contantinople, istanbul, petersburg, berlin and london instructions.
|
// contantinople, istanbul, petersburg, berlin and london instructions.
|
||||||
func newLondonInstructionSet() JumpTable {
|
func newLondonInstructionSet() JumpTable {
|
||||||
|
|
|
||||||
|
|
@ -704,7 +704,8 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
|
||||||
// Compute intrinsic gas
|
// Compute intrinsic gas
|
||||||
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
||||||
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
||||||
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
|
isShanghai := env.ChainConfig().IsShanghai(env.Context.BlockNumber)
|
||||||
|
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul, isShanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ type TxPool struct {
|
||||||
|
|
||||||
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
istanbul bool // Fork indicator whether we are in the istanbul stage.
|
||||||
eip2718 bool // Fork indicator whether we are in the eip2718 stage.
|
eip2718 bool // Fork indicator whether we are in the eip2718 stage.
|
||||||
|
shanghai bool // Fork indicator whether we are in the shanghai stage.
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxRelayBackend provides an interface to the mechanism that forwards transacions
|
// TxRelayBackend provides an interface to the mechanism that forwards transacions
|
||||||
|
|
@ -316,6 +317,7 @@ func (pool *TxPool) setNewHead(head *types.Header) {
|
||||||
next := new(big.Int).Add(head.Number, big.NewInt(1))
|
next := new(big.Int).Add(head.Number, big.NewInt(1))
|
||||||
pool.istanbul = pool.config.IsIstanbul(next)
|
pool.istanbul = pool.config.IsIstanbul(next)
|
||||||
pool.eip2718 = pool.config.IsBerlin(next)
|
pool.eip2718 = pool.config.IsBerlin(next)
|
||||||
|
pool.shanghai = pool.config.IsShanghai(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the light transaction pool
|
// Stop stops the light transaction pool
|
||||||
|
|
@ -383,7 +385,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should supply enough intrinsic gas
|
// Should supply enough intrinsic gas
|
||||||
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul)
|
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,10 @@ const (
|
||||||
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
|
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
|
||||||
CallStipend uint64 = 2300 // Free gas given at beginning of call.
|
CallStipend uint64 = 2300 // Free gas given at beginning of call.
|
||||||
|
|
||||||
Sha3Gas uint64 = 30 // Once per SHA3 operation.
|
Sha3Gas uint64 = 30 // Once per SHA3 operation.
|
||||||
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
|
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
|
||||||
|
Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
|
||||||
|
InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract.
|
||||||
|
|
||||||
SstoreSetGas uint64 = 20000 // Once per SSTORE operation.
|
SstoreSetGas uint64 = 20000 // Once per SSTORE operation.
|
||||||
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
|
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
|
||||||
|
|
@ -122,7 +124,8 @@ const (
|
||||||
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
|
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
|
||||||
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
|
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
|
||||||
|
|
||||||
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
|
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
|
||||||
|
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
|
||||||
|
|
||||||
// Precompiled contract gas prices
|
// Precompiled contract gas prices
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Intrinsic gas
|
// Intrinsic gas
|
||||||
requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul)
|
requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue