Skip to content

Commit

Permalink
1/2 fix(cli): Align genesis validator-root cmd with processing of g…
Browse files Browse the repository at this point in the history
…enesis validators (berachain#2393)

Co-authored-by: aBear <[email protected]>
Co-authored-by: Friðrik Ásmundsson <[email protected]>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent 1a275ea commit c1ae2d7
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 158 deletions.
68 changes: 39 additions & 29 deletions cli/commands/genesis/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,26 @@ import (
"github.com/berachain/beacon-kit/chain"
"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/errors"
"github.com/berachain/beacon-kit/primitives/bytes"
"github.com/berachain/beacon-kit/primitives/common"
"github.com/berachain/beacon-kit/primitives/encoding/json"
"github.com/berachain/beacon-kit/primitives/math"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)

// Beacon, AppState and Genesis are code duplications that
// collectively reproduce part of genesis file structure

type Beacon struct {
Deposits types.Deposits `json:"deposits"`
}

type AppState struct {
Beacon `json:"beacon"`
}

type Genesis struct {
AppState struct {
Beacon struct {
Deposits []struct {
Pubkey bytes.B48 `json:"pubkey"`
Credentials bytes.B32 `json:"credentials"`
Amount math.U64 `json:"amount"`
Signature string `json:"signature"`
Index int `json:"index"`
} `json:"deposits"`
} `json:"beacon"`
} `json:"app_state"`
AppState `json:"app_state"`
}

// TODO: move this logic to the `deposit create-validator/validate` commands as it is only
Expand All @@ -66,26 +67,35 @@ func GetGenesisValidatorRootCmd(cs chain.Spec) *cobra.Command {
return errors.Wrap(err, "failed to unmarshal JSON")
}

depositCount := uint64(len(genesis.AppState.Beacon.Deposits))
validators := make(
types.Validators,
depositCount,
)
for i, deposit := range genesis.AppState.Beacon.Deposits {
val := types.NewValidatorFromDeposit(
deposit.Pubkey,
types.WithdrawalCredentials(deposit.Credentials),
deposit.Amount,
math.Gwei(cs.EffectiveBalanceIncrement()),
math.Gwei(cs.MaxEffectiveBalance()),
)
validators[i] = val
}

cmd.Printf("%s\n", validators.HashTreeRoot())
validatorHashTreeRoot := ValidatorsRoot(genesis.Deposits, cs)
cmd.Printf("%s\n", validatorHashTreeRoot)
return nil
},
}

return cmd
}

func ValidatorsRoot(deposits types.Deposits, cs chain.Spec) common.Root {
validators := make(types.Validators, len(deposits))
minEffectiveBalance := math.Gwei(cs.EjectionBalance() + cs.EffectiveBalanceIncrement())

for i, deposit := range deposits {
val := types.NewValidatorFromDeposit(
deposit.Pubkey,
deposit.Credentials,
deposit.Amount,
math.Gwei(cs.EffectiveBalanceIncrement()),
math.Gwei(cs.MaxEffectiveBalance()),
)

// mimic processGenesisActivation
if val.GetEffectiveBalance() >= minEffectiveBalance {
val.SetActivationEligibilityEpoch(0)
val.SetActivationEpoch(0)
}
validators[i] = val
}

return validators.HashTreeRoot()
}
124 changes: 124 additions & 0 deletions cli/commands/genesis/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//go:build test
// +build test

// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2025, 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 genesis_test

import (
"bytes"
"fmt"
"math/rand"
"reflect"
"testing"
"testing/quick"

"github.com/berachain/beacon-kit/cli/commands/genesis"
"github.com/berachain/beacon-kit/config/spec"
"github.com/berachain/beacon-kit/consensus-types/types"
"github.com/berachain/beacon-kit/primitives/common"
"github.com/berachain/beacon-kit/primitives/crypto"
"github.com/berachain/beacon-kit/primitives/math"
"github.com/berachain/beacon-kit/primitives/version"
statetransition "github.com/berachain/beacon-kit/testing/state-transition"
"github.com/stretchr/testify/require"
)

