From de24a7a6128c44f40ab8a339b6bcb0fce6522680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 7 Nov 2023 11:55:27 +0200 Subject: [PATCH] core: nicer 0-basefee, make it work for blob fees too --- core/evm.go | 6 +++++- core/state_transition.go | 22 ++++++++++++++-------- core/vm/eips.go | 8 ++------ core/vm/evm.go | 26 +++++++++++++++----------- core/vm/runtime/env.go | 1 + core/vm/runtime/runtime.go | 3 ++- 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/core/evm.go b/core/evm.go index 46dcb31462..c4801dc797 100644 --- a/core/evm.go +++ b/core/evm.go @@ -77,11 +77,15 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common // NewEVMTxContext creates a new transaction context for a single transaction. func NewEVMTxContext(msg *Message) vm.TxContext { - return vm.TxContext{ + ctx := vm.TxContext{ Origin: msg.From, GasPrice: new(big.Int).Set(msg.GasPrice), BlobHashes: msg.BlobHashes, } + if msg.BlobGasFeeCap != nil { + ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap) + } + return ctx } // GetHashFn returns a GetHashFunc which retrieves header hashes by number diff --git a/core/state_transition.go b/core/state_transition.go index fb03c48aab..dd407b2f5b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -287,7 +287,6 @@ func (st *StateTransition) preCheck() error { msg.From.Hex(), codeHash) } } - // Make sure that transaction gasFeeCap is greater than the baseFee (post london) if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) @@ -307,7 +306,7 @@ func (st *StateTransition) preCheck() error { // This will panic if baseFee is nil, but basefee presence is verified // as part of header validation. if msg.GasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { - return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow, + return fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow, msg.From.Hex(), msg.GasFeeCap, st.evm.Context.BaseFee) } } @@ -324,17 +323,24 @@ func (st *StateTransition) preCheck() error { } } } - + // Check that the user is paying at least the current blob fee if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) { if st.blobGasUsed() > 0 { - // Check that the user is paying at least the current blob fee - blobFee := st.evm.Context.BlobBaseFee - if st.msg.BlobGasFeeCap.Cmp(blobFee) < 0 { - return fmt.Errorf("%w: address %v have %v want %v", ErrBlobFeeCapTooLow, st.msg.From.Hex(), st.msg.BlobGasFeeCap, blobFee) + // Skip the checks if gas fields are zero and blobBaseFee was explicitly disabled (eth_call) + if !st.evm.Config.NoBaseFee && msg.BlobGasFeeCap.BitLen() > 0 { + if l := msg.BlobGasFeeCap.BitLen(); l > 256 { + return fmt.Errorf("%w: address %v, blobGasFeeCap bit length: %d", ErrFeeCapVeryHigh, + msg.From.Hex(), l) + } + // This will panic if blobBaseFee is nil, but blobBaseFee presence + // is verified as part of header validation. + if msg.BlobGasFeeCap.Cmp(st.evm.Context.BlobBaseFee) < 0 { + return fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow, + msg.From.Hex(), msg.BlobGasFeeCap, st.evm.Context.BlobBaseFee) + } } } } - return st.buyGas() } diff --git a/core/vm/eips.go b/core/vm/eips.go index 44d4ba9cff..35f0a3f7c2 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -215,12 +215,8 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // opBaseFee implements BASEFEE opcode func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - if interpreter.evm.Config.NoBaseFee { - scope.Stack.push(new(uint256.Int)) - } else { - baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee) - scope.Stack.push(baseFee) - } + baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee) + scope.Stack.push(baseFee) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index 2c6cc7d484..96a23a7bec 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -72,8 +72,8 @@ type BlockContext struct { BlockNumber *big.Int // Provides information for NUMBER Time uint64 // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY - BaseFee *big.Int // Provides information for BASEFEE - BlobBaseFee *big.Int // Provides information for BLOBBASEFEE + BaseFee *big.Int // Provides information for BASEFEE (0 if vm runs with NoBaseFee flag and 0 gas price) + BlobBaseFee *big.Int // Provides information for BLOBBASEFEE (0 if vm runs with NoBaseFee flag and 0 blob gas price) Random *common.Hash // Provides information for PREVRANDAO } @@ -82,7 +82,8 @@ type BlockContext struct { type TxContext struct { // Message information Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE + GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) + BlobFeeCap *big.Int // Provides information for BLOBGASPRICE(?) (and is used to zero the blobbasefee if NoBaseFee is set) BlobHashes []common.Hash // Provides information for BLOBHASH } @@ -125,6 +126,17 @@ type EVM struct { // NewEVM returns a new EVM. The returned EVM is not thread safe and should // only ever be used *once*. func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM { + // If basefee tracking is disabled (eth_call, eth_estimateGas, etc), and no + // gas prices were specified, lower the basefee to 0 to avoid breaking EVM + // invariants (basefee < feecap) + if config.NoBaseFee { + if txCtx.GasPrice.BitLen() == 0 { + blockCtx.BaseFee = new(big.Int) + } + if txCtx.BlobFeeCap != nil && txCtx.BlobFeeCap.BitLen() == 0 { + blockCtx.BlobBaseFee = new(big.Int) + } + } evm := &EVM{ Context: blockCtx, TxContext: txCtx, @@ -160,14 +172,6 @@ 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 diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 64aa550a25..34335b8e9e 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -26,6 +26,7 @@ func NewEnv(cfg *Config) *vm.EVM { Origin: cfg.Origin, GasPrice: cfg.GasPrice, BlobHashes: cfg.BlobHashes, + BlobFeeCap: cfg.BlobFeeCap, } blockContext := vm.BlockContext{ CanTransfer: core.CanTransfer, diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index cfd7e4dbc4..d10457e7fa 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -46,6 +46,7 @@ type Config struct { BaseFee *big.Int BlobBaseFee *big.Int BlobHashes []common.Hash + BlobFeeCap *big.Int Random *common.Hash State *state.StateDB @@ -97,7 +98,7 @@ func setDefaults(cfg *Config) { cfg.BaseFee = big.NewInt(params.InitialBaseFee) } if cfg.BlobBaseFee == nil { - cfg.BlobBaseFee = new(big.Int) + cfg.BlobBaseFee = big.NewInt(params.BlobTxMinBlobGasprice) } }