From c1ae2d75f8de77c5c8d1fb13456b17adcca3ec55 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 22 Jan 2025 11:49:15 -0500 Subject: [PATCH] 1/2 fix(cli): Align `genesis validator-root` cmd with processing of genesis validators (#2393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aBear Co-authored-by: Friðrik Ásmundsson --- cli/commands/genesis/root.go | 68 +++++---- cli/commands/genesis/root_test.go | 124 +++++++++++++++ state-transition/core/core_test.go | 124 +-------------- .../core/state_processor_genesis_test.go | 8 +- .../core/state_processor_staking_test.go | 18 ++- .../core/validation_deposits_test.go | 12 +- testing/state-transition/README.md | 7 + testing/state-transition/state-transition.go | 144 ++++++++++++++++++ 8 files changed, 347 insertions(+), 158 deletions(-) create mode 100644 cli/commands/genesis/root_test.go create mode 100644 testing/state-transition/README.md create mode 100644 testing/state-transition/state-transition.go diff --git a/cli/commands/genesis/root.go b/cli/commands/genesis/root.go index 2930583323..2e828b61fd 100644 --- a/cli/commands/genesis/root.go +++ b/cli/commands/genesis/root.go @@ -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 @@ -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() +} diff --git a/cli/commands/genesis/root_test.go b/cli/commands/genesis/root_test.go new file mode 100644 index 0000000000..d77850ed18 --- /dev/null +++ b/cli/commands/genesis/root_test.go @@ -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)) +} diff --git a/state-transition/core/core_test.go b/state-transition/core/core_test.go index 5f49e6a291..1bda79c9f0 100644 --- a/state-transition/core/core_test.go +++ b/state-transition/core/core_test.go @@ -1,3 +1,6 @@ +//go:build test +// +build test + // SPDX-License-Identifier: BUSL-1.1 // // Copyright (C) 2025, Berachain Foundation. All rights reserved. @@ -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() @@ -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() @@ -183,7 +75,7 @@ func progressStateToSlot( func buildNextBlock( t *testing.T, - beaconState *TestBeaconStateT, + beaconState *statetransition.TestBeaconStateT, nextBlkBody *types.BeaconBlockBody, ) *types.BeaconBlock { t.Helper() @@ -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 { diff --git a/state-transition/core/state_processor_genesis_test.go b/state-transition/core/state_processor_genesis_test.go index ea60f7dfe6..e313fe6e7f 100644 --- a/state-transition/core/state_processor_genesis_test.go +++ b/state-transition/core/state_processor_genesis_test.go @@ -1,3 +1,6 @@ +//go:build test +// +build test + // SPDX-License-Identifier: BUSL-1.1 // // Copyright (C) 2025, Berachain Foundation. All rights reserved. @@ -30,12 +33,13 @@ import ( "github.com/berachain/beacon-kit/primitives/constants" "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" ) func TestInitialize(t *testing.T) { cs := setupChain(t, components.BetnetChainSpecType) - sp, st, _, _ := setupState(t, cs) + sp, st, _, _ := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -158,7 +162,7 @@ func TestInitialize(t *testing.T) { func checkValidator( t *testing.T, cs chain.Spec, - bs *TestBeaconStateT, + bs *statetransition.TestBeaconStateT, dep *types.Deposit, ) { t.Helper() diff --git a/state-transition/core/state_processor_staking_test.go b/state-transition/core/state_processor_staking_test.go index 3b8a2b2f25..9c89cbdff3 100644 --- a/state-transition/core/state_processor_staking_test.go +++ b/state-transition/core/state_processor_staking_test.go @@ -1,3 +1,6 @@ +//go:build test +// +build test + // SPDX-License-Identifier: BUSL-1.1 // // Copyright (C) 2025, Berachain Foundation. All rights reserved. @@ -34,6 +37,7 @@ import ( "github.com/berachain/beacon-kit/primitives/math" "github.com/berachain/beacon-kit/primitives/transition" "github.com/berachain/beacon-kit/primitives/version" + statetransition "github.com/berachain/beacon-kit/testing/state-transition" "github.com/stretchr/testify/require" ) @@ -41,7 +45,7 @@ import ( // updated (increasing amount), corresponding balance is updated. func TestTransitionUpdateValidators(t *testing.T) { cs := setupChain(t, components.BetnetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -192,7 +196,7 @@ func TestTransitionUpdateValidators(t *testing.T) { func TestTransitionCreateValidator(t *testing.T) { // Create state processor to test cs := setupChain(t, components.BetnetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -389,7 +393,7 @@ func TestTransitionCreateValidator(t *testing.T) { func TestTransitionWithdrawals(t *testing.T) { cs := setupChain(t, components.BoonetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -480,7 +484,7 @@ func TestTransitionMaxWithdrawals(t *testing.T) { cs, err := chain.NewSpec(csData) require.NoError(t, err) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -617,7 +621,7 @@ func TestTransitionMaxWithdrawals(t *testing.T) { // and its deposit is returned at after next epoch starts. func TestTransitionHittingValidatorsCap_ExtraSmall(t *testing.T) { cs := setupChain(t, components.BetnetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -847,7 +851,7 @@ func TestTransitionHittingValidatorsCap_ExtraSmall(t *testing.T) { //nolint:maintidx // Okay for test. func TestTransitionHittingValidatorsCap_ExtraBig(t *testing.T) { cs := setupChain(t, components.BetnetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -1136,7 +1140,7 @@ func TestTransitionHittingValidatorsCap_ExtraBig(t *testing.T) { func TestValidatorNotWithdrawable(t *testing.T) { cs := setupChain(t, components.BetnetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( belowActiveBalance = math.Gwei(cs.EjectionBalance()) diff --git a/state-transition/core/validation_deposits_test.go b/state-transition/core/validation_deposits_test.go index dce24f9870..d95aab5a5b 100644 --- a/state-transition/core/validation_deposits_test.go +++ b/state-transition/core/validation_deposits_test.go @@ -1,3 +1,6 @@ +//go:build test +// +build test + // SPDX-License-Identifier: BUSL-1.1 // // Copyright (C) 2025, Berachain Foundation. All rights reserved. @@ -31,12 +34,13 @@ import ( "github.com/berachain/beacon-kit/primitives/common" "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" ) func TestInvalidDeposits(t *testing.T) { cs := setupChain(t, components.BoonetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( minBalance = math.Gwei(cs.EjectionBalance() + cs.EffectiveBalanceIncrement()) @@ -110,7 +114,7 @@ func TestInvalidDeposits(t *testing.T) { func TestInvalidDepositsCount(t *testing.T) { cs := setupChain(t, components.BoonetChainSpecType) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -187,7 +191,7 @@ func TestLocalDepositsExceedBlockDeposits(t *testing.T) { csData.MaxDepositsPerBlock = 1 // Set only 1 deposit allowed per block. cs, err := chain.NewSpec(csData) require.NoError(t, err) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) @@ -264,7 +268,7 @@ func TestLocalDepositsExceedBlockDepositsBadRoot(t *testing.T) { csData.MaxDepositsPerBlock = 1 // Set only 1 deposit allowed per block. cs, err := chain.NewSpec(csData) require.NoError(t, err) - sp, st, ds, ctx := setupState(t, cs) + sp, st, ds, ctx := statetransition.SetupTestState(t, cs) var ( maxBalance = math.Gwei(cs.MaxEffectiveBalance()) diff --git a/testing/state-transition/README.md b/testing/state-transition/README.md new file mode 100644 index 0000000000..4668809c4e --- /dev/null +++ b/testing/state-transition/README.md @@ -0,0 +1,7 @@ +# Testing state transitions + +This package, `statetransition`, contains code which is helpful to test state transitions. + +Specifically, it contains code to instantiate a test state processor with a mocked bls signer and its own state. + +This code can be reused across tests in multiple packages but **should not be used in production code**. \ No newline at end of file diff --git a/testing/state-transition/state-transition.go b/testing/state-transition/state-transition.go new file mode 100644 index 0000000000..4953741c51 --- /dev/null +++ b/testing/state-transition/state-transition.go @@ -0,0 +1,144 @@ +// 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. +//go:build test +// +build test + +package statetransition + +import ( + "context" + "fmt" + "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" + "github.com/berachain/beacon-kit/log/noop" + nodemetrics "github.com/berachain/beacon-kit/node-core/components/metrics" + "github.com/berachain/beacon-kit/primitives/bytes" + cryptomocks "github.com/berachain/beacon-kit/primitives/crypto/mocks" + "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" + "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) +} + +//nolint:gochecknoglobals // unexported and use only in tests +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 SetupTestState(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 +}