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

Supplement baseFee and gasLimit for pre gingerbread blocks #302

Merged
merged 19 commits into from
Jan 15, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions core/rawdb/celo_accessors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package rawdb

import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
)

var (
CeloPreGingerbreadBlockBaseFeePrefix = []byte("celoPgbBlockBaseFee-") // CeloPreGingerbreadBlockBaseFeePrefix + block hash -> BaseFee
)

// preGingerbreadBlockBaseFeeKey calculates a database key of pre-Gingerbread block BaseFee for the given block hash
func preGingerbreadBlockBaseFeeKey(hash common.Hash) []byte {
return append(CeloPreGingerbreadBlockBaseFeePrefix, hash[:]...)
}

// ReadPreGingerbreadBlockBaseFee reads BaseFee of pre-Gingerbread block from the given database for the given block hash
func ReadPreGingerbreadBlockBaseFee(db ethdb.KeyValueReader, blockHash common.Hash) (*big.Int, error) {
data, err := db.Get(preGingerbreadBlockBaseFeeKey(blockHash))
if err != nil {
return nil, fmt.Errorf("error retrieving pre gingerbread base fee for block: %s, error: %w", blockHash, err)
}
if len(data) == 0 {
return nil, nil
}

return new(big.Int).SetBytes(data), nil
}

// WritePreGingerbreadBlockBaseFee writes BaseFee of pre-Gingerbread block to the given database at the given block hash
func WritePreGingerbreadBlockBaseFee(db ethdb.KeyValueWriter, blockHash common.Hash, baseFee *big.Int) error {
return db.Put(preGingerbreadBlockBaseFeeKey(blockHash), baseFee.Bytes())
}
33 changes: 33 additions & 0 deletions core/rawdb/celo_accessors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package rawdb

