Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -157,16 +157,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
GasLimit: pre.Env.GasLimit,
GetHash: getHash,
}
env := &pre.Env
env.BaseFee = eip1559.CalcBaseFeeDBFT(chainConfig, &types.Header{
Number: new(big.Int).SetUint64(env.Number - 1),
BaseFee: env.ParentBaseFee,
GasUsed: env.ParentGasUsed,
GasLimit: env.ParentGasLimit,
}, statedb)
// If currentBaseFee is defined, add it to the vmContext.
// If currentBaseFee is defined, add it to the vmContext and to the state DB.
if pre.Env.BaseFee != nil {
vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee)
if chainConfig.IsNeoXBurn(vmContext.BlockNumber, vmContext.Time) {
statedb.SetState(systemcontracts.PolicyProxyHash, systemcontracts.GetBaseFeeStateHash(), common.BigToHash(pre.Env.BaseFee))
}
}
// If random is defined, add it to the vmContext.
if pre.Env.Random != nil {
Expand Down
14 changes: 14 additions & 0 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ func Transition(ctx *cli.Context) error {
if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil {
return err
}
if err := applyNeoXBurnChecks(&prestate.Env, chainConfig); err != nil {
return err
}
if err := applyShanghaiChecks(&prestate.Env, chainConfig); err != nil {
return err
}
Expand All @@ -192,6 +195,17 @@ func Transition(ctx *cli.Context) error {
return dispatchOutput(ctx, baseDir, result, collector, body)
}

func applyNeoXBurnChecks(env *stEnv, chainConfig *params.ChainConfig) error {
if !chainConfig.IsNeoXBurn(big.NewInt(int64(env.Number)), env.Timestamp) {
return nil
}
// Sanity check, to not `panic` in state_transition
if env.BaseFee == nil {
return NewError(ErrorConfig, errors.New("NeoXBurn config but missing 'currentBaseFee' in env section"))
}
return nil
}

func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
if !chainConfig.IsLondon(big.NewInt(int64(env.Number))) {
return nil
Expand Down
2 changes: 1 addition & 1 deletion consensus/dbft/dbft.go
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ func (c *DBFT) verifyCascadingFields(chain consensus.ChainHeaderReader, header *
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
return err
}
} else if err := eip1559.VerifyEIP1559HeaderDBFT(chain.Config(), parent, header); err != nil {
} else if err := eip1559.VerifyEIP1559Header(chain.Config(), parent, header); err != nil {
// Verify the header's EIP-1559 attributes.
return err
}
Expand Down
25 changes: 5 additions & 20 deletions consensus/misc/eip1559/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
if header.BaseFee == nil {
return errors.New("header is missing baseFee")
}
// For NeoXBurn block BaseFee verification is performed by consensus nodes and hence
// not included into the state-independent ordinary block verification rules.
if config.IsNeoXBurn(parent.Number, parent.Time) {
return nil
}
// Verify the baseFee is correct based on the parent header.
expectedBaseFee := CalcBaseFee(config, parent)
if header.BaseFee.Cmp(expectedBaseFee) != 0 {
Expand Down Expand Up @@ -96,26 +101,6 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
}
}

// VerifyEIP1559HeaderDBFT verifies some header attributes which were changed in EIP-1559 with dbft consensus,
// - gas limit check
// - basefee check, basefee value check moved to VerifyBlock.
func VerifyEIP1559HeaderDBFT(config *params.ChainConfig, parent, header *types.Header) error {
// Verify that the gas limit remains within allowed bounds
parentGasLimit := parent.GasLimit
if !config.IsLondon(parent.Number) {
parentGasLimit = parent.GasLimit * config.ElasticityMultiplier()
}
if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil {
return err
}
// Verify the header is not malformed
if header.BaseFee == nil {
return errors.New("header is missing baseFee")
}
// Verifing the baseFee is moved to VerifyBlock.
return nil
}

// CalcBaseFeeDBFT calculates the basefee of the header.
// if is neoXBurn fork, get basefee from Policy contract.
func CalcBaseFeeDBFT(config *params.ChainConfig, parent *types.Header, state *state.StateDB) *big.Int {
Expand Down
2 changes: 1 addition & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2473,7 +2473,7 @@ func (bc *BlockChain) getParentState(block *types.Block) (*state.StateDB, *types
}

// ProcessState processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the transaction messages using the statedb (if given) and applying any rewards to both
// the processor (coinbase) and any included uncles. It doesn't persist any data.
func (bc *BlockChain) ProcessState(block *types.Block, statedb *state.StateDB) (*state.StateDB, types.Receipts, []*types.Log, uint64, error) {
var err error
Expand Down
8 changes: 1 addition & 7 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
Expand Down Expand Up @@ -365,15 +364,10 @@ func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
}

func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
suggestTipCap, err := b.gpo.SuggestTipCap(ctx)
suggestTipCap, minGasTipCap, err := b.gpo.SuggestTipCap(ctx)
if err != nil {
return nil, err
}
stateDb, _, err := b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil {
return nil, err
}
minGasTipCap := stateDb.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMinGasTipCapStateHash()).Big()
return cmath.BigMax(suggestTipCap, minGasTipCap), nil
}

