package transition import ( "context" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair" b "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stateutil" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" ) // GenesisBeaconStateBellatrix gets called when MinGenesisActiveValidatorCount count of // full deposits were made to the deposit contract and the ChainStart log gets emitted. // // Spec pseudocode definition: // // def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, // eth1_timestamp: uint64, // deposits: Sequence[Deposit]) -> BeaconState: // fork = Fork( // previous_version=GENESIS_FORK_VERSION, // current_version=GENESIS_FORK_VERSION, // epoch=GENESIS_EPOCH, // ) // state = BeaconState( // genesis_time=eth1_timestamp + GENESIS_DELAY, // fork=fork, // eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), // latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), // randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy // ) // // # Process deposits // leaves = list(map(lambda deposit: deposit.data, deposits)) // for index, deposit in enumerate(deposits): // deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) // state.eth1_data.deposit_root = hash_tree_root(deposit_data_list) // process_deposit(state, deposit) // // # Process activations // for index, validator in enumerate(state.validators): // balance = state.balances[index] // validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) // if validator.effective_balance == MAX_EFFECTIVE_BALANCE: // validator.activation_eligibility_epoch = GENESIS_EPOCH // validator.activation_epoch = GENESIS_EPOCH // // # Set genesis validators root for domain separation and chain versioning // state.genesis_validators_root = hash_tree_root(state.validators) // // return state // // This method differs from the spec so as to process deposits beforehand instead of the end of the function. func GenesisBeaconStateBellatrix(ctx context.Context, deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data, ep *enginev1.ExecutionPayload) (state.BeaconState, error) { st, err := EmptyGenesisStateBellatrix() if err != nil { return nil, err } // Process initial deposits. st, err = helpers.UpdateGenesisEth1Data(st, deposits, eth1Data) if err != nil { return nil, err } st, err = b.ProcessPreGenesisDeposits(ctx, st, deposits) if err != nil { return nil, errors.Wrap(err, "could not process validator deposits") } // After deposits have been processed, overwrite eth1data to what is passed in. This allows us to "pre-mine" validators // without the deposit root and count mismatching the real deposit contract. if err := st.SetEth1Data(eth1Data); err != nil { return nil, err } if err := st.SetEth1DepositIndex(eth1Data.DepositCount); err != nil { return nil, err } return OptimizedGenesisBeaconStateBellatrix(genesisTime, st, st.Eth1Data(), ep) } // OptimizedGenesisBeaconState is used to create a state that has already processed deposits. This is to efficiently // create a mainnet state at chainstart. func OptimizedGenesisBeaconStateBellatrix(genesisTime uint64, preState state.BeaconState, eth1Data *ethpb.Eth1Data, ep *enginev1.ExecutionPayload) (state.BeaconState, error) { if eth1Data == nil { return nil, errors.New("no eth1data provided for genesis state") } randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector) for i := 0; i < len(randaoMixes); i++ { h := make([]byte, 32) copy(h, eth1Data.BlockHash) randaoMixes[i] = h } zeroHash := params.BeaconConfig().ZeroHash[:] activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector) for i := 0; i < len(activeIndexRoots); i++ { activeIndexRoots[i] = zeroHash } blockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot) for i := 0; i < len(blockRoots); i++ { blockRoots[i] = zeroHash } stateRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot) for i := 0; i < len(stateRoots); i++ { stateRoots[i] = zeroHash } slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector) genesisValidatorsRoot, err := stateutil.ValidatorRegistryRoot(preState.Validators()) if err != nil { return nil, errors.Wrapf(err, "could not hash tree root genesis validators %v", err) } scores, err := preState.InactivityScores() if err != nil { return nil, err } scoresMissing := len(preState.Validators()) - len(scores) if scoresMissing > 0 { for i := 0; i < scoresMissing; i++ { scores = append(scores, 0) } } wep, err := blocks.WrappedExecutionPayload(ep) if err != nil { return nil, err } eph, err := blocks.PayloadToHeader(wep) if err != nil { return nil, err } st := ðpb.BeaconStateBellatrix{ // Misc fields. Slot: 0, GenesisTime: genesisTime, GenesisValidatorsRoot: genesisValidatorsRoot[:], Fork: ðpb.Fork{ PreviousVersion: params.BeaconConfig().AltairForkVersion, CurrentVersion: params.BeaconConfig().BellatrixForkVersion, Epoch: 0, }, // Validator registry fields. Validators: preState.Validators(), Balances: preState.Balances(), // Randomness and committees. RandaoMixes: randaoMixes, // Finality. PreviousJustifiedCheckpoint: ðpb.Checkpoint{ Epoch: 0, Root: params.BeaconConfig().ZeroHash[:], }, CurrentJustifiedCheckpoint: ðpb.Checkpoint{ Epoch: 0, Root: params.BeaconConfig().ZeroHash[:], }, JustificationBits: []byte{0}, FinalizedCheckpoint: ðpb.Checkpoint{ Epoch: 0, Root: params.BeaconConfig().ZeroHash[:], }, HistoricalRoots: [][]byte{}, BlockRoots: blockRoots, StateRoots: stateRoots, Slashings: slashings, // Eth1 data. Eth1Data: eth1Data, Eth1DataVotes: []*ethpb.Eth1Data{}, Eth1DepositIndex: preState.Eth1DepositIndex(), LatestExecutionPayloadHeader: eph, InactivityScores: scores, } bodyRoot, err := (ðpb.BeaconBlockBodyBellatrix{ RandaoReveal: make([]byte, 96), Eth1Data: ðpb.Eth1Data{ DepositRoot: make([]byte, 32), BlockHash: make([]byte, 32), }, Graffiti: make([]byte, 32), SyncAggregate: ðpb.SyncAggregate{ SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8), SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), }, ExecutionPayload: &enginev1.ExecutionPayload{ ParentHash: make([]byte, 32), FeeRecipient: make([]byte, 20), StateRoot: make([]byte, 32), ReceiptsRoot: make([]byte, 32), LogsBloom: make([]byte, 256), PrevRandao: make([]byte, 32), BaseFeePerGas: make([]byte, 32), BlockHash: make([]byte, 32), Transactions: make([][]byte, 0), }, }).HashTreeRoot() if err != nil { return nil, errors.Wrap(err, "could not hash tree root empty block body") } st.LatestBlockHeader = ðpb.BeaconBlockHeader{ ParentRoot: zeroHash, StateRoot: zeroHash, BodyRoot: bodyRoot[:], } ist, err := state_native.InitializeFromProtoBellatrix(st) if err != nil { return nil, err } sc, err := altair.NextSyncCommittee(context.Background(), ist) if err != nil { return nil, err } if err := ist.SetNextSyncCommittee(sc); err != nil { return nil, err } if err := ist.SetCurrentSyncCommittee(sc); err != nil { return nil, err } return ist, nil } // EmptyGenesisState returns an empty beacon state object. func EmptyGenesisStateBellatrix() (state.BeaconState, error) { st := ðpb.BeaconStateBellatrix{ // Misc fields. Slot: 0, Fork: ðpb.Fork{ PreviousVersion: params.BeaconConfig().AltairForkVersion, CurrentVersion: params.BeaconConfig().BellatrixForkVersion, Epoch: 0, }, // Validator registry fields. Validators: []*ethpb.Validator{}, Balances: []uint64{}, JustificationBits: []byte{0}, HistoricalRoots: [][]byte{}, // Eth1 data. Eth1Data: ðpb.Eth1Data{}, Eth1DataVotes: []*ethpb.Eth1Data{}, Eth1DepositIndex: 0, 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), BaseFeePerGas: make([]byte, 32), BlockHash: make([]byte, 32), TransactionsRoot: make([]byte, 32), }, } return state_native.InitializeFromProtoBellatrix(st) }