// generator for random deposits.
type TestDeposits []types.Deposit

func (TestDeposits) Generate(rand *rand.Rand, size int) reflect.Value {
res := make(TestDeposits, size)
for i := range size {
var (
pubKey crypto.BLSPubkey
creds types.WithdrawalCredentials
sign crypto.BLSSignature

err error
)
_, err = rand.Read(pubKey[:])
if err != nil {
panic(fmt.Errorf("failed generating random pubKey: %w", err))
}
_, err = rand.Read(creds[:])
if err != nil {
panic(fmt.Errorf("failed generating random cred: %w", err))
}
_, err = rand.Read(sign[:])
if err != nil {
panic(fmt.Errorf("failed generating random sign: %w", err))
}

res[i] = types.Deposit{
Pubkey: pubKey,
Credentials: creds,
Amount: math.Gwei(rand.Uint64()),
Signature: sign,
Index: 0, // indexes will be set in order in the test
}
}
return reflect.ValueOf(res)
}

func TestCompareGenesisCmdWithStateProcessor(t *testing.T) {
qc := &quick.Config{MaxCount: 1_000}
cs, err := spec.MainnetChainSpec()
require.NoError(t, err)

f := func(inputs TestDeposits) bool {
deposits := make(types.Deposits, len(inputs))
for i, input := range inputs {
deposits[i] = &types.Deposit{
Pubkey: input.Pubkey,
Credentials: input.Credentials,
Amount: input.Amount,
Signature: input.Signature,
Index: uint64(i),
}
}
// genesis validators root from CLI
cliValRoot := genesis.ValidatorsRoot(deposits, cs)

// genesis validators root from StateProcessor
sp, st, _, _ := statetransition.SetupTestState(t, cs)
var (
genPayloadHeader = new(types.ExecutionPayloadHeader).Empty()
genVersion = version.FromUint32[common.Version](version.Deneb)
)
_, err = sp.InitializePreminedBeaconStateFromEth1(
st,
deposits,
genPayloadHeader,
genVersion,
)
require.NoError(t, err)

var processorRoot common.Root
processorRoot, err = st.GetGenesisValidatorsRoot()
require.NoError(t, err)

// assert that they generate the same root, given the same list of deposits
return bytes.Equal(cliValRoot[:], processorRoot[:])
}

require.NoError(t, quick.Check(f, qc))
}
124 changes: 8 additions & 116 deletions state-transition/core/core_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build test
// +build test

// SPDX-License-Identifier: BUSL-1.1
//
// Copyright (C) 2025, Berachain Foundation. All rights reserved.
Expand All @@ -21,89 +24,21 @@
package core_test

import (
"context"
"fmt"
"strconv"
"testing"

corestore "cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/store"
"cosmossdk.io/store/metrics"
storetypes "cosmossdk.io/store/types"
"github.com/berachain/beacon-kit/chain"
"github.com/berachain/beacon-kit/consensus-types/types"
engineprimitives "github.com/berachain/beacon-kit/engine-primitives/engine-primitives"
"github.com/berachain/beacon-kit/log/noop"
"github.com/berachain/beacon-kit/node-core/components"
nodemetrics "github.com/berachain/beacon-kit/node-core/components/metrics"
"github.com/berachain/beacon-kit/primitives/bytes"
"github.com/berachain/beacon-kit/primitives/common"
cryptomocks "github.com/berachain/beacon-kit/primitives/crypto/mocks"
"github.com/berachain/beacon-kit/primitives/math"
"github.com/berachain/beacon-kit/primitives/transition"
"github.com/berachain/beacon-kit/state-transition/core"
"github.com/berachain/beacon-kit/state-transition/core/mocks"
statedb "github.com/berachain/beacon-kit/state-transition/core/state"
"github.com/berachain/beacon-kit/storage"
"github.com/berachain/beacon-kit/storage/beacondb"
"github.com/berachain/beacon-kit/storage/db"
depositstore "github.com/berachain/beacon-kit/storage/deposit"
dbm "github.com/cosmos/cosmos-db"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/mock"
statetransition "github.com/berachain/beacon-kit/testing/state-transition"
"github.com/stretchr/testify/require"
)