Expand Down
29 changes: 21 additions & 8 deletions eth/filters/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,33 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool)
defer pendingTxSub.Unsubscribe()

chainConfig := api.sys.backend.ChainConfig()
currHeader := api.sys.backend.CurrentHeader()
state, _, err := api.sys.backend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(currHeader.Number.Int64()))
if err != nil {
log.Error("Failed to get state to handle pending transactions", "err", err, "header number", currHeader.Number)
return
}
baseFee := eip1559.CalcBaseFeeDBFT(chainConfig, currHeader, state) // OK

for {
select {
case txs := <-txs:
// Update base fee cache only if new block is available.
if latestHeader := api.sys.backend.CurrentHeader(); latestHeader.Number.Cmp(currHeader.Number) > 0 {
currHeader = latestHeader
state, _, err = api.sys.backend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(currHeader.Number.Int64()))
if err != nil {
// We're toast, use the old base fee value.
log.Error("Failed to get state to handle pending transactions", "err", err, "header number", currHeader.Number)
} else {
baseFee = eip1559.CalcBaseFeeDBFT(chainConfig, currHeader, state)
}
}
// To keep the original behaviour, send a single tx hash in one notification.
// TODO(rjl493456442) Send a batch of tx hashes in one notification
latest := api.sys.backend.CurrentHeader()
state, _, err := api.sys.backend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(latest.Number.Int64()))
if err != nil {
log.Error("Failed to get state", "err", err, "header number", latest.Number)
}
baseFee := eip1559.CalcBaseFeeDBFT(chainConfig, latest, state)
for _, tx := range txs {
if fullTx != nil && *fullTx {
rpcTx := ethapi.NewRPCPendingTransaction(tx, latest, chainConfig, baseFee)
rpcTx := ethapi.NewRPCPendingTransaction(tx, currHeader, chainConfig, baseFee)
notifier.Notify(rpcSub.ID, rpcTx)
} else {
notifier.Notify(rpcSub.ID, tx.Hash())
Expand Down Expand Up @@ -438,7 +450,8 @@ func (api *FilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
latest := api.sys.backend.CurrentHeader()
state, _, err := api.sys.backend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(latest.Number.Int64()))
if err != nil {
log.Error("Failed to get state", "err", err, "header number", latest.Number)
log.Error("Failed to get state to retrieve filter changes", "err", err, "header number", latest.Number)
return []interface{}{}, err
}
baseFee := eip1559.CalcBaseFeeDBFT(chainConfig, latest, state)

Expand Down
16 changes: 8 additions & 8 deletions eth/gasprice/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"sync/atomic"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
Expand Down Expand Up @@ -77,17 +76,19 @@ type txGasAndReward struct {
// processBlock takes a blockFees structure with the blockNumber, the header and optionally
// the block field filled in, retrieves the block from the backend if not present yet and
// fills in the rest of the fields.
func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
func (oracle *Oracle) processBlock(ctx context.Context, bf *blockFees, percentiles []float64) {
chainconfig := oracle.backend.ChainConfig()
if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
bf.results.baseFee = new(big.Int)
}
if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
state, _, err := oracle.backend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(bf.blockNumber))
_, baseFee, _, err := oracle.suggestTipCapInternal(ctx, bf.header)
if err != nil {
log.Error("Failed to get state", "err", err, "header number", bf.blockNumber)
log.Error(fmt.Sprintf("Failed to calculate BaseFee: %s", err))
bf.results.nextBaseFee = new(big.Int)
} else {
bf.results.nextBaseFee = baseFee
}
bf.results.nextBaseFee = eip1559.CalcBaseFeeDBFT(chainconfig, bf.header, state)
} else {
bf.results.nextBaseFee = new(big.Int)
}
Expand All @@ -98,7 +99,6 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
}
if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
log.Error("Block or receipts are missing while reward percentiles are requested")
return
}

