diff --git a/beacon-chain/core/blocks/genesis.go b/beacon-chain/core/blocks/genesis.go index afdfdfd48c68..435a02fb24e0 100644 --- a/beacon-chain/core/blocks/genesis.go +++ b/beacon-chain/core/blocks/genesis.go @@ -193,8 +193,9 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa Signature: params.BeaconConfig().EmptySignature[:], }) case *ethpb.BeaconStateGloas: + gs := ps.(*ethpb.BeaconStateGloas) return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockGloas{ - Block: gloasGenesisBlock(root), + Block: gloasGenesisBlock(root, gs.LatestExecutionPayloadBid), Signature: params.BeaconConfig().EmptySignature[:], }) default: @@ -202,7 +203,24 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa } } -func gloasGenesisBlock(root [fieldparams.RootLength]byte) *ethpb.BeaconBlockGloas { +func gloasGenesisBlock(root [fieldparams.RootLength]byte, latestBid *ethpb.ExecutionPayloadBid) *ethpb.BeaconBlockGloas { + // The genesis block body's signed_execution_payload_bid mirrors the state's + // latest_execution_payload_bid so the reconstructed block's body_root matches + // state.latest_block_header.body_root (which the genesis distribution tool + // commits to). Falling back to a zero bid is only useful in tests that + // initialize a Gloas state without populating latest_execution_payload_bid. + bidMessage := latestBid.Copy() + if bidMessage == nil { + bidMessage = ðpb.ExecutionPayloadBid{ + ParentBlockHash: make([]byte, 32), + ParentBlockRoot: make([]byte, 32), + BlockHash: make([]byte, 32), + PrevRandao: make([]byte, 32), + FeeRecipient: make([]byte, 20), + BlobKzgCommitments: make([][]byte, 0), + ExecutionRequestsRoot: make([]byte, 32), + } + } return ðpb.BeaconBlockGloas{ ParentRoot: params.BeaconConfig().ZeroHash[:], StateRoot: root[:], @@ -218,17 +236,15 @@ func gloasGenesisBlock(root [fieldparams.RootLength]byte) *ethpb.BeaconBlockGloa SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), }, SignedExecutionPayloadBid: ðpb.SignedExecutionPayloadBid{ - Message: ðpb.ExecutionPayloadBid{ - ParentBlockHash: make([]byte, 32), - ParentBlockRoot: make([]byte, 32), - BlockHash: make([]byte, 32), - PrevRandao: make([]byte, 32), - FeeRecipient: make([]byte, 20), - BlobKzgCommitments: make([][]byte, 0), - }, + Message: bidMessage, Signature: make([]byte, fieldparams.BLSSignatureLength), }, PayloadAttestations: make([]*ethpb.PayloadAttestation, 0), + ParentExecutionRequests: &enginev1.ExecutionRequests{ + Withdrawals: make([]*enginev1.WithdrawalRequest, 0), + Deposits: make([]*enginev1.DepositRequest, 0), + Consolidations: make([]*enginev1.ConsolidationRequest, 0), + }, }, } } diff --git a/changelog/barnabasbusa_fix-gloas-genesis-block.md b/changelog/barnabasbusa_fix-gloas-genesis-block.md new file mode 100644 index 000000000000..e0dec53ddc88 --- /dev/null +++ b/changelog/barnabasbusa_fix-gloas-genesis-block.md @@ -0,0 +1,3 @@ +### Fixed + +- Fixed Gloas genesis block reconstruction so the body's `signed_execution_payload_bid.message` mirrors `state.latest_execution_payload_bid`, matching the body root committed to in `state.latest_block_header`. Without this, Prysm built a divergent genesis block (different `block_root` / `body_root` from other clients) and the validator client failed to propose at slot 1 with "parent root … does not match the latest block header signing root in state". Also initializes `parent_execution_requests` so the body's SSZ hash tree root can be computed.