From a966dcbe7d40130875057b48b7fb8dfd495b66ad Mon Sep 17 00:00:00 2001 From: Zsolt Felfoldi Date: Fri, 19 Apr 2024 17:39:22 +0200 Subject: [PATCH] beacon/light/sync: fix head sync test and add finality test cases --- beacon/light/sync/head_sync.go | 12 ++++----- beacon/light/sync/head_sync_test.go | 42 +++++++++++++++++++++++------ beacon/light/sync/test_helpers.go | 7 ++++- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/beacon/light/sync/head_sync.go b/beacon/light/sync/head_sync.go index 64c079777587..dd05d3958879 100644 --- a/beacon/light/sync/head_sync.go +++ b/beacon/light/sync/head_sync.go @@ -73,6 +73,12 @@ func NewHeadSync(headTracker headTracker, chain committeeChain) *HeadSync { // Process implements request.Module. func (s *HeadSync) Process(requester request.Requester, events []request.Event) { + nextPeriod, chainInit := s.chain.NextSyncPeriod() + if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit { + s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit + s.processUnvalidatedUpdates() + } + for _, event := range events { switch event.Type { case EvNewHead: @@ -101,12 +107,6 @@ func (s *HeadSync) Process(requester request.Requester, events []request.Event) delete(s.unvalidatedFinality, event.Server) } } - - nextPeriod, chainInit := s.chain.NextSyncPeriod() - if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit { - s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit - s.processUnvalidatedUpdates() - } } // newOptimisticUpdate handles received optimistic update; either validates it if diff --git a/beacon/light/sync/head_sync_test.go b/beacon/light/sync/head_sync_test.go index af0bda82f98e..cd7dacf7fe7d 100644 --- a/beacon/light/sync/head_sync_test.go +++ b/beacon/light/sync/head_sync_test.go @@ -19,6 +19,7 @@ package sync import ( "testing" + "github.com/ethereum/go-ethereum/beacon/light/request" "github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/common" ) @@ -28,6 +29,7 @@ var ( testServer2 = testServer("testServer2") testServer3 = testServer("testServer3") testServer4 = testServer("testServer4") + testServer5 = testServer("testServer5") testHead0 = types.HeadInfo{} testHead1 = types.HeadInfo{Slot: 123, BlockRoot: common.Hash{1}} @@ -42,6 +44,14 @@ var ( testOptUpdate4 = types.OptimisticUpdate{SignatureSlot: 0x6444, Attested: types.HeaderWithExecProof{Header: types.Header{Slot: 0x6443, StateRoot: common.Hash{4}}}} ) +func finality(opt types.OptimisticUpdate) types.FinalityUpdate { + return types.FinalityUpdate{ + SignatureSlot: opt.SignatureSlot, + Attested: opt.Attested, + Finalized: types.HeaderWithExecProof{Header: types.Header{Slot: (opt.Attested.Header.Slot - 64) & uint64(0xffffffffffffffe0)}}, + } +} + type testServer string func (t testServer) Name() string { @@ -58,7 +68,7 @@ func TestValidatedHead(t *testing.T) { ts.AddServer(testServer1, 1) ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate1) - ts.Run(1) + ts.Run(1, testServer1, ReqFinality{}) // announced head should be queued because of uninitialized chain ht.ExpValidated(t, 1, nil) @@ -68,6 +78,7 @@ func TestValidatedHead(t *testing.T) { ht.ExpValidated(t, 2, []types.OptimisticUpdate{testOptUpdate1}) chain.SetNextSyncPeriod(1) + ts.ServerEvent(EvNewFinalityUpdate, testServer1, finality(testOptUpdate2)) ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate2) ts.AddServer(testServer2, 1) ts.ServerEvent(EvNewOptimisticUpdate, testServer2, testOptUpdate2) @@ -78,7 +89,8 @@ func TestValidatedHead(t *testing.T) { ts.ServerEvent(EvNewOptimisticUpdate, testServer1, testOptUpdate3) ts.AddServer(testServer3, 1) ts.ServerEvent(EvNewOptimisticUpdate, testServer3, testOptUpdate4) - ts.Run(4) + // finality should be requested from both servers + ts.Run(4, testServer1, ReqFinality{}, testServer3, ReqFinality{}) // future period annonced heads should be queued ht.ExpValidated(t, 4, nil) @@ -87,20 +99,34 @@ func TestValidatedHead(t *testing.T) { // testOptUpdate3 can be validated now but not testOptUpdate4 ht.ExpValidated(t, 5, []types.OptimisticUpdate{testOptUpdate3}) + ts.AddServer(testServer4, 1) + ts.ServerEvent(EvNewOptimisticUpdate, testServer4, testOptUpdate3) + // new server joined with recent optimistic update but still no finality; should be requested + ts.Run(6, testServer4, ReqFinality{}) + ht.ExpValidated(t, 6, []types.OptimisticUpdate{testOptUpdate3}) + + ts.AddServer(testServer5, 1) + ts.RequestEvent(request.EvResponse, ts.Request(6, 1), finality(testOptUpdate3)) + ts.ServerEvent(EvNewOptimisticUpdate, testServer5, testOptUpdate3) + // finality update request answered; new server should not be requested + ts.Run(7) + ht.ExpValidated(t, 7, []types.OptimisticUpdate{testOptUpdate3}) + // server 3 disconnected without proving period 3, its announced head should be dropped ts.RemoveServer(testServer3) - ts.Run(6) - ht.ExpValidated(t, 6, nil) + ts.Run(8) + ht.ExpValidated(t, 8, nil) chain.SetNextSyncPeriod(3) - ts.Run(7) + ts.Run(9) // testOptUpdate4 could be validated now but it's not queued by any registered server - ht.ExpValidated(t, 7, nil) + ht.ExpValidated(t, 9, nil) + ts.ServerEvent(EvNewFinalityUpdate, testServer2, finality(testOptUpdate4)) ts.ServerEvent(EvNewOptimisticUpdate, testServer2, testOptUpdate4) - ts.Run(8) + ts.Run(10) // now testOptUpdate4 should be validated - ht.ExpValidated(t, 8, []types.OptimisticUpdate{testOptUpdate4}) + ht.ExpValidated(t, 10, []types.OptimisticUpdate{testOptUpdate4}) } func TestPrefetchHead(t *testing.T) { diff --git a/beacon/light/sync/test_helpers.go b/beacon/light/sync/test_helpers.go index 11ea4e470a14..cfca8ad8a45f 100644 --- a/beacon/light/sync/test_helpers.go +++ b/beacon/light/sync/test_helpers.go @@ -213,6 +213,7 @@ func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) { type TestHeadTracker struct { phead types.HeadInfo validated []types.OptimisticUpdate + finality types.FinalityUpdate } func (ht *TestHeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) { @@ -220,11 +221,15 @@ func (ht *TestHeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bo return true, nil } -//TODO add test case for finality func (ht *TestHeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) { + ht.finality = update return true, nil } +func (ht *TestHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) { + return ht.finality, ht.finality.Attested.Header != (types.Header{}) +} + func (ht *TestHeadTracker) ExpValidated(t *testing.T, tci int, expHeads []types.OptimisticUpdate) { for i, expHead := range expHeads { if i >= len(ht.validated) {