mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
EIP-3860: Limit and meter initcode (#5892)
[EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) is [included](https://github.com/ethereum/execution-specs/pull/633) into [Shanghai](https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md). TODO: similar changes to `txpool`. Co-authored-by: Andrei Maiboroda <andrei@ethereum.org>
This commit is contained in:
parent
3163f40e58
commit
9ffc457cbb
@ -69,6 +69,10 @@ var (
|
||||
// by a transaction is higher than what's left in the block.
|
||||
ErrGasLimitReached = errors.New("gas limit reached")
|
||||
|
||||
// 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
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
@ -18,11 +18,11 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/common/math"
|
||||
cmath "github.com/ledgerwatch/erigon/common/math"
|
||||
"github.com/ledgerwatch/erigon/common/u256"
|
||||
"github.com/ledgerwatch/erigon/consensus"
|
||||
@ -129,7 +129,7 @@ func (result *ExecutionResult) Revert() []byte {
|
||||
}
|
||||
|
||||
// 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, isEIP3860 bool) (uint64, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
@ -138,11 +138,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||
gas = params.TxGas
|
||||
}
|
||||
|
||||
// Auxiliary variables for overflow protection
|
||||
var product, overflow uint64
|
||||
|
||||
dataLen := uint64(len(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
|
||||
var nz uint64
|
||||
for _, byt := range data {
|
||||
@ -156,41 +154,53 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
|
||||
nonZeroGas = params.TxDataNonZeroGasEIP2028
|
||||
}
|
||||
|
||||
overflow, product = bits.Mul64(nz, nonZeroGas)
|
||||
if overflow != 0 {
|
||||
product, overflow := math.SafeMul(nz, nonZeroGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas, overflow = bits.Add64(gas, product, 0)
|
||||
if overflow != 0 {
|
||||
gas, overflow = math.SafeAdd(gas, product)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
z := uint64(len(data)) - nz
|
||||
overflow, product = bits.Mul64(z, params.TxDataZeroGas)
|
||||
if overflow != 0 {
|
||||
z := dataLen - nz
|
||||
product, overflow = math.SafeMul(z, params.TxDataZeroGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas, overflow = bits.Add64(gas, product, 0)
|
||||
if overflow != 0 {
|
||||
gas, overflow = math.SafeAdd(gas, product)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if isContractCreation && isEIP3860 {
|
||||
numWords := vm.ToWordSize(dataLen)
|
||||
product, overflow = math.SafeMul(numWords, params.InitCodeWordGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas, overflow = math.SafeAdd(gas, product)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
if accessList != nil {
|
||||
overflow, product = bits.Mul64(uint64(len(accessList)), params.TxAccessListAddressGas)
|
||||
if overflow != 0 {
|
||||
product, overflow := math.SafeMul(uint64(len(accessList)), params.TxAccessListAddressGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas, overflow = bits.Add64(gas, product, 0)
|
||||
if overflow != 0 {
|
||||
gas, overflow = math.SafeAdd(gas, product)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
overflow, product = bits.Mul64(uint64(accessList.StorageKeys()), params.TxAccessListStorageKeyGas)
|
||||
if overflow != 0 {
|
||||
product, overflow = math.SafeMul(uint64(accessList.StorageKeys()), params.TxAccessListStorageKeyGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas, overflow = bits.Add64(gas, product, 0)
|
||||
if overflow != 0 {
|
||||
gas, overflow = math.SafeAdd(gas, product)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
@ -391,7 +401,7 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
|
||||
}
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul)
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -408,6 +418,11 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
if rules.IsBerlin {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
|
@ -74,8 +74,8 @@ func getDataBig(data []byte, start *uint256.Int, size uint64) []byte {
|
||||
return getData(data, start64, size)
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for memory expansion.
|
||||
func toWordSize(size uint64) uint64 {
|
||||
// ToWordSize returns the ceiled word size required for memory expansion.
|
||||
func ToWordSize(size uint64) uint64 {
|
||||
if size > math.MaxUint64-31 {
|
||||
return math.MaxUint64/32 + 1
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
3855: enable3855,
|
||||
3860: enable3860,
|
||||
3529: enable3529,
|
||||
3198: enable3198,
|
||||
2929: enable2929,
|
||||
@ -189,3 +190,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||
scope.Stack.Push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
@ -32,6 +32,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")
|
||||
|
@ -484,6 +484,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
err = ErrContractAddressCollision
|
||||
return nil, common.Address{}, 0, err
|
||||
}
|
||||
// Check whether the init code size has been exceeded.
|
||||
if evm.chainRules.IsShanghai && len(codeAndHash.code) > params.MaxInitCodeSize {
|
||||
return nil, address, gas, ErrMaxInitCodeSizeExceeded
|
||||
}
|
||||
// Create a new account on the state
|
||||
snapshot := evm.intraBlockState.Snapshot()
|
||||
evm.intraBlockState.CreateAccount(address, true)
|
||||
|
@ -41,7 +41,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||
if newMemSize > 0x1FFFFFFFE0 {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSizeWords := ToWordSize(newMemSize)
|
||||
newMemSize = newMemSizeWords * 32
|
||||
|
||||
if newMemSize > uint64(mem.Len()) {
|
||||
@ -78,7 +78,7 @@ func memoryCopierGas(stackpos int) gasFunc {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||
if words, overflow = math.SafeMul(ToWordSize(words), params.CopyGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory,
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
if wordGas, overflow = math.SafeMul(ToWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
@ -287,18 +287,33 @@ var (
|
||||
)
|
||||
|
||||
func gasCreate2(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gasCreateImplementation(stack, mem, memorySize, params.Keccak256WordGas)
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gasCreateImplementation(stack, mem, memorySize, params.InitCodeWordGas)
|
||||
}
|
||||
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gasCreateImplementation(stack, mem, memorySize, params.Keccak256WordGas+params.InitCodeWordGas)
|
||||
}
|
||||
|
||||
func gasCreateImplementation(stack *stack.Stack, mem *Memory, memorySize uint64, wordCost uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
len, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
|
||||
numWords := ToWordSize(len)
|
||||
wordGas, overflow := math.SafeMul(numWords, wordCost)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
gas, overflow = math.SafeAdd(gas, wordGas)
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
|
@ -114,3 +114,50 @@ func TestEIP2200(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var createGasTests = []struct {
|
||||
code string
|
||||
eip3860 bool
|
||||
gasUsed uint64
|
||||
}{
|
||||
// create(0, 0, 0xc000)
|
||||
{"0x61C00060006000f0", false, 41225},
|
||||
// create(0, 0, 0xc000)
|
||||
{"0x61C00060006000f0", true, 44297},
|
||||
// create2(0, 0, 0xc000, 0)
|
||||
{"0x600061C00060006000f5", false, 50444},
|
||||
// create2(0, 0, 0xc000, 0)
|
||||
{"0x600061C00060006000f5", true, 53516},
|
||||
}
|
||||
|
||||
func TestCreateGas(t *testing.T) {
|
||||
for i, tt := range createGasTests {
|
||||
address := common.BytesToAddress([]byte("contract"))
|
||||
_, tx := memdb.NewTestTx(t)
|
||||
|
||||
s := state.New(state.NewPlainStateReader(tx))
|
||||
s.CreateAccount(address, true)
|
||||
s.SetCode(address, hexutil.MustDecode(tt.code))
|
||||
_ = s.CommitBlock(params.AllEthashProtocolChanges.Rules(0), state.NewPlainStateWriter(tx, tx, 0))
|
||||
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(IntraBlockState, common.Address, *uint256.Int) bool { return true },
|
||||
Transfer: func(IntraBlockState, common.Address, common.Address, *uint256.Int, bool) {},
|
||||
}
|
||||
config := Config{}
|
||||
if tt.eip3860 {
|
||||
config.ExtraEips = []int{3860}
|
||||
}
|
||||
|
||||
vmenv := NewEVM(vmctx, TxContext{}, s, params.AllEthashProtocolChanges, config)
|
||||
|
||||
var startGas uint64 = math.MaxUint64
|
||||
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int), false /* bailout */)
|
||||
if err != nil {
|
||||
t.Errorf("test %d execution failed: %v", i, err)
|
||||
}
|
||||
if gasUsed := startGas - gas; gasUsed != tt.gasUsed {
|
||||
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,17 @@ type VM struct {
|
||||
returnData []byte // Last CALL's return data for subsequent reuse
|
||||
}
|
||||
|
||||
func copyJumpTable(jt *JumpTable) *JumpTable {
|
||||
var copy JumpTable
|
||||
for i, op := range jt {
|
||||
if op != nil {
|
||||
opCopy := *op
|
||||
copy[i] = &opCopy
|
||||
}
|
||||
}
|
||||
return ©
|
||||
}
|
||||
|
||||
// NewEVMInterpreter returns a new instance of the Interpreter.
|
||||
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||
var jt *JumpTable
|
||||
@ -115,6 +126,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
||||
jt = &frontierInstructionSet
|
||||
}
|
||||
if len(cfg.ExtraEips) > 0 {
|
||||
jt = copyJumpTable(jt)
|
||||
for i, eip := range cfg.ExtraEips {
|
||||
if err := EnableEIP(eip, jt); err != nil {
|
||||
// Disable it, so caller can check if it's activated or not
|
||||
@ -299,7 +311,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
}
|
||||
// memory is expanded in words of 32 bytes. Gas
|
||||
// is also calculated in words.
|
||||
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
||||
if memorySize, overflow = math.SafeMul(ToWordSize(memSize), 32); overflow {
|
||||
return nil, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ func newCancunInstructionSet() JumpTable {
|
||||
func newShanghaiInstructionSet() JumpTable {
|
||||
instructionSet := newLondonInstructionSet()
|
||||
enable3855(&instructionSet) // PUSH0 instruction https://eips.ethereum.org/EIPS/eip-3855
|
||||
enable3860(&instructionSet) // Limit and meter initcode https://eips.ethereum.org/EIPS/eip-3860
|
||||
return instructionSet
|
||||
}
|
||||
|
||||
|
@ -600,7 +600,8 @@ func (jst *Tracer) CaptureStart(env *vm.EVM, depth int, from common.Address, to
|
||||
// Compute intrinsic gas
|
||||
isHomestead := env.ChainConfig().IsHomestead(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 {
|
||||
return
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ const (
|
||||
|
||||
Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
|
||||
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 SLOAD operation.
|
||||
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
|
||||
@ -125,7 +126,8 @@ const (
|
||||
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
|
||||
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
|
||||
|
||||
|
@ -71,7 +71,7 @@ func (tt *TransactionTest) Run(chainID *big.Int) error {
|
||||
sender := msg.From()
|
||||
|
||||
// Intrinsic gas
|
||||
requiredGas, err := core.IntrinsicGas(msg.Data(), msg.AccessList(), msg.To() == nil, rules.IsHomestead, rules.IsIstanbul)
|
||||
requiredGas, err := core.IntrinsicGas(msg.Data(), msg.AccessList(), msg.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user