package util import ( "context" "fmt" "testing" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/go-bitfield" b "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/iface" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/crypto/bls" enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v3/testing/require" ) // FillRootsNaturalOpt is meant to be used as an option when calling NewBeaconState. // It fills state and block roots with hex representations of natural numbers starting with 0. // Example: 16 becomes 0x00...0f. func FillRootsNaturalOpt(state *ethpb.BeaconState) error { roots, err := PrepareRoots(int(params.BeaconConfig().SlotsPerHistoricalRoot)) if err != nil { return err } state.StateRoots = roots state.BlockRoots = roots return nil } // FillRootsNaturalOptAltair is meant to be used as an option when calling NewBeaconStateAltair. // It fills state and block roots with hex representations of natural numbers starting with 0. // Example: 16 becomes 0x00...0f. func FillRootsNaturalOptAltair(state *ethpb.BeaconStateAltair) error { roots, err := PrepareRoots(int(params.BeaconConfig().SlotsPerHistoricalRoot)) if err != nil { return err } state.StateRoots = roots state.BlockRoots = roots return nil } // FillRootsNaturalOptBellatrix is meant to be used as an option when calling NewBeaconStateBellatrix. // It fills state and block roots with hex representations of natural numbers starting with 0. // Example: 16 becomes 0x00...0f. func FillRootsNaturalOptBellatrix(state *ethpb.BeaconStateBellatrix) error { roots, err := PrepareRoots(int(params.BeaconConfig().SlotsPerHistoricalRoot)) if err != nil { return err } state.StateRoots = roots state.BlockRoots = roots return nil } // FillRootsNaturalOptCapella is meant to be used as an option when calling NewBeaconStateCapella. // It fills state and block roots with hex representations of natural numbers starting with 0. // Example: 16 becomes 0x00...0f. func FillRootsNaturalOptCapella(state *ethpb.BeaconStateCapella) error { roots, err := PrepareRoots(int(params.BeaconConfig().SlotsPerHistoricalRoot)) if err != nil { return err } state.StateRoots = roots state.BlockRoots = roots return nil } type NewBeaconStateOption func(state *ethpb.BeaconState) error // NewBeaconState creates a beacon state with minimum marshalable fields. func NewBeaconState(options ...NewBeaconStateOption) (state.BeaconState, error) { seed := ðpb.BeaconState{ BlockRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), StateRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), RandaoMixes: filledByteSlice2D(uint64(params.BeaconConfig().EpochsPerHistoricalVector), 32), Validators: make([]*ethpb.Validator, 0), CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, Eth1Data: ðpb.Eth1Data{ DepositRoot: make([]byte, fieldparams.RootLength), BlockHash: make([]byte, 32), }, Fork: ðpb.Fork{ PreviousVersion: make([]byte, 4), CurrentVersion: make([]byte, 4), }, Eth1DataVotes: make([]*ethpb.Eth1Data, 0), HistoricalRoots: make([][]byte, 0), JustificationBits: bitfield.Bitvector4{0x0}, FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, LatestBlockHeader: HydrateBeaconHeader(ðpb.BeaconBlockHeader{}), PreviousEpochAttestations: make([]*ethpb.PendingAttestation, 0), CurrentEpochAttestations: make([]*ethpb.PendingAttestation, 0), PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, } for _, opt := range options { err := opt(seed) if err != nil { return nil, err } } var st, err = state_native.InitializeFromProtoUnsafePhase0(seed) if err != nil { return nil, err } return st.Copy(), nil } // NewBeaconStateAltair creates a beacon state with minimum marshalable fields. func NewBeaconStateAltair(options ...func(state *ethpb.BeaconStateAltair) error) (state.BeaconState, error) { pubkeys := make([][]byte, 512) for i := range pubkeys { pubkeys[i] = make([]byte, 48) } seed := ðpb.BeaconStateAltair{ BlockRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), StateRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), RandaoMixes: filledByteSlice2D(uint64(params.BeaconConfig().EpochsPerHistoricalVector), 32), Validators: make([]*ethpb.Validator, 0), CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, Eth1Data: ðpb.Eth1Data{ DepositRoot: make([]byte, fieldparams.RootLength), BlockHash: make([]byte, 32), }, Fork: ðpb.Fork{ PreviousVersion: make([]byte, 4), CurrentVersion: make([]byte, 4), }, Eth1DataVotes: make([]*ethpb.Eth1Data, 0), HistoricalRoots: make([][]byte, 0), JustificationBits: bitfield.Bitvector4{0x0}, FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, LatestBlockHeader: HydrateBeaconHeader(ðpb.BeaconBlockHeader{}), PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, PreviousEpochParticipation: make([]byte, 0), CurrentEpochParticipation: make([]byte, 0), CurrentSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, NextSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, } for _, opt := range options { err := opt(seed) if err != nil { return nil, err } } var st, err = state_native.InitializeFromProtoUnsafeAltair(seed) if err != nil { return nil, err } return st.Copy(), nil } // NewBeaconStateBellatrix creates a beacon state with minimum marshalable fields. func NewBeaconStateBellatrix(options ...func(state *ethpb.BeaconStateBellatrix) error) (state.BeaconState, error) { pubkeys := make([][]byte, 512) for i := range pubkeys { pubkeys[i] = make([]byte, 48) } seed := ðpb.BeaconStateBellatrix{ BlockRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), StateRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), RandaoMixes: filledByteSlice2D(uint64(params.BeaconConfig().EpochsPerHistoricalVector), 32), Validators: make([]*ethpb.Validator, 0), CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, Eth1Data: ðpb.Eth1Data{ DepositRoot: make([]byte, fieldparams.RootLength), BlockHash: make([]byte, 32), }, Fork: ðpb.Fork{ PreviousVersion: make([]byte, 4), CurrentVersion: make([]byte, 4), }, Eth1DataVotes: make([]*ethpb.Eth1Data, 0), HistoricalRoots: make([][]byte, 0), JustificationBits: bitfield.Bitvector4{0x0}, FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, LatestBlockHeader: HydrateBeaconHeader(ðpb.BeaconBlockHeader{}), PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, PreviousEpochParticipation: make([]byte, 0), CurrentEpochParticipation: make([]byte, 0), CurrentSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, NextSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeader{ ParentHash: make([]byte, 32), FeeRecipient: make([]byte, 20), StateRoot: make([]byte, 32), ReceiptsRoot: make([]byte, 32), LogsBloom: make([]byte, 256), PrevRandao: make([]byte, 32), ExtraData: make([]byte, 0), BaseFeePerGas: make([]byte, 32), BlockHash: make([]byte, 32), TransactionsRoot: make([]byte, 32), }, } for _, opt := range options { err := opt(seed) if err != nil { return nil, err } } var st, err = state_native.InitializeFromProtoUnsafeBellatrix(seed) if err != nil { return nil, err } return st.Copy(), nil } // NewBeaconStateCapella creates a beacon state with minimum marshalable fields. func NewBeaconStateCapella(options ...func(state *ethpb.BeaconStateCapella) error) (state.BeaconState, error) { pubkeys := make([][]byte, 512) for i := range pubkeys { pubkeys[i] = make([]byte, 48) } seed := ðpb.BeaconStateCapella{ BlockRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), StateRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32), Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), RandaoMixes: filledByteSlice2D(uint64(params.BeaconConfig().EpochsPerHistoricalVector), 32), Validators: make([]*ethpb.Validator, 0), CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, Eth1Data: ðpb.Eth1Data{ DepositRoot: make([]byte, fieldparams.RootLength), BlockHash: make([]byte, 32), }, Fork: ðpb.Fork{ PreviousVersion: make([]byte, 4), CurrentVersion: make([]byte, 4), }, Eth1DataVotes: make([]*ethpb.Eth1Data, 0), HistoricalRoots: make([][]byte, 0), JustificationBits: bitfield.Bitvector4{0x0}, FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, LatestBlockHeader: HydrateBeaconHeader(ðpb.BeaconBlockHeader{}), PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)}, PreviousEpochParticipation: make([]byte, 0), CurrentEpochParticipation: make([]byte, 0), CurrentSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, NextSyncCommittee: ðpb.SyncCommittee{ Pubkeys: pubkeys, AggregatePubkey: make([]byte, 48), }, LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderCapella{ ParentHash: make([]byte, 32), FeeRecipient: make([]byte, 20), StateRoot: make([]byte, 32), ReceiptsRoot: make([]byte, 32), LogsBloom: make([]byte, 256), PrevRandao: make([]byte, 32), ExtraData: make([]byte, 0), BaseFeePerGas: make([]byte, 32), BlockHash: make([]byte, 32), TransactionsRoot: make([]byte, 32), WithdrawalsRoot: make([]byte, 32), }, } for _, opt := range options { err := opt(seed) if err != nil { return nil, err } } var st, err = state_native.InitializeFromProtoUnsafeCapella(seed) if err != nil { return nil, err } return st.Copy(), nil } // SSZ will fill 2D byte slices with their respective values, so we must fill these in too for round // trip testing. func filledByteSlice2D(length, innerLen uint64) [][]byte { b := make([][]byte, length) for i := uint64(0); i < length; i++ { b[i] = make([]byte, innerLen) } return b } // PrepareRoots returns a list of roots with hex representations of natural numbers starting with 0. // Example: 16 becomes 0x00...0f. func PrepareRoots(size int) ([][]byte, error) { roots := make([][]byte, size) for i := 0; i < size; i++ { roots[i] = make([]byte, fieldparams.RootLength) } for j := 0; j < len(roots); j++ { // Remove '0x' prefix and left-pad '0' to have 64 chars in total. s := fmt.Sprintf("%064s", hexutil.EncodeUint64(uint64(j))[2:]) h, err := hexutil.Decode("0x" + s) if err != nil { return nil, err } roots[j] = h } return roots, nil } // DeterministicGenesisStateWithGenesisBlock creates a genesis state, saves the genesis block, // genesis state and head block root. It returns the genesis state, genesis block's root and // validator private keys. func DeterministicGenesisStateWithGenesisBlock( t *testing.T, ctx context.Context, db iface.HeadAccessDatabase, numValidators uint64, ) (state.BeaconState, [32]byte, []bls.SecretKey) { genesisState, privateKeys := DeterministicGenesisState(t, numValidators) stateRoot, err := genesisState.HashTreeRoot(ctx) require.NoError(t, err, "Could not hash genesis state") genesis := b.NewGenesisBlock(stateRoot[:]) SaveBlock(t, ctx, db, genesis) parentRoot, err := genesis.Block.HashTreeRoot() require.NoError(t, err, "Could not get signing root") require.NoError(t, db.SaveState(ctx, genesisState, parentRoot), "Could not save genesis state") require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state") return genesisState, parentRoot, privateKeys }