Skip to content

Commit

Permalink
chore(consensus) : Validate the genesis state (#2189)
Browse files Browse the repository at this point in the history
Signed-off-by: nidhi-singh02 <trippin@berachain.com>
  • Loading branch information
nidhi-singh02 authored Dec 6, 2024
1 parent cf624db commit 6e83dfe
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 3 deletions.
13 changes: 12 additions & 1 deletion consensus/cometbft/service/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
errNilFinalizeBlockState = errors.New("finalizeBlockState is nil")
)

//nolint:gocognit // this is fine.
func (s *Service[LoggerT]) InitChain(
_ context.Context,
req *cmtabci.InitChainRequest,
Expand All @@ -59,6 +60,16 @@ func (s *Service[LoggerT]) InitChain(
)
}

var genesisState map[string]json.RawMessage
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
return nil, fmt.Errorf("failed to unmarshal genesis state: %w", err)
}
// Validate the genesis state.
err := s.ValidateGenesis(genesisState)
if err != nil {
return nil, err
}

s.logger.Info(
"InitChain",
"initialHeight",
Expand All @@ -78,7 +89,7 @@ func (s *Service[LoggerT]) InitChain(
// if req.InitialHeight is > 1, then we set the initial version on all
// stores
if req.InitialHeight > 1 {
if err := s.sm.CommitMultiStore().
if err = s.sm.CommitMultiStore().
SetInitialVersion(req.InitialHeight); err != nil {
return nil, err
}
Expand Down
44 changes: 42 additions & 2 deletions consensus/cometbft/service/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ package cometbft

import (
"crypto/sha256"
"fmt"

"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
"github.com/berachain/beacon-kit/primitives/encoding/json"
cmtcfg "github.com/cometbft/cometbft/config"
"github.com/cometbft/cometbft/node"
Expand All @@ -46,11 +48,49 @@ func (s *Service[_]) DefaultGenesis() map[string]json.RawMessage {

// ValidateGenesis validates the provided genesis state.
func (s *Service[_]) ValidateGenesis(
_ map[string]json.RawMessage,
genesisState map[string]json.RawMessage,
) error {
// Implement the validation logic for the provided genesis state.
// Implemented the validation logic for the provided genesis state.
// This should validate the genesis state for each module in the
// application.

// Validate that required modules are present in genesis. Currently,
// only the beacon module is required.
beaconGenesisBz, ok := genesisState["beacon"]
if !ok {
return errors.New(
"beacon module genesis state is required but was not found",
)
}

beaconGenesis := &types.Genesis[
*types.Deposit,
*types.ExecutionPayloadHeader,
]{}

if err := json.Unmarshal(beaconGenesisBz, &beaconGenesis); err != nil {
return fmt.Errorf(
"failed to unmarshal beacon genesis state: %w",
err,
)
}

if !isValidForkVersion(beaconGenesis.GetForkVersion()) {
return fmt.Errorf("invalid fork version format: %s",
beaconGenesis.ForkVersion,
)
}

if err := validateDeposits(beaconGenesis.GetDeposits()); err != nil {
return fmt.Errorf("invalid deposits: %w", err)
}

if err := validateExecutionHeader(
beaconGenesis.GetExecutionPayloadHeader(),
); err != nil {
return fmt.Errorf("invalid execution payload header: %w", err)
}

return nil
}

Expand Down
59 changes: 59 additions & 0 deletions consensus/cometbft/service/validate_deposits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2024, Berachain Foundation. All rights reserved.
// Use of this software is governed by the Business Source License included
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
//
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
// VERSIONS OF THE LICENSED WORK.
//
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
//
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.

package cometbft

import (
"encoding/hex"
"fmt"

"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
)

// validateDeposits performs validation of the provided deposits.
// It ensures:
// - At least one deposit is present
// - No duplicate public keys
// Returns an error with details if any validation fails.
func validateDeposits(deposits []*types.Deposit) error {
if len(deposits) == 0 {
return errors.New("at least one deposit is required")
}

seenPubkeys := make(map[string]struct{})

// In genesis, we have 1:1 mapping between deposits and validators. Hence,
// we check for duplicate public key.
for i, deposit := range deposits {
if deposit == nil {
return fmt.Errorf("deposit %d is nil", i)
}

// Check for duplicate pubkeys
pubkeyHex := hex.EncodeToString(deposit.Pubkey[:])
if _, seen := seenPubkeys[pubkeyHex]; seen {
return fmt.Errorf("duplicate pubkey found in deposit %d", i)
}
seenPubkeys[pubkeyHex] = struct{}{}
}

return nil
}
121 changes: 121 additions & 0 deletions consensus/cometbft/service/validate_execution_header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2024, Berachain Foundation. All rights reserved.
// Use of this software is governed by the Business Source License included
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
//
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
// VERSIONS OF THE LICENSED WORK.
//
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
//
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.

package cometbft

import (
"bytes"
"fmt"

"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
"github.com/berachain/beacon-kit/primitives/common"
)

// maxExtraDataSize defines the maximum allowed size in bytes for the ExtraData
// field in the execution payload header.
const maxExtraDataSize = 32

// validateExecutionHeader validates the provided execution payload header
// for the genesis block.
func validateExecutionHeader(header *types.ExecutionPayloadHeader) error {
if header == nil {
return errors.New("execution payload header cannot be nil")
}

// Check block number to be 0
if header.Number != 0 {
return errors.New("block number must be 0 for genesis block")
}

if header.GasLimit == 0 {
return errors.New("gas limit cannot be zero")
}

if header.GasUsed != 0 {
return errors.New("gas used must be zero for genesis block")
}

if header.BaseFeePerGas == nil {
return errors.New("base fee per gas cannot be nil")
}

// Additional Deneb-specific validations for blob gas
if header.BlobGasUsed != 0 {
return errors.New(
"blob gas used must be zero for genesis block",
)
}
if header.ExcessBlobGas != 0 {
return errors.New(
"excess blob gas must be zero for genesis block",
)
}

if header.BlobGasUsed > header.GasLimit {
return fmt.Errorf("blob gas used (%d) exceeds gas limit (%d)",
header.BlobGasUsed, header.GasLimit,
)
}

// Validate hash fields are not zero
zeroHash := common.ExecutionHash{}
emptyTrieRoot := common.Bytes32(
common.NewExecutionHashFromHex(
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
))

// For genesis block (when block number is 0), ParentHash must be zero
if !bytes.Equal(header.ParentHash[:], zeroHash[:]) {
return errors.New("parent hash must be zero for genesis block")
}

if header.ReceiptsRoot != emptyTrieRoot {
return errors.New(
"receipts root must be empty trie root for genesis block",
)
}

if bytes.Equal(header.BlockHash[:], zeroHash[:]) {
return errors.New("block hash cannot be zero")
}

// Validate prevRandao is zero for genesis
var zeroBytes32 common.Bytes32
if !bytes.Equal(header.Random[:], zeroBytes32[:]) {
return errors.New("prevRandao must be zero for genesis block")
}

// Fee recipient can be zero in genesis block
// No need to validate fee recipient for genesis

// We don't validate LogsBloom as it can legitimately be
// all zeros in a genesis block or in blocks with no logs

// Extra data length check (max 32 bytes)
if len(header.ExtraData) > maxExtraDataSize {
return fmt.Errorf(
"extra data too long: got %d bytes, max 32 bytes",
len(header.ExtraData),
)
}

return nil
}
53 changes: 53 additions & 0 deletions consensus/cometbft/service/validate_fork_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2024, Berachain Foundation. All rights reserved.
// Use of this software is governed by the Business Source License included
// in the LICENSE file of this repository and at www.mariadb.com/bsl11.
//
// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY
// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER
// VERSIONS OF THE LICENSED WORK.
//
// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF
// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF
// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE).
//
// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
// TITLE.

package cometbft

import (
"encoding/hex"
"strings"

"github.com/berachain/beacon-kit/primitives/common"
)

const expectedHexLength = 8

// isValidForkVersion returns true if the provided fork version is valid.
// A valid fork version must:
// - Start with "0x"
// - Be followed by exactly 8 hexadecimal characters.
func isValidForkVersion(forkVersion common.Version) bool {
forkVersionStr := forkVersion.String()
if !strings.HasPrefix(forkVersionStr, "0x") {
return false
}

// Remove "0x" prefix and verify remaining characters
hexPart := strings.TrimPrefix(forkVersionStr, "0x")

// Should have exactly 8 characters after 0x prefix
if len(hexPart) != expectedHexLength {
return false
}

// Verify it's a valid hex number
_, err := hex.DecodeString(hexPart)
return err == nil
}

0 comments on commit 6e83dfe

Please sign in to comment.