Skip to content

Commit

Permalink
Add totalDifficulty field to JSON-RPC schema of Celo1 block (#310)
Browse files Browse the repository at this point in the history
* Add totalDifficulty field to JSON-RPC response for Celo1 header & block

* Fix test

* Remove new line

---------

Co-authored-by: piersy <[email protected]>
  • Loading branch information
Kourin1996 and piersy authored Jan 21, 2025
1 parent 1d58f64 commit 5df6a68
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 4 deletions.
17 changes: 13 additions & 4 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.Bloc
header, err := api.b.HeaderByNumber(ctx, number)
if header != nil && err == nil {
header := PopulatePreGingerbreadHeaderFields(ctx, api.b, header)
response := RPCMarshalHeader(header)
response := RPCMarshalHeader(header, isCelo1Block(api.b.ChainConfig(), header.Time))
if number == rpc.PendingBlockNumber && api.b.ChainConfig().Optimism == nil {
// Pending header need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
Expand All @@ -861,7 +861,7 @@ func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash)
header, _ := api.b.HeaderByHash(ctx, hash)
if header != nil {
header := PopulatePreGingerbreadHeaderFields(ctx, api.b, header)
return RPCMarshalHeader(header)
return RPCMarshalHeader(header, isCelo1Block(api.b.ChainConfig(), header.Time))
}
return nil
}
Expand Down Expand Up @@ -1520,7 +1520,7 @@ func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs,
}

// RPCMarshalHeader converts the given header to the RPC output .
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
func RPCMarshalHeader(head *types.Header, isCelo1 bool) map[string]interface{} {
result := map[string]interface{}{
"number": (*hexutil.Big)(head.Number),
"hash": head.Hash(),
Expand Down Expand Up @@ -1557,14 +1557,23 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
if head.RequestsHash != nil {
result["requestsRoot"] = head.RequestsHash
}
if isCelo1 {
result["totalDifficulty"] = (*hexutil.Big)(new(big.Int).Add(head.Number, common.Big1))
}
return result
}

// isCelo1Block determines whether the given block is a Celo1 block
// based on the chain config and the provided block time
func isCelo1Block(config *params.ChainConfig, blockTime uint64) bool {
return config.Cel2Time != nil && !config.IsCel2(blockTime)
}

// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func RPCMarshalBlock(ctx context.Context, block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig, backend Backend) (map[string]interface{}, error) {
fields := RPCMarshalHeader(block.Header())
fields := RPCMarshalHeader(block.Header(), isCelo1Block(config, block.Header().Time))
fields["size"] = hexutil.Uint64(block.Size())

if inclTx {
Expand Down
82 changes: 82 additions & 0 deletions internal/ethapi/celo_api_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package ethapi

import (
"context"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/blocktest"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -531,3 +533,83 @@ func checkTxFields(
assert.Equal(t, (*hexutil.Big)(tx.GatewayFee()), rpcTx.GatewayFee)
assert.Equal(t, tx.GatewayFeeRecipient(), rpcTx.GatewayFeeRecipient)
}

// Test_isCelo1Block tests isCelo1Block function to determine whether the given block time
// corresponds to Celo1 chain based on the provided chain configuration
func Test_isCelo1Block(t *testing.T) {
cel2Time := uint64(1000)

t.Run("Non-Celo", func(t *testing.T) {
res := isCelo1Block(&params.ChainConfig{
Cel2Time: nil,
}, 1000)

assert.False(t, res)
})

t.Run("Celo1", func(t *testing.T) {
res := isCelo1Block(&params.ChainConfig{
Cel2Time: &cel2Time,
}, 500)

assert.True(t, res)
})

t.Run("Celo2", func(t *testing.T) {
res := isCelo1Block(&params.ChainConfig{
Cel2Time: &cel2Time,
}, 1000)

assert.False(t, res)
})
}

// TestRPCMarshalBlock_Celo1TotalDifficulty tests the RPCMarshalBlock function, specifically for totalDifficulty field
// It validates the result has `totalDifficulty` field only if it's Celo1 block
func TestRPCMarshalBlock_Celo1TotalDifficulty(t *testing.T) {
t.Parallel()

blockTime := uint64(1000)
block := types.NewBlock(&types.Header{Number: big.NewInt(100), Time: blockTime}, &types.Body{Transactions: []*types.Transaction{}}, nil, blocktest.NewHasher())

marshalBlock := func(t *testing.T, config *params.ChainConfig) map[string]interface{} {
t.Helper()

resp, err := RPCMarshalBlock(context.Background(), block, false, false, config, testBackend{})
if err != nil {
require.NoError(t, err)
}

return resp
}

t.Run("Non-Celo", func(t *testing.T) {
config := *params.MainnetChainConfig

res := marshalBlock(t, &config)

assert.Equal(t, nil, res["totalDifficulty"])
})

t.Run("Celo1", func(t *testing.T) {
expected := (*hexutil.Big)(new(big.Int).Add(block.Number(), common.Big1))

cel2Time := blockTime + 500
config := *params.MainnetChainConfig
config.Cel2Time = &cel2Time

res := marshalBlock(t, &config)

assert.Equal(t, expected, res["totalDifficulty"])
})

t.Run("Celo2", func(t *testing.T) {
cel2Time := blockTime - 500
config := *params.MainnetChainConfig
config.Cel2Time = &cel2Time

res := marshalBlock(t, &config)

assert.Equal(t, nil, res["totalDifficulty"])
})
}

0 comments on commit 5df6a68

Please sign in to comment.