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

Fix genesis block gingerbread fields #2245

Merged
merged 10 commits into from
Feb 1, 2024
11 changes: 11 additions & 0 deletions core/types/celo_additions.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package types

import "github.com/celo-org/celo-blockchain/common"

// IstanbulExtra returns the 'Extra' field of the header deserialized into an
// IstanbulExtra struct, if there is an error deserializing the 'Extra' field
// it will be returned.
Expand All @@ -16,3 +18,12 @@ func (h *Header) IstanbulExtra() (*IstanbulExtra, error) {

return h.extraValue, h.extraError
}

// ParentOrGenesisHash returns the parent hash unless this is the genesis block
// in which case it reurns the hash of the genesis block.
func (h *Header) ParentOrGenesisHash() common.Hash {
if h.Number.Uint64() == 0 {
return h.Hash()
}
return h.ParentHash
}
124 changes: 107 additions & 17 deletions e2e_test/e2e_test.go
piersy marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func init() {
// network to process the transaction.
func TestSendCelo(t *testing.T) {
ac := test.AccountConfig(3, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand All @@ -70,7 +70,7 @@ func TestSendCelo(t *testing.T) {
// and can be useful for debugging these traces.
func TestTraceSendCeloViaGoldToken(t *testing.T) {
ac := test.AccountConfig(3, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestTraceSendCeloViaGoldToken(t *testing.T) {
// Use the callTracer to trace a native CELO transfer.
func TestCallTraceTransactionNativeTransfer(t *testing.T) {
ac := test.AccountConfig(1, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestCallTraceTransactionNativeTransfer(t *testing.T) {
// Use the prestateTracer to trace a native CELO transfer.
func TestPrestateTransactionNativeTransfer(t *testing.T) {
ac := test.AccountConfig(1, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -183,7 +183,7 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) {
iterations := 5
txsPerIteration := 5
ac := test.AccountConfig(1, 1)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
gc.Istanbul.Epoch = uint64(iterations) * 50 // avoid the epoch for this test
Expand All @@ -209,7 +209,7 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) {
// We previously had an open bug for this https://github.com/celo-org/celo-blockchain/issues/1574
func TestEpochBlockMarshaling(t *testing.T) {
accounts := test.AccountConfig(1, 0)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(accounts, gingerbreadBlock)
require.NoError(t, err)

Expand Down Expand Up @@ -239,7 +239,7 @@ func TestEpochBlockMarshaling(t *testing.T) {
// validators are shut down, when they restart the network is able to continue.
func TestStartStopValidators(t *testing.T) {
ac := test.AccountConfig(4, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, _, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -376,7 +376,7 @@ func TestStartStopValidators(t *testing.T) {
// trace block code was the source of the concurrent map access.
func TestBlockTracingConcurrentMapAccess(t *testing.T) {
ac := test.AccountConfig(1, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -427,7 +427,7 @@ func TestBlockTracingConcurrentMapAccess(t *testing.T) {
// Helpful for debugging issues in TestBlockTracingConcurrentMapAccess.
func TestBlockTracingSequentialAccess(t *testing.T) {
ac := test.AccountConfig(1, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -462,7 +462,7 @@ type rpcCustomTransaction struct {
// price by the tx, which could be less than the feeCap (as in this example)
func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) {
ac := test.AccountConfig(3, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -503,7 +503,7 @@ func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) {
// GasPriceMinimum contract
func TestRPCDynamicTxGasPriceWithState(t *testing.T) {
ac := test.AccountConfig(3, 2)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
ec.TxLookupLimit = 0
Expand Down Expand Up @@ -578,7 +578,7 @@ func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyAfterGingerbread(
func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterGingerbread, alternativeCurrency bool) {
var gingerbreadBlock *big.Int
if afterGingerbread {
gingerbreadBlock = common.Big1
gingerbreadBlock = common.Big0
}
cusdAddress := common.HexToAddress("0xd008")
ac := test.AccountConfig(3, 2)
Expand Down Expand Up @@ -655,7 +655,7 @@ func pruneStateOfBlock(ctx context.Context, node *test.Node, blockNumber *big.In

func runMochaTest(t *testing.T, add_args func(*env.AccountsConfig, *genesis.Config, test.Network) []string) {
ac := test.AccountConfig(1, 1)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
Expand Down Expand Up @@ -701,7 +701,7 @@ func TestEthersJSCompatibility(t *testing.T) {
// returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks.
func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) {
ac := test.AccountConfig(1, 1)
gingerbreadBlock := common.Big1
gingerbreadBlock := common.Big0
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
require.NoError(t, err)

Expand All @@ -723,7 +723,7 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) {
}
require.Equal(t, result["sha3Uncles"], "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")

// Turn off compatibility and check fields are not present
// Turn off compatibility and check fields are still present
ec.RPCEthCompatibility = false
network, shutdown, err = test.NewNetwork(ac, gc, ec)
require.NoError(t, err)
Expand All @@ -733,15 +733,16 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) {
defer cancel()

result = make(map[string]interface{})
err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true)
err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "0x0", true)
require.NoError(t, err)

fmt.Printf("baseFee %v\n", result["baseFeePerGas"])
piersy marked this conversation as resolved.
Show resolved Hide resolved
// After Gingerbread, gasLimit should be returned directly from the header, even if
// RPCEthCompatibility is off, since it is now part of the header hash.
_, ok := result["gasLimit"]
assert.True(t, ok, "gasLimit field must be present on RPC block after Gingerbread")
_, ok = result["baseFeePerGas"]
assert.True(t, ok, "baseFeePerGas field must be present on RPC block")
assert.True(t, ok, "baseFeePerGas field must be present on RPC block after Gingerbread")
}

// This test checks the functionality of the configuration to enable/disable
Expand Down Expand Up @@ -792,3 +793,92 @@ func TestEthersJSCompatibilityDisableBeforeGingerbread(t *testing.T) {
assert.Falsef(t, ok, "%s field should not be present on RPC block before Gingerbread", field)
}
}

// Initially we could not retrieve the eth compatibility fields (gasLimit &
// baseFee) on the genesis block because our logic dictated that the effective
// base fee and gas limit for a block were the ones set in the previous block
// (because those values are required by the vm before processing a block).
// There was no special case to handle the genesis block. We now have a special
// case to handle the genesis block which returns the values retrieved from the
// state at the genesis block.
func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) {
ac := test.AccountConfig(1, 1)
// Gingerbread needs to be disabled for this test to be meaningful (since
// gingerbread added gasLimt & baseFee fields to the block)
var gingerbreadBlock *big.Int = nil

// Fist we test without eth compatibility to ensure that the setting has an effect.
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
ec.RPCEthCompatibility = false
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
require.NoError(t, err)
defer shutdown()

ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()

block, err := network[0].WsClient.BlockByNumber(ctx, big.NewInt(0))
require.NoError(t, err)

var nilBigInt *big.Int
require.Equal(t, nilBigInt, block.BaseFee())
require.Equal(t, uint64(0), block.GasLimit())

// Now we with eth compatility enabled and see that gasLimit and baseFee
// are returned on the block.
gc, ec, err = test.BuildConfig(ac, gingerbreadBlock)
ec.RPCEthCompatibility = true
require.NoError(t, err)
network, shutdown, err = test.NewNetwork(ac, gc, ec)
require.NoError(t, err)
defer shutdown()

block, err = network[0].WsClient.BlockByNumber(ctx, big.NewInt(0))
require.NoError(t, err)

require.NotEqual(t, nilBigInt, block.BaseFee())
require.Greater(t, block.BaseFee().Uint64(), uint64(0))
require.Greater(t, block.GasLimit(), uint64(0))
}

// Initially we were not able to set the gingerbread activation block to be the genesis block, this test checks that it's possible.
func TestSettingGingerbreadOnGenesisBlock(t *testing.T) {
ac := test.AccountConfig(1, 1)

// Fist we test without gingerbread to ensure that setting the gingerbread
// actually has an effect.
var gingerbreadBlock *big.Int = nil
gc, ec, err := test.BuildConfig(ac, gingerbreadBlock)
ec.RPCEthCompatibility = false
require.NoError(t, err)
network, shutdown, err := test.NewNetwork(ac, gc, ec)
require.NoError(t, err)
defer shutdown()

ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()

block, err := network[0].WsClient.BlockByNumber(ctx, big.NewInt(0))
require.NoError(t, err)

var nilBigInt *big.Int
require.Equal(t, nilBigInt, block.BaseFee())
require.Equal(t, uint64(0), block.GasLimit())

// Now we check that setting the gingerbread block at genesis causes gasLimit and baseFee to be set on the block.
gingerbreadBlock = big.NewInt(0)
gc, ec, err = test.BuildConfig(ac, gingerbreadBlock)
ec.RPCEthCompatibility = false
require.NoError(t, err)
network, shutdown, err = test.NewNetwork(ac, gc, ec)
require.NoError(t, err)
defer shutdown()

block, err = network[0].WsClient.BlockByNumber(ctx, big.NewInt(0))
require.NoError(t, err)

require.NotEqual(t, nilBigInt, block.BaseFee())
require.Greater(t, block.BaseFee().Uint64(), uint64(0))
require.Greater(t, block.GasLimit(), uint64(0))
}
20 changes: 12 additions & 8 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,9 @@ func (b *EthAPIBackend) GasPriceMinimumForHeader(ctx context.Context, currencyAd
}
// The gasPriceMinimum (celo or alternative currency) of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unleess this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return nil, err
}
Expand All @@ -332,8 +333,9 @@ func (b *EthAPIBackend) RealGasPriceMinimumForHeader(ctx context.Context, curren
}
// The gasPriceMinimum (celo or alternative currency) of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unleess this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return nil, err
}
Expand All @@ -360,8 +362,9 @@ func (b *EthAPIBackend) GetBlockGasLimit(ctx context.Context, blockNrOrHash rpc.
}
// The gasLimit of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unless this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
log.Warn("Cannot create evmCaller to get blockGasLimit", "err", err)
return params.DefaultGasLimit
Expand All @@ -380,8 +383,9 @@ func (b *EthAPIBackend) GetRealBlockGasLimit(ctx context.Context, blockNrOrHash
}
// The gasLimit of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unless this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return 0, fmt.Errorf("EthApiBackend failed to retrieve state for block gas limit for block %v: %w", blockNrOrHash, err)
}
Expand Down
20 changes: 12 additions & 8 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,9 @@ func (b *LesApiBackend) GetBlockGasLimit(ctx context.Context, blockNrOrHash rpc.
}
// The gasLimit of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unless this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
log.Warn("Cannot create evmCaller to get blockGasLimit", "err", err)
return params.DefaultGasLimit
Expand All @@ -316,8 +317,9 @@ func (b *LesApiBackend) GetRealBlockGasLimit(ctx context.Context, blockNrOrHash
}
// The gasLimit of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unless this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return 0, fmt.Errorf("LesApiBackend failed to retrieve state for block gas limit for block %v: %w", blockNrOrHash, err)
}
Expand Down Expand Up @@ -359,8 +361,9 @@ func (b *LesApiBackend) GasPriceMinimumForHeader(ctx context.Context, currencyAd
}
// The gasPriceMinimum (celo or alternative currency) of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unleess this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return nil, err
}
Expand All @@ -374,8 +377,9 @@ func (b *LesApiBackend) RealGasPriceMinimumForHeader(ctx context.Context, curren
}
// The gasPriceMinimum (celo or alternative currency) of a specific block, is the one at the beginning of the block,
// not the end of it (the state_root of the header is the a state resulted of applying the block). So, the state to
// be used, MUST be the state result of the parent block
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &header.ParentHash})
// be used, MUST be the state result of the parent block unleess this is the genesis block.
h := header.ParentOrGenesisHash()
state, parent, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockHash: &h})
if err != nil {
return nil, err
}
Expand Down
14 changes: 1 addition & 13 deletions mycelo/genesis/genesis.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package genesis

import (
"errors"
"math/big"
"time"

Expand All @@ -21,18 +20,7 @@ var genesisMsgHash = common.HexToHash("ecc833a7747eaa8327335e8e0c6b6d8aa3a38d006

// CreateCommonGenesisConfig generates a config starting point which templates can then customize further
func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Address, istanbulConfig params.IstanbulConfig, gingerbreadBlock *big.Int) (*Config, error) {
baseOpCodeBlock := gingerbreadBlock
if gingerbreadBlock == nil {
baseOpCodeBlock = common.Big0
} else {
// The gasPriceMinimum contract requires the gingerbread block activation, but
// zero is to deactivate the if that reads the baseFee from the header. So we
// need to have gingerbread activation block bigger than zero
if gingerbreadBlock.Uint64() == 0 {
return nil, errors.New("gingerbread must be bigger than zero")
}
}
genesisConfig := BaseConfig(baseOpCodeBlock)
genesisConfig := BaseConfig(gingerbreadBlock)
genesisConfig.ChainID = chainID
genesisConfig.GenesisTimestamp = uint64(time.Now().Unix())
genesisConfig.Istanbul = istanbulConfig
Expand Down
Loading