bf.results.reward = make([]*big.Int, len(percentiles))
Expand Down Expand Up @@ -267,7 +267,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
fees.block, fees.receipts = pendingBlock, pendingReceipts
fees.header = fees.block.Header()
oracle.processBlock(fees, rewardPercentiles)
oracle.processBlock(ctx, fees, rewardPercentiles)
results <- fees
} else {
cacheKey := cacheKey{number: blockNumber, percentiles: string(percentileKey)}
Expand All @@ -286,7 +286,7 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
}
if fees.header != nil && fees.err == nil {
oracle.processBlock(fees, rewardPercentiles)
oracle.processBlock(ctx, fees, rewardPercentiles)
if fees.err == nil {
oracle.historyCache.Add(cacheKey, fees.results)
}
Expand Down
55 changes: 40 additions & 15 deletions eth/gasprice/gasprice.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ package gasprice

import (
"context"
"fmt"
"math/big"
"sync"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -62,15 +65,17 @@ type OracleBackend interface {
}

// Oracle recommends gas prices based on the content of recent
// blocks. Suitable for both light and full clients.
// blocks. Suitable only for full clients.
type Oracle struct {
backend OracleBackend
lastHead common.Hash
lastPrice *big.Int
maxPrice *big.Int
ignorePrice *big.Int
cacheLock sync.RWMutex
fetchLock sync.Mutex
backend OracleBackend
lastHead common.Hash
lastPrice *big.Int
lastBaseFee *big.Int // lastBaseFee contains next BaseFee value calculated based on the lastHead block.
lastMinGasTipCap *big.Int // lastMinGasTipCap contains next MinGasTipCap value calculated based on the lastHead block.
maxPrice *big.Int
ignorePrice *big.Int
cacheLock sync.RWMutex
fetchLock sync.Mutex

checkBlocks, percentile int
maxHeaderHistory, maxBlockHistory uint64
Expand Down Expand Up @@ -149,26 +154,34 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
// necessary to add the basefee to the returned number to fall back to the legacy
// behavior.
func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, *big.Int, error) {
head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)

price, _, minGasTipCap, err := oracle.suggestTipCapInternal(ctx, head)
return price, minGasTipCap, err
}

// suggestTipCapInternal return GAS price, BaseFee and minGasTipCap for the specified block.
// It updates the cache for the specified block height if needed.
func (oracle *Oracle) suggestTipCapInternal(ctx context.Context, head *types.Header) (*big.Int, *big.Int, *big.Int, error) {
headHash := head.Hash()

// If the latest gasprice is still available, return it.
oracle.cacheLock.RLock()
lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
lastHead, lastPrice, lastBaseFee, lastMinGasTipCap := oracle.lastHead, oracle.lastPrice, oracle.lastBaseFee, oracle.lastMinGasTipCap
oracle.cacheLock.RUnlock()
if headHash == lastHead {
return new(big.Int).Set(lastPrice), nil
return new(big.Int).Set(lastPrice), new(big.Int).Set(lastBaseFee), new(big.Int).Set(lastMinGasTipCap), nil
}
oracle.fetchLock.Lock()
defer oracle.fetchLock.Unlock()

// Try checking the cache again, maybe the last fetch fetched what we need
oracle.cacheLock.RLock()
lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
lastHead, lastPrice, lastBaseFee, lastMinGasTipCap = oracle.lastHead, oracle.lastPrice, oracle.lastBaseFee, oracle.lastMinGasTipCap
oracle.cacheLock.RUnlock()
if headHash == lastHead {
return new(big.Int).Set(lastPrice), nil
return new(big.Int).Set(lastPrice), new(big.Int).Set(lastBaseFee), new(big.Int).Set(lastMinGasTipCap), nil
}
var (
sent, exp int
Expand All @@ -187,7 +200,7 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
res := <-result
if res.err != nil {
close(quit)
return new(big.Int).Set(lastPrice), res.err
return new(big.Int).Set(lastPrice), new(big.Int).Set(lastBaseFee), new(big.Int).Set(lastMinGasTipCap), res.err
}
exp--
// Nothing returned. There are two special cases here:
Expand Down Expand Up @@ -216,12 +229,24 @@ func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
if price.Cmp(oracle.maxPrice) > 0 {
price = new(big.Int).Set(oracle.maxPrice)
}
if cfg := oracle.backend.ChainConfig(); cfg.IsLondon(head.Number) {
state, _, err := oracle.backend.StateAndHeaderByNumber(ctx, rpc.BlockNumber(head.Number.Uint64()))
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get state at %d to calculate base fee: %w", head.Number.Uint64(), err)
}
lastBaseFee = eip1559.CalcBaseFeeDBFT(cfg, head, state) // OK
if cfg.IsNeoXBurn(head.Number, head.Time) {
lastMinGasTipCap = state.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMinGasTipCapStateHash()).Big()
}
}
oracle.cacheLock.Lock()
oracle.lastHead = headHash
oracle.lastPrice = price
oracle.lastBaseFee = lastBaseFee
oracle.lastMinGasTipCap = lastMinGasTipCap
oracle.cacheLock.Unlock()

return new(big.Int).Set(price), nil
return new(big.Int).Set(price), new(big.Int).Set(lastBaseFee), new(big.Int).Set(lastMinGasTipCap), nil
}

type results struct {
Expand Down
2 changes: 1 addition & 1 deletion eth/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func TestSuggestTipCap(t *testing.T) {
oracle := NewOracle(backend, config)

// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
got, err := oracle.SuggestTipCap(context.Background())
got, _, err := oracle.SuggestTipCap(context.Background())
backend.teardown()
if err != nil {
t.Fatalf("Failed to retrieve recommended gas price: %v", err)
Expand Down
Loading