Skip to content

Commit

Permalink
Hardcode pre gingerbread block gas limits for existing chains (#2216)
Browse files Browse the repository at this point in the history
Ensures that mainnet, alfajores and baklava will return the correct gas limit
on pre gingerbread blocks when running in eth compatibility mode.

See this ticket for more detail and the correct gas limit values:
#2214
  • Loading branch information
piersy authored Jan 23, 2024
1 parent 3a9fda2 commit 06a5ca4
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 37 deletions.
83 changes: 46 additions & 37 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -706,64 +706,69 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H
// only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, number)
if block != nil && err == nil {
response, err := s.rpcMarshalBlock(ctx, block, true, fullTx)

if err == nil {
if block == nil || err != nil {
return nil, err
}
response, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
if err == nil {
if s.b.RPCEthCompatibility() {
addEthCompatibilityFields(ctx, response, s.b, block)
if number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
}
}
if number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
}
}
return response, err
}
return nil, err
return response, err
}

// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
// detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByHash(ctx, hash)
if block != nil {
result, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
if err != nil {
return nil, err
}
if block == nil {
return nil, err
}
result, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
if err != nil {
return nil, err
}
if s.b.RPCEthCompatibility() {
addEthCompatibilityFields(ctx, result, s.b, block)
return result, nil
}
return nil, err
return result, nil
}

// addEthCompatibilityFields seeks to work around the incompatibility of celo
// and ethers.js (and potentially other web3 clients) by adding fields to our
// rpc response that ethers.js depends upon.
// See https://github.com/celo-org/celo-blockchain/issues/1945
func addEthCompatibilityFields(ctx context.Context, response map[string]interface{}, b Backend, block *types.Block) {
isGingerbread := b.ChainConfig().IsGingerbread(block.Number())
if !b.RPCEthCompatibility() {
if !isGingerbread {
delete(response, "gasLimit")
}
return
}

header := block.Header()
if !isGingerbread {
// Before Gingerbread, the header did not include the gasLimit, so we have to manually add it for eth-compatible RPC responses.
hash := header.Hash()
numhash := rpc.BlockNumberOrHash{
BlockHash: &hash,
}
gasLimit, err := b.GetRealBlockGasLimit(ctx, numhash)
if err != nil {
log.Debug("Not adding gasLimit to RPC response, failed to retrieve it", "block", header.Number.Uint64(), "err", err)
if !b.ChainConfig().IsGingerbread(block.Number()) {
// Before Gingerbread, the header did not include the gasLimit, now we manually add it for old blocks.
var gasLimit uint64
var err error

// For mainnet, alfajores and baklava we have a set of hardcoded values derived from historical state that we can
// use, note ChainID might be unset, so we need to account for that.
chainId := b.ChainConfig().ChainID
if chainId != nil && params.PreGingerbreadNetworkGasLimits[chainId.Uint64()] != nil {
gasLimit = params.PreGingerbreadNetworkGasLimits[chainId.Uint64()].Limit(header.Number)
} else {
response["gasLimit"] = hexutil.Uint64(gasLimit)
// If no hardcoded limits are available for this network then we will try to look up the gas limit in the state.
hash := header.Hash()
numhash := rpc.BlockNumberOrHash{
BlockHash: &hash,
}
gasLimit, err = b.GetRealBlockGasLimit(ctx, numhash)
if err != nil {
log.Debug("Failed to retrieve gas limit for RPC block response, zero gas limit will be returned", "block", header.Number.Uint64(), "err", err)
}
}
response["gasLimit"] = hexutil.Uint64(gasLimit)
}

if header.BaseFee != nil {
Expand Down Expand Up @@ -1148,7 +1153,6 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
"miner": head.Coinbase,
"extraData": hexutil.Bytes(head.Extra),
"size": hexutil.Uint64(head.Size()),
"gasLimit": hexutil.Uint64(head.GasLimit),
"gasUsed": hexutil.Uint64(head.GasUsed),
"timestamp": hexutil.Uint64(head.Time),
"transactionsRoot": head.TxHash,
Expand All @@ -1164,6 +1168,11 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
result["mixHash"] = head.MixDigest
}

// Before the gingerbread hardfork gas limit was not part of the block header.
if head.GasLimit > 0 {
result["gasLimit"] = hexutil.Uint64(head.GasLimit)
}

if head.BaseFee != nil {
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
}
Expand Down
45 changes: 45 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,51 @@ var (
},
)
TestRules = TestChainConfig.Rules(new(big.Int))

mainnetGasLimits = &GasLimits{
changes: []LimitChange{
{big.NewInt(0), 20e6},
{big.NewInt(3317), 10e6},
{big.NewInt(3251772), 13e6},
{big.NewInt(6137285), 20e6},
{big.NewInt(13562578), 50e6},
{big.NewInt(14137511), 13e6},
{big.NewInt(21355415), 32e6},
},
}

alfajoresGasLimits = &GasLimits{
changes: []LimitChange{
{big.NewInt(0), 20e6},
{big.NewInt(912), 10e6},
{big.NewInt(1392355), 130e6},
{big.NewInt(1507905), 13e6},
{big.NewInt(4581182), 20e6},
{big.NewInt(11143973), 35e6},
},
}

baklavaGasLimits = &GasLimits{
changes: []LimitChange{
{big.NewInt(0), 20e6},
{big.NewInt(1230), 10e6},
{big.NewInt(1713181), 130e6},
{big.NewInt(1945003), 13e6},
{big.NewInt(15158971), 20e6},
},
}

// This is a hardcoded set of gas limit changes derived from historical
// state. They allow non archive nodes to return the gas limit for blocks
// before gingerbread (where we added gas limit to the block header).
// Additionally they ensure that archive nodes return the correct value at
// the beginning of the chain, before the blockchain parameters contract
// was deployed and activated.
PreGingerbreadNetworkGasLimits = map[uint64]*GasLimits{
MainnetNetworkId: mainnetGasLimits,
AlfajoresNetworkId: alfajoresGasLimits,
BaklavaNetworkId: baklavaGasLimits,
}
)

// TrustedCheckpoint represents a set of post-processed trie roots (CHT and
Expand Down
25 changes: 25 additions & 0 deletions params/gas_limts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package params

import "math/big"

type GasLimits struct {
// changes holds all gas limit changes, it is assumed that the first change ocurrs at block 0.
changes []LimitChange
}

type LimitChange struct {
block *big.Int
gasLimit uint64
}

func (g *GasLimits) Limit(block *big.Int) uint64 {
// Grab the gas limit at block 0
curr := g.changes[0].gasLimit
for _, c := range g.changes[1:] {
if block.Cmp(c.block) < 0 {
return curr
}
curr = c.gasLimit
}
return curr
}

0 comments on commit 06a5ca4

Please sign in to comment.