Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth/gasprice/feehistory: support finalized block #25442

Merged
merged 1 commit into from Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 51 additions & 27 deletions eth/gasprice/feehistory.go
Expand Up @@ -137,44 +137,68 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
// also returned if requested and available.
// Note: an error is only returned if retrieving the head header has failed. If there are no
// retrievable blocks in the specified range then zero block count is returned with no error.
func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) {
func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) {
var (
headBlock rpc.BlockNumber
headBlock *types.Header
pendingBlock *types.Block
pendingReceipts types.Receipts
err error
)
// query either pending block or head header and set headBlock
if lastBlock == rpc.PendingBlockNumber {
if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
lastBlock = rpc.BlockNumber(pendingBlock.NumberU64())
headBlock = lastBlock - 1
} else {
// pending block not supported by backend, process until latest block
lastBlock = rpc.LatestBlockNumber
blocks--
if blocks == 0 {
return nil, nil, 0, 0, nil

// Get the chain's current head.
if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil {
return nil, nil, 0, 0, err
}
head := rpc.BlockNumber(headBlock.Number.Uint64())

// Fail if request block is beyond the chain's current head.
if head < reqEnd {
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head)
}

// Resolve block tag.
if reqEnd < 0 {
var (
resolved *types.Header
err error
)
switch reqEnd {
case rpc.PendingBlockNumber:
if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
resolved = pendingBlock.Header()
} else {
// Pending block not supported by backend, process only until latest block.
resolved = headBlock

// Update total blocks to return to account for this.
blocks--
}
case rpc.LatestBlockNumber:
// Retrieved above.
resolved = headBlock
case rpc.SafeBlockNumber:
resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
case rpc.FinalizedBlockNumber:
resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
case rpc.EarliestBlockNumber:
resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber)
}
}
if pendingBlock == nil {
// if pending block is not fetched then we retrieve the head header to get the head block number
if latestHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err == nil {
headBlock = rpc.BlockNumber(latestHeader.Number.Uint64())
} else {
if resolved == nil || err != nil {
return nil, nil, 0, 0, err
}
// Absolute number resolved.
reqEnd = rpc.BlockNumber(resolved.Number.Uint64())
}
if lastBlock == rpc.LatestBlockNumber {
lastBlock = headBlock
} else if pendingBlock == nil && lastBlock > headBlock {
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock)

// If there are no blocks to return, short circuit.
if blocks == 0 {
return nil, nil, 0, 0, nil
}
// ensure not trying to retrieve before genesis
if rpc.BlockNumber(blocks) > lastBlock+1 {
blocks = int(lastBlock + 1)
// Ensure not trying to retrieve before genesis.
if int(reqEnd+1) < blocks {
blocks = int(reqEnd + 1)
}
return pendingBlock, pendingReceipts, uint64(lastBlock), blocks, nil
return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil
}

// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
Expand Down
2 changes: 2 additions & 0 deletions eth/gasprice/feehistory_test.go
Expand Up @@ -50,6 +50,8 @@ func TestFeeHistory(t *testing.T) {
{false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
{true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
{true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
{false, 1000, 1000, 2, rpc.FinalizedBlockNumber, []float64{0, 10}, 24, 2, nil},
{false, 1000, 1000, 2, rpc.SafeBlockNumber, []float64{0, 10}, 24, 2, nil},
}
for i, c := range cases {
config := Config{
Expand Down
21 changes: 21 additions & 0 deletions eth/gasprice/gasprice_test.go
Expand Up @@ -45,6 +45,15 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber
if number > testHead {
return nil, nil
}
if number == rpc.EarliestBlockNumber {
number = 0
}
if number == rpc.FinalizedBlockNumber {
return b.chain.CurrentFinalizedBlock().Header(), nil
}
if number == rpc.SafeBlockNumber {
return b.chain.CurrentSafeBlock().Header(), nil
}
if number == rpc.LatestBlockNumber {
number = testHead
}
Expand All @@ -62,6 +71,15 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
if number > testHead {
return nil, nil
}
if number == rpc.EarliestBlockNumber {
number = 0
}
if number == rpc.FinalizedBlockNumber {
return b.chain.CurrentFinalizedBlock(), nil
}
if number == rpc.SafeBlockNumber {
return b.chain.CurrentSafeBlock(), nil
}
if number == rpc.LatestBlockNumber {
number = testHead
}
Expand Down Expand Up @@ -109,6 +127,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
config.LondonBlock = londonBlock
config.ArrowGlacierBlock = londonBlock
config.GrayGlacierBlock = londonBlock
config.TerminalTotalDifficulty = common.Big0
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
genesis, err := gspec.Commit(db)
Expand Down Expand Up @@ -150,6 +169,8 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
t.Fatalf("Failed to create local chain, %v", err)
}
chain.InsertChain(blocks)
chain.SetFinalized(chain.GetBlockByNumber(25))
chain.SetSafe(chain.GetBlockByNumber(25))
return &testBackend{chain: chain, pending: pending}
}

Expand Down