From d8021c7d09c5d6112d9ffa2cf65bd164c44f314c Mon Sep 17 00:00:00 2001 From: caplost Date: Thu, 17 Jul 2025 16:04:16 +0800 Subject: [PATCH] beacon/blsync: add finality test case This commit adds a comprehensive test case for the finality update functionality in the beacon block sync module. The test verifies: - Chain head events are properly triggered for optimistic updates - Finality information is correctly included in chain head events - The block sync properly handles both optimistic and finality updates The test uses two different blocks to ensure proper event triggering and validates that finalized block hashes are correctly propagated through the chain head event system. --- beacon/blsync/block_sync_test.go | 123 +++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/beacon/blsync/block_sync_test.go b/beacon/blsync/block_sync_test.go index e7c2c4d163..907dbeeca5 100644 --- a/beacon/blsync/block_sync_test.go +++ b/beacon/blsync/block_sync_test.go @@ -137,6 +137,7 @@ func TestBlockSync(t *testing.T) { type testHeadTracker struct { prefetch types.HeadInfo validated types.SignedHeader + finalized types.HeaderWithExecProof } func (h *testHeadTracker) PrefetchHead() types.HeadInfo { @@ -144,20 +145,128 @@ func (h *testHeadTracker) PrefetchHead() types.HeadInfo { } func (h *testHeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) { + if h.validated.Header == (types.Header{}) { + return types.OptimisticUpdate{}, false + } + + // Create execution header for the attested block + execHeader := types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{ + BlockNumber: 456, + BlockHash: zrntcommon.Hash32(common.HexToHash("905ac721c4058d9ed40b27b6b9c1bdd10d4333e4f3d9769100bf9dfb80e5d1f6")), + }) + return types.OptimisticUpdate{ - Attested: types.HeaderWithExecProof{Header: h.validated.Header}, + Attested: types.HeaderWithExecProof{Header: h.validated.Header, PayloadHeader: execHeader}, Signature: h.validated.Signature, SignatureSlot: h.validated.SignatureSlot, - }, h.validated.Header != (types.Header{}) + }, true } -// TODO add test case for finality func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) { - finalized := types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader)) + if h.validated.Header == (types.Header{}) { + return types.FinalityUpdate{}, false + } + + // Use the finalized field if set, otherwise create a default one + finalized := h.finalized + if finalized.PayloadHeader == nil { + finalized = types.HeaderWithExecProof{PayloadHeader: types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader))} + return types.FinalityUpdate{ + Attested: types.HeaderWithExecProof{Header: h.validated.Header}, + Finalized: finalized, + Signature: h.validated.Signature, + SignatureSlot: h.validated.SignatureSlot, + }, false // Return false when no real finalized data + } + + // Create execution header for the attested block + execHeader := types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{ + BlockNumber: 456, + BlockHash: zrntcommon.Hash32(common.HexToHash("905ac721c4058d9ed40b27b6b9c1bdd10d4333e4f3d9769100bf9dfb80e5d1f6")), + }) + return types.FinalityUpdate{ - Attested: types.HeaderWithExecProof{Header: h.validated.Header}, - Finalized: types.HeaderWithExecProof{PayloadHeader: finalized}, + Attested: types.HeaderWithExecProof{Header: h.validated.Header, PayloadHeader: execHeader}, + Finalized: finalized, Signature: h.validated.Signature, SignatureSlot: h.validated.SignatureSlot, - }, h.validated.Header != (types.Header{}) + }, true // Return true when finalized data is available +} + +// TestBlockSyncFinality tests the finality update functionality in block sync +func TestBlockSyncFinality(t *testing.T) { + ht := &testHeadTracker{} + blockSync := newBeaconBlockSync(ht) + headCh := make(chan types.ChainHeadEvent, 16) + blockSync.SubscribeChainHead(headCh) + ts := sync.NewTestScheduler(t, blockSync) + ts.AddServer(testServer1, 1) + ts.AddServer(testServer2, 1) + + // Helper function to check chain head event + checkChainHeadEvent := func(expectedFinalizedHash common.Hash) { + t.Helper() + select { + case event := <-headCh: + if event.Finalized != expectedFinalizedHash { + t.Errorf("Wrong finalized hash, expected %v, got %v", expectedFinalizedHash, event.Finalized) + } + t.Logf("Chain head event received: block=%d, finalized=%v", event.Block.NumberU64(), event.Finalized) + default: + t.Error("Expected chain head event, but got none") + } + } + + // Set block 1 as prefetch head + head1 := blockHeadInfo(testBlock1) + ht.prefetch = head1 + ts.ServerEvent(sync.EvNewHead, testServer1, head1) + + // Request and receive block 1 + ts.Run(1, testServer1, sync.ReqBeaconBlock(head1.BlockRoot)) + ts.RequestEvent(request.EvResponse, ts.Request(1, 1), testBlock1) + ts.AddAllowance(testServer1, 1) + ts.Run(2) + + // Set as validated head - this should trigger a chain head event without finality + ht.validated.Header = testBlock1.Header() + ts.Run(3) + + // Should get a chain head event without finalized hash + checkChainHeadEvent(common.Hash{}) + + // Now test finality update with block 2 to ensure different head info + head2 := blockHeadInfo(testBlock2) + ht.prefetch = head2 + ts.ServerEvent(sync.EvNewHead, testServer2, head2) + + // Request and receive block 2 + ts.Run(4, testServer2, sync.ReqBeaconBlock(head2.BlockRoot)) + ts.RequestEvent(request.EvResponse, ts.Request(4, 1), testBlock2) + ts.AddAllowance(testServer2, 1) + ts.Run(5) + + // Create a finalized execution header + finalizedExecHeader := types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{ + BlockNumber: 400, // Earlier block number + BlockHash: zrntcommon.Hash32(common.HexToHash("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890")), + }) + + // Create a finalized header with the same epoch as the attested header + finalizedHeader := types.Header{ + Slot: testBlock2.Header().Slot, // Same epoch as block 2 + } + + // Update the testHeadTracker to return finality info and set block 2 as validated + ht.finalized = types.HeaderWithExecProof{ + Header: finalizedHeader, + PayloadHeader: finalizedExecHeader, + } + ht.validated.Header = testBlock2.Header() + + // Trigger another update to check finality + ts.Run(6) + + // Should now have a finalized hash in the chain head event + checkChainHeadEvent(finalizedExecHeader.BlockHash()) }