type (
TestBeaconStateMarshallableT = types.BeaconState

TestBeaconStateT = statedb.StateDB

TestStateProcessorT = core.StateProcessor[*transition.Context]
)

type testKVStoreService struct {
ctx sdk.Context
}

func (kvs *testKVStoreService) OpenKVStore(context.Context) corestore.KVStore {
//nolint:contextcheck // fine with tests
store := sdk.UnwrapSDKContext(kvs.ctx).KVStore(testStoreKey)
return storage.NewKVStore(store)
}

var testStoreKey = storetypes.NewKVStoreKey("state-transition-tests")

func initTestStores() (*beacondb.KVStore, *depositstore.KVStore, error) {
db, err := db.OpenDB("", dbm.MemDBBackend)
if err != nil {
return nil, nil, fmt.Errorf("failed opening mem db: %w", err)
}
var (
nopLog = log.NewNopLogger()
noopCloseFunc = func() error { return nil }
nopMetrics = metrics.NewNoOpMetrics()
)

cms := store.NewCommitMultiStore(
db,
nopLog,
nopMetrics,
)

cms.MountStoreWithDB(testStoreKey, storetypes.StoreTypeIAVL, nil)
if err = cms.LoadLatestVersion(); err != nil {
return nil, nil, fmt.Errorf("failed to load latest version: %w", err)
}

ctx := sdk.NewContext(cms, true, nopLog)
testStoreService := &testKVStoreService{ctx: ctx}
return beacondb.New(testStoreService),
depositstore.NewStore(testStoreService, noopCloseFunc, nopLog),
nil
}

func setupChain(t *testing.T, chainSpecType string) chain.Spec {
t.Helper()

Expand All @@ -114,53 +49,10 @@ func setupChain(t *testing.T, chainSpecType string) chain.Spec {
return cs
}

func setupState(t *testing.T, cs chain.Spec) (
*TestStateProcessorT,
*TestBeaconStateT,
*depositstore.KVStore,
*transition.Context,
) {
t.Helper()

execEngine := mocks.NewExecutionEngine(t)

mocksSigner := &cryptomocks.BLSSigner{}
mocksSigner.On(
"VerifySignature",
mock.Anything, mock.Anything, mock.Anything,
).Return(nil)

dummyProposerAddr := []byte{0xff}

kvStore, depositStore, err := initTestStores()
require.NoError(t, err)
beaconState := statedb.NewBeaconStateFromDB(kvStore, cs)

sp := core.NewStateProcessor[*transition.Context](
noop.NewLogger[any](),
cs,
execEngine,
depositStore,
mocksSigner,
func(bytes.B48) ([]byte, error) {
return dummyProposerAddr, nil
},
nodemetrics.NewNoOpTelemetrySink(),
)

ctx := &transition.Context{
SkipPayloadVerification: true,
SkipValidateResult: true,
ProposerAddress: dummyProposerAddr,
}

return sp, beaconState, depositStore, ctx
}

//nolint:unused // may be used in the future.
func progressStateToSlot(
t *testing.T,
beaconState *TestBeaconStateT,
beaconState *statetransition.TestBeaconStateT,
slot math.U64,
) {
t.Helper()
Expand All @@ -183,7 +75,7 @@ func progressStateToSlot(

func buildNextBlock(
t *testing.T,
beaconState *TestBeaconStateT,
beaconState *statetransition.TestBeaconStateT,
nextBlkBody *types.BeaconBlockBody,
) *types.BeaconBlock {
t.Helper()
Expand Down Expand Up @@ -234,8 +126,8 @@ func moveToEndOfEpoch(
t *testing.T,
tip *types.BeaconBlock,
cs chain.Spec,
sp *TestStateProcessorT,
st *TestBeaconStateT,
sp *statetransition.TestStateProcessorT,
st *statetransition.TestBeaconStateT,
ctx *transition.Context,
depRoot common.Root,
) *types.BeaconBlock {
Expand Down
Loading

0 comments on commit c1ae2d7

Please sign in to comment.