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

chore(consensus) : Validate the genesis state #2189

Merged
merged 26 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e20c459
validate genesis stuff
nidhi-singh02 Nov 27, 2024
91a03b0
cleanup and gosec
nidhi-singh02 Nov 28, 2024
0a3d099
linter and block no check
nidhi-singh02 Nov 28, 2024
abf7f73
helper func above the calling func
nidhi-singh02 Nov 28, 2024
4e351a6
prev randao check
nidhi-singh02 Nov 28, 2024
729259e
enhancements and new func added
nidhi-singh02 Nov 28, 2024
0bea76b
lint
nidhi-singh02 Nov 28, 2024
8fece51
linter work work work
nidhi-singh02 Nov 28, 2024
11ed660
refactor validations into separate files
nidhi-singh02 Nov 28, 2024
8e782b6
add more detailed comments
nidhi-singh02 Nov 28, 2024
fa66092
lint
nidhi-singh02 Nov 28, 2024
f2adb92
nit
nidhi-singh02 Dec 2, 2024
fb61aee
define func in file
nidhi-singh02 Dec 2, 2024
f770029
use genesis from types, and review comments
nidhi-singh02 Dec 2, 2024
2ddb7c3
lint fix
nidhi-singh02 Dec 2, 2024
11551a0
remove struct for genesisState
nidhi-singh02 Dec 2, 2024
38e7e0e
optimized zerobytes func
nidhi-singh02 Dec 3, 2024
07e3136
depedency correct path
nidhi-singh02 Dec 6, 2024
3015664
blob gas vals added
nidhi-singh02 Dec 6, 2024
3eba1d9
validation in execution headers for root - rn commented
nidhi-singh02 Dec 6, 2024
65983de
lint
nidhi-singh02 Dec 6, 2024
465259f
nits
nidhi-singh02 Dec 6, 2024
62ded03
removed code val for empty roots
nidhi-singh02 Dec 6, 2024
6b481ea
optimized the order of validations
nidhi-singh02 Dec 6, 2024
0feec46
removed zero valued check for deposits
nidhi-singh02 Dec 6, 2024
a598b8d
Merge branch 'main' into genesis-validator-comet
nidhi-singh02 Dec 6, 2024
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
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
85 changes: 85 additions & 0 deletions consensus/cometbft/service/validate_deposits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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"
"encoding/hex"
"fmt"

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

// isZeroBytes returns true if the provided byte slice is all zeros.
func isZeroBytes(b []byte) bool {
return bytes.Equal(b, make([]byte, len(b)))
}

// validateDeposits performs validation of the provided deposits.
// It ensures:
// - At least one deposit is present
// - Deposit indices match their position in the slice
// - No duplicate public keys
// - Non-zero values for required fields
// (pubkey, credentials, amount, signature)
// 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)
}
if isZeroBytes(deposit.Pubkey[:]) {
return fmt.Errorf("deposit %d has a zeroed public key", 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{}{}

if isZeroBytes(deposit.Credentials[:]) {
return fmt.Errorf(
"deposit %d has zeroed withdrawal credentials",
i,
)
}

if deposit.Amount == 0 {
return fmt.Errorf("deposit %d has zero amount", i)
}

if isZeroBytes(deposit.Signature[:]) {
return fmt.Errorf("deposit %d has a zeroed signature", i)
}
}

return nil
}
104 changes: 104 additions & 0 deletions consensus/cometbft/service/validate_execution_header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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")
}
// Validate hash fields are not zero
zeroHash := common.ExecutionHash{}
// 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 bytes.Equal(header.StateRoot[:], zeroHash[:]) {
return errors.New("state root cannot be zero")
}
if bytes.Equal(header.ReceiptsRoot[:], zeroHash[:]) {
return errors.New("receipts root cannot be zero")
}
if bytes.Equal(header.BlockHash[:], zeroHash[:]) {
return errors.New("block hash cannot be zero")
}
if bytes.Equal(header.TransactionsRoot[:], zeroHash[:]) {
return errors.New("transactions root cannot be zero")
}

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

// Validate prevRandao is zero for genesis
if !bytes.Equal(header.Random[:], zeroHash[:]) {
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

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

// 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),
)
}

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

// Additional Deneb-specific validations for blob gas
if header.BlobGasUsed > header.GetGasLimit() {
return fmt.Errorf("blob gas used (%d) exceeds gas limit (%d)",
header.BlobGasUsed, header.GetGasLimit(),
)
}

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
}
Loading