import (
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestReadAndWritePreGingerbreadBlockBaseFee tests reading and writing pre-gingerbread block base fee to database
func TestReadAndWritePreGingerbreadBlockBaseFee(t *testing.T) {
db := NewMemoryDatabase()

hash := common.HexToHash("0x1")
value := big.NewInt(1234)

// Make sure it returns an error for a nonexistent record
record0, err := ReadPreGingerbreadBlockBaseFee(db, hash)
assert.ErrorContains(t, err, fmt.Sprintf("error retrieving pre gingerbread base fee for block: %s, error: not found", hash.String()))
require.Nil(t, record0)

// Write data
err = WritePreGingerbreadBlockBaseFee(db, hash, value)
require.NoError(t, err)

// Read data
record, err := ReadPreGingerbreadBlockBaseFee(db, hash)
require.NoError(t, err)
assert.Equal(t, value, record)
}
6 changes: 6 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@ func decodeHash(s string) (h common.Hash, inputLength int, err error) {
func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) {
header, err := api.b.HeaderByNumber(ctx, number)
if header != nil && err == nil {
header := PopulatePreGingerbreadHeaderFields(ctx, api.b, header)
response := RPCMarshalHeader(header)
if number == rpc.PendingBlockNumber && api.b.ChainConfig().Optimism == nil {
// Pending header need to nil out a few fields
Expand All @@ -859,6 +860,7 @@ func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.Bloc
func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} {
header, _ := api.b.HeaderByHash(ctx, hash)
if header != nil {
header := PopulatePreGingerbreadHeaderFields(ctx, api.b, header)
return RPCMarshalHeader(header)
}
return nil
Expand All @@ -874,6 +876,7 @@ func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash)
func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := api.b.BlockByNumber(ctx, number)
if block != nil && err == nil {
block := PopulatePreGingerbreadBlockFields(ctx, api.b, block)
response, err := RPCMarshalBlock(ctx, block, true, fullTx, api.b.ChainConfig(), api.b)
if err == nil && number == rpc.PendingBlockNumber && api.b.ChainConfig().Optimism == nil {
// Pending blocks need to nil out a few fields
Expand All @@ -891,6 +894,7 @@ func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.Block
func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) {
block, err := api.b.BlockByHash(ctx, hash)
if block != nil {
block := PopulatePreGingerbreadBlockFields(ctx, api.b, block)
return RPCMarshalBlock(ctx, block, true, fullTx, api.b.ChainConfig(), api.b)
}
return nil, err
Expand All @@ -906,6 +910,7 @@ func (api *BlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blo
return nil, nil
}
block = types.NewBlockWithHeader(uncles[index])
block = PopulatePreGingerbreadBlockFields(ctx, api.b, block)
return RPCMarshalBlock(ctx, block, false, false, api.b.ChainConfig(), api.b)
}
return nil, err
Expand All @@ -921,6 +926,7 @@ func (api *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, block
return nil, nil
}
block = types.NewBlockWithHeader(uncles[index])
block = PopulatePreGingerbreadBlockFields(ctx, api.b, block)
return RPCMarshalBlock(ctx, block, false, false, api.b.ChainConfig(), api.b)
}
return nil, err
Expand Down
153 changes: 153 additions & 0 deletions internal/ethapi/celo_block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package ethapi

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

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)

var (
gasPriceMinimumABIJson = `[{"inputs":[{"internalType":"uint256","name":"gasPriceMinimum","type":"uint256"}],"name":"GasPriceMinimumUpdated","outputs":[],"type":"event"}]`
gasPriceMinimumABI abi.ABI
)

func init() {
parsedAbi, _ := abi.JSON(strings.NewReader(gasPriceMinimumABIJson))
gasPriceMinimumABI = parsedAbi
}

// PopulatePreGingerbreadBlockFields populates the baseFee and gasLimit fields of the block for pre-gingerbread blocks
func PopulatePreGingerbreadBlockFields(ctx context.Context, backend CeloBackend, block *types.Block) *types.Block {
return block.WithSeal(
PopulatePreGingerbreadHeaderFields(ctx, backend, block.Header()),
)
}

// PopulatePreGingerbreadHeaderFields populates the baseFee and gasLimit fields of the header for pre-gingerbread blocks
func PopulatePreGingerbreadHeaderFields(ctx context.Context, backend CeloBackend, header *types.Header) *types.Header {
// If the block is post-Gingerbread, return the header as is
if backend.ChainConfig().IsGingerbread(header.Number) {
return header
}

var (
gasLimit *big.Int
baseFee *big.Int
err error
)

if chainId := backend.ChainConfig().ChainID; chainId != nil {
gasLimit = retrievePreGingerbreadGasLimit(chainId.Uint64(), header.Number)
}

baseFee, err = rawdb.ReadPreGingerbreadBlockBaseFee(backend.ChainDb(), header.Hash())
if err != nil {
log.Debug("failed to load pre-Gingerbread block base fee from database", "block", header.Number.Uint64(), "err", err)
}
if baseFee == nil {
// If the record is not found, get the values and store them
baseFee, err = retrievePreGingerbreadBlockBaseFee(ctx, backend, header.Number)
if err != nil {
log.Debug("Not adding to RPC response, failed to retrieve pre-Gingerbread block base fee", "block", header.Number.Uint64(), "err", err)
}

// Store the base fee for future use
if baseFee != nil {
err = rawdb.WritePreGingerbreadBlockBaseFee(backend.ChainDb(), header.Hash(), baseFee)
if err != nil {
log.Debug("failed to write pre-Gingerbread block base fee", "block", header.Number.Uint64(), "err", err)
}
}
}

if baseFee != nil {
header.BaseFee = baseFee
}
if gasLimit != nil {
header.GasLimit = gasLimit.Uint64()
}

return header
}

// retrievePreGingerbreadGasLimit retrieves a gas limit at given height from hardcoded values
func retrievePreGingerbreadGasLimit(chainId uint64, height *big.Int) *big.Int {
limits, ok := params.PreGingerbreadNetworkGasLimits[chainId]
if !ok {
log.Debug("Not adding gasLimit to RPC response, unknown network", "chainID", chainId)
return nil
}

return new(big.Int).SetUint64(limits.Limit(height))
}

// retrievePreGingerbreadBlockBaseFee retrieves a base fee at given height from the previous block
func retrievePreGingerbreadBlockBaseFee(ctx context.Context, backend CeloBackend, height *big.Int) (*big.Int, error) {
if height.Cmp(common.Big0) <= 0 {
return nil, nil
}

prevHeight := height.Uint64() - 1
prevBlock, err := backend.BlockByNumber(ctx, rpc.BlockNumber(prevHeight))
if err != nil {
return nil, err
}
if prevBlock == nil {
return nil, fmt.Errorf("block #%d not found", prevHeight)
}

prevReceipts, err := backend.GetReceipts(ctx, prevBlock.Hash())
if err != nil {
return nil, err
}

numTxs, numReceipts := len(prevBlock.Transactions()), len(prevReceipts)
palango marked this conversation as resolved.
Show resolved Hide resolved
if numReceipts <= numTxs {
return nil, fmt.Errorf("receipts of block #%d don't contain system logs", prevHeight)
}

systemReceipt := prevReceipts[numTxs]
for _, logRecord := range systemReceipt.Logs {
if logRecord.Topics[0] != gasPriceMinimumABI.Events["GasPriceMinimumUpdated"].ID {
continue
}

baseFee, err := parseGasPriceMinimumUpdated(logRecord.Data)
if err != nil {
return nil, fmt.Errorf("failed to extract GasPriceMinimumUpdated event from system logs: %w", err)
}

return baseFee, nil
}

return nil, fmt.Errorf("an event GasPriceMinimumUpdated is not included in receipts of block #%d", prevHeight)
}

// parseGasPriceMinimumUpdated parses the data of GasPriceMinimumUpdated event
func parseGasPriceMinimumUpdated(data []byte) (*big.Int, error) {
values, err := gasPriceMinimumABI.Unpack("GasPriceMinimumUpdated", data)
if err != nil {
return nil, err
}

// safe check, actually Unpack will parse first 32 bytes as a single value
if len(values) != 1 {
return nil, fmt.Errorf("unexpected format of values in GasPriceMinimumUpdated event")
}

baseFee, ok := values[0].(*big.Int)
if !ok {
return nil, fmt.Errorf("unexpected base fee type in GasPriceMinimumUpdated event: expected *big.Int, got %T", values[0])
}

return baseFee, nil
}
Loading
Loading