diff --git a/beacon-chain/core/light-client/lightclient_test.go b/beacon-chain/core/light-client/lightclient_test.go index ac687eef8abb..2916b77e7e1f 100644 --- a/beacon-chain/core/light-client/lightclient_test.go +++ b/beacon-chain/core/light-client/lightclient_test.go @@ -22,6 +22,15 @@ import ( ) func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 1 + cfg.BellatrixForkEpoch = 2 + cfg.CapellaForkEpoch = 3 + cfg.DenebForkEpoch = 4 + cfg.ElectraForkEpoch = 5 + params.OverrideBeaconConfig(cfg) + t.Run("Altair", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() @@ -59,9 +68,31 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) l.CheckSyncAggregate(update.SyncAggregate()) l.CheckAttestedHeader(update.AttestedHeader()) }) + + t.Run("Electra", func(t *testing.T) { + l := util.NewTestLightClient(t).SetupTestElectra(false) + + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock) + require.NoError(t, err) + require.NotNil(t, update, "update is nil") + + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") + + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) + }) } func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig() + cfg.AltairForkEpoch = 1 + cfg.BellatrixForkEpoch = 2 + cfg.CapellaForkEpoch = 3 + cfg.DenebForkEpoch = 4 + cfg.ElectraForkEpoch = 5 + params.OverrideBeaconConfig(cfg) + t.Run("Altair", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() @@ -356,6 +387,157 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") }) }) + + t.Run("Electra", func(t *testing.T) { + t.Run("FinalizedBlock Not Nil", func(t *testing.T) { + l := util.NewTestLightClient(t).SetupTestElectra(false) + + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + require.NoError(t, err) + require.NotNil(t, update, "update is nil") + + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") + + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) + + //zeroHash := params.BeaconConfig().ZeroHash[:] + finalizedBlockHeader, err := l.FinalizedBlock.Header() + require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() + require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") + require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") + fb, err := update.FinalityBranchElectra() + require.NoError(t, err) + proof, err := l.AttestedState.FinalizedRootProof(l.Ctx) + require.NoError(t, err) + for i, leaf := range fb { + require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal") + } + + // Check Execution BlockHash + payloadInterface, err := l.FinalizedBlock.Block().Body().Execution() + require.NoError(t, err) + transactionsRoot, err := payloadInterface.TransactionsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + transactions, err := payloadInterface.Transactions() + require.NoError(t, err) + transactionsRootArray, err := ssz.TransactionsRoot(transactions) + require.NoError(t, err) + transactionsRoot = transactionsRootArray[:] + } else { + require.NoError(t, err) + } + withdrawalsRoot, err := payloadInterface.WithdrawalsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + withdrawals, err := payloadInterface.Withdrawals() + require.NoError(t, err) + withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) + withdrawalsRoot = withdrawalsRootArray[:] + } else { + require.NoError(t, err) + } + execution := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payloadInterface.ParentHash(), + FeeRecipient: payloadInterface.FeeRecipient(), + StateRoot: payloadInterface.StateRoot(), + ReceiptsRoot: payloadInterface.ReceiptsRoot(), + LogsBloom: payloadInterface.LogsBloom(), + PrevRandao: payloadInterface.PrevRandao(), + BlockNumber: payloadInterface.BlockNumber(), + GasLimit: payloadInterface.GasLimit(), + GasUsed: payloadInterface.GasUsed(), + Timestamp: payloadInterface.Timestamp(), + ExtraData: payloadInterface.ExtraData(), + BaseFeePerGas: payloadInterface.BaseFeePerGas(), + BlockHash: payloadInterface.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + } + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") + }) + + t.Run("FinalizedBlock In Previous Fork", func(t *testing.T) { + l := util.NewTestLightClient(t).SetupTestElectraFinalizedBlockDeneb(false) + + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + require.NoError(t, err) + require.NotNil(t, update, "update is nil") + + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") + + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) + + finalizedBlockHeader, err := l.FinalizedBlock.Header() + require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() + require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") + require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") + require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") + fb, err := update.FinalityBranchElectra() + require.NoError(t, err) + proof, err := l.AttestedState.FinalizedRootProof(l.Ctx) + require.NoError(t, err) + for i, leaf := range fb { + require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal") + } + + // Check Execution BlockHash + payloadInterface, err := l.FinalizedBlock.Block().Body().Execution() + require.NoError(t, err) + transactionsRoot, err := payloadInterface.TransactionsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + transactions, err := payloadInterface.Transactions() + require.NoError(t, err) + transactionsRootArray, err := ssz.TransactionsRoot(transactions) + require.NoError(t, err) + transactionsRoot = transactionsRootArray[:] + } else { + require.NoError(t, err) + } + withdrawalsRoot, err := payloadInterface.WithdrawalsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + withdrawals, err := payloadInterface.Withdrawals() + require.NoError(t, err) + withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) + withdrawalsRoot = withdrawalsRootArray[:] + } else { + require.NoError(t, err) + } + execution := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payloadInterface.ParentHash(), + FeeRecipient: payloadInterface.FeeRecipient(), + StateRoot: payloadInterface.StateRoot(), + ReceiptsRoot: payloadInterface.ReceiptsRoot(), + LogsBloom: payloadInterface.LogsBloom(), + PrevRandao: payloadInterface.PrevRandao(), + BlockNumber: payloadInterface.BlockNumber(), + GasLimit: payloadInterface.GasLimit(), + GasUsed: payloadInterface.GasUsed(), + Timestamp: payloadInterface.Timestamp(), + ExtraData: payloadInterface.ExtraData(), + BaseFeePerGas: payloadInterface.BaseFeePerGas(), + BlockHash: payloadInterface.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + } + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") + }) + }) } func TestLightClient_BlockToLightClientHeader(t *testing.T) { diff --git a/changelog/rupam_lightclient-electra-tests.md b/changelog/rupam_lightclient-electra-tests.md new file mode 100644 index 000000000000..c4e199ee160e --- /dev/null +++ b/changelog/rupam_lightclient-electra-tests.md @@ -0,0 +1,3 @@ +### Added + +- Added Electra tests for `TestLightClient_NewLightClientOptimisticUpdateFromBeaconState` and `TestLightClient_NewLightClientFinalityUpdateFromBeaconState` \ No newline at end of file diff --git a/testing/util/lightclient.go b/testing/util/lightclient.go index a37dbc780156..5223359dc9da 100644 --- a/testing/util/lightclient.go +++ b/testing/util/lightclient.go @@ -551,22 +551,23 @@ func (l *TestLightClient) SetupTestElectra(blinded bool) *TestLightClient { ctx := context.Background() slot := primitives.Slot(params.BeaconConfig().ElectraForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) + finalizedBlockSlot := primitives.Slot(params.BeaconConfig().ElectraForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)) attestedState, err := NewBeaconStateElectra() require.NoError(l.T, err) err = attestedState.SetSlot(slot) require.NoError(l.T, err) - finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockElectra()) + finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockDeneb()) require.NoError(l.T, err) - finalizedBlock.SetSlot(1) + finalizedBlock.SetSlot(finalizedBlockSlot) finalizedHeader, err := finalizedBlock.Header() require.NoError(l.T, err) finalizedRoot, err := finalizedHeader.Header.HashTreeRoot() require.NoError(l.T, err) require.NoError(l.T, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ - Epoch: params.BeaconConfig().ElectraForkEpoch - 10, + Epoch: params.BeaconConfig().ElectraForkEpoch, Root: finalizedRoot[:], })) @@ -883,6 +884,119 @@ func (l *TestLightClient) SetupTestDenebFinalizedBlockCapella(blinded bool) *Tes return l } +func (l *TestLightClient) SetupTestElectraFinalizedBlockDeneb(blinded bool) *TestLightClient { + ctx := context.Background() + + slot := primitives.Slot(params.BeaconConfig().ElectraForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) + finalizedBlockSlot := primitives.Slot(params.BeaconConfig().DenebForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)) + + attestedState, err := NewBeaconStateElectra() + require.NoError(l.T, err) + err = attestedState.SetSlot(slot) + require.NoError(l.T, err) + + finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockDeneb()) + require.NoError(l.T, err) + finalizedBlock.SetSlot(finalizedBlockSlot) + finalizedHeader, err := finalizedBlock.Header() + require.NoError(l.T, err) + finalizedRoot, err := finalizedHeader.Header.HashTreeRoot() + require.NoError(l.T, err) + + require.NoError(l.T, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + Epoch: params.BeaconConfig().DenebForkEpoch, + Root: finalizedRoot[:], + })) + + parent := NewBeaconBlockElectra() + parent.Block.Slot = slot + + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(l.T, err) + + parentHeader, err := signedParent.Header() + require.NoError(l.T, err) + attestedHeader := parentHeader.Header + + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(l.T, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(l.T, err) + + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(l.T, err) + + state, err := NewBeaconStateElectra() + require.NoError(l.T, err) + err = state.SetSlot(slot) + require.NoError(l.T, err) + + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(l.T, err) + + var signedBlock interfaces.SignedBeaconBlock + if blinded { + block := NewBlindedBeaconBlockElectra() + block.Message.Slot = slot + block.Message.ParentRoot = parentRoot[:] + + for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ { + block.Message.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + + h, err := signedBlock.Header() + require.NoError(l.T, err) + + err = state.SetLatestBlockHeader(h.Header) + require.NoError(l.T, err) + stateRoot, err := state.HashTreeRoot(ctx) + require.NoError(l.T, err) + + // get a new signed block so the root is updated with the new state root + block.Message.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + } else { + block := NewBeaconBlockElectra() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] + + for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + + h, err := signedBlock.Header() + require.NoError(l.T, err) + + err = state.SetLatestBlockHeader(h.Header) + require.NoError(l.T, err) + stateRoot, err := state.HashTreeRoot(ctx) + require.NoError(l.T, err) + + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + } + + l.State = state + l.AttestedState = attestedState + l.AttestedBlock = signedParent + l.Block = signedBlock + l.Ctx = ctx + l.FinalizedBlock = finalizedBlock + + return l +} + func (l *TestLightClient) CheckAttestedHeader(header interfaces.LightClientHeader) { updateAttestedHeaderBeacon := header.Beacon() testAttestedHeader, err := l.AttestedBlock.Header()