mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-21 15:29:27 +00:00
beacon/blsync: test validated finality (#32633)
This PR improves `TestBlockSync` so that it also tests the finality update validation. Note: to this date four long and complex (at least partly AI generated) PRs arrived that did something related to testing finality but honestly we do not need a bloated "comprehensive" test to test a trivial feature because maintaining these tests can also be a pain over the long term. This PR adds some sufficient sanity checks to detect if finality ever gets broken by a future change.
This commit is contained in:
parent
2b3d617e04
commit
b08b629818
1 changed files with 42 additions and 15 deletions
|
|
@ -32,7 +32,7 @@ var (
|
||||||
testServer2 = testServer("testServer2")
|
testServer2 = testServer("testServer2")
|
||||||
|
|
||||||
testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{
|
testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{
|
||||||
Slot: 123,
|
Slot: 127,
|
||||||
Body: deneb.BeaconBlockBody{
|
Body: deneb.BeaconBlockBody{
|
||||||
ExecutionPayload: deneb.ExecutionPayload{
|
ExecutionPayload: deneb.ExecutionPayload{
|
||||||
BlockNumber: 456,
|
BlockNumber: 456,
|
||||||
|
|
@ -41,7 +41,7 @@ var (
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
testBlock2 = types.NewBeaconBlock(&deneb.BeaconBlock{
|
testBlock2 = types.NewBeaconBlock(&deneb.BeaconBlock{
|
||||||
Slot: 124,
|
Slot: 128,
|
||||||
Body: deneb.BeaconBlockBody{
|
Body: deneb.BeaconBlockBody{
|
||||||
ExecutionPayload: deneb.ExecutionPayload{
|
ExecutionPayload: deneb.ExecutionPayload{
|
||||||
BlockNumber: 457,
|
BlockNumber: 457,
|
||||||
|
|
@ -49,6 +49,14 @@ var (
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
testFinal1 = types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{
|
||||||
|
BlockNumber: 395,
|
||||||
|
BlockHash: zrntcommon.Hash32(common.HexToHash("abbe7625624bf8ddd84723709e2758956289465dd23475f02387e0854942666")),
|
||||||
|
})
|
||||||
|
testFinal2 = types.NewExecutionHeader(&deneb.ExecutionPayloadHeader{
|
||||||
|
BlockNumber: 420,
|
||||||
|
BlockHash: zrntcommon.Hash32(common.HexToHash("9182a6ef8723654de174283750932ccc092378549836bf4873657eeec474598")),
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
type testServer string
|
type testServer string
|
||||||
|
|
@ -66,9 +74,10 @@ func TestBlockSync(t *testing.T) {
|
||||||
ts.AddServer(testServer1, 1)
|
ts.AddServer(testServer1, 1)
|
||||||
ts.AddServer(testServer2, 1)
|
ts.AddServer(testServer2, 1)
|
||||||
|
|
||||||
expHeadBlock := func(expHead *types.BeaconBlock) {
|
expHeadEvent := func(expHead *types.BeaconBlock, expFinal *types.ExecutionHeader) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var expNumber, headNumber uint64
|
var expNumber, headNumber uint64
|
||||||
|
var expFinalHash, finalHash common.Hash
|
||||||
if expHead != nil {
|
if expHead != nil {
|
||||||
p, err := expHead.ExecutionPayload()
|
p, err := expHead.ExecutionPayload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -76,19 +85,26 @@ func TestBlockSync(t *testing.T) {
|
||||||
}
|
}
|
||||||
expNumber = p.NumberU64()
|
expNumber = p.NumberU64()
|
||||||
}
|
}
|
||||||
|
if expFinal != nil {
|
||||||
|
expFinalHash = expFinal.BlockHash()
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case event := <-headCh:
|
case event := <-headCh:
|
||||||
headNumber = event.Block.NumberU64()
|
headNumber = event.Block.NumberU64()
|
||||||
|
finalHash = event.Finalized
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if headNumber != expNumber {
|
if headNumber != expNumber {
|
||||||
t.Errorf("Wrong head block, expected block number %d, got %d)", expNumber, headNumber)
|
t.Errorf("Wrong head block, expected block number %d, got %d)", expNumber, headNumber)
|
||||||
}
|
}
|
||||||
|
if finalHash != expFinalHash {
|
||||||
|
t.Errorf("Wrong finalized block, expected block hash %064x, got %064x)", expFinalHash[:], finalHash[:])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no block requests expected until head tracker knows about a head
|
// no block requests expected until head tracker knows about a head
|
||||||
ts.Run(1)
|
ts.Run(1)
|
||||||
expHeadBlock(nil)
|
expHeadEvent(nil, nil)
|
||||||
|
|
||||||
// set block 1 as prefetch head, announced by server 2
|
// set block 1 as prefetch head, announced by server 2
|
||||||
head1 := blockHeadInfo(testBlock1)
|
head1 := blockHeadInfo(testBlock1)
|
||||||
|
|
@ -103,12 +119,13 @@ func TestBlockSync(t *testing.T) {
|
||||||
ts.AddAllowance(testServer2, 1)
|
ts.AddAllowance(testServer2, 1)
|
||||||
ts.Run(3)
|
ts.Run(3)
|
||||||
// head block still not expected as the fetched block is not the validated head yet
|
// head block still not expected as the fetched block is not the validated head yet
|
||||||
expHeadBlock(nil)
|
expHeadEvent(nil, nil)
|
||||||
|
|
||||||
// set as validated head, expect no further requests but block 1 set as head block
|
// set as validated head, expect no further requests but block 1 set as head block
|
||||||
ht.validated.Header = testBlock1.Header()
|
ht.validated.Header = testBlock1.Header()
|
||||||
|
ht.finalized, ht.finalizedPayload = testBlock1.Header(), testFinal1
|
||||||
ts.Run(4)
|
ts.Run(4)
|
||||||
expHeadBlock(testBlock1)
|
expHeadEvent(testBlock1, testFinal1)
|
||||||
|
|
||||||
// set block 2 as prefetch head, announced by server 1
|
// set block 2 as prefetch head, announced by server 1
|
||||||
head2 := blockHeadInfo(testBlock2)
|
head2 := blockHeadInfo(testBlock2)
|
||||||
|
|
@ -126,17 +143,26 @@ func TestBlockSync(t *testing.T) {
|
||||||
// expect req2 retry to server 2
|
// expect req2 retry to server 2
|
||||||
ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot))
|
ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot))
|
||||||
// now head block should be unavailable again
|
// now head block should be unavailable again
|
||||||
expHeadBlock(nil)
|
expHeadEvent(nil, nil)
|
||||||
|
|
||||||
// valid response, now head block should be block 2 immediately as it is already validated
|
// valid response, now head block should be block 2 immediately as it is already validated
|
||||||
|
// but head event is still not expected because an epoch boundary was crossed and the
|
||||||
|
// expected finality update has not arrived yet
|
||||||
ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2)
|
ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2)
|
||||||
ts.Run(8)
|
ts.Run(8)
|
||||||
expHeadBlock(testBlock2)
|
expHeadEvent(nil, nil)
|
||||||
|
|
||||||
|
// expected finality update arrived, now a head event is expected
|
||||||
|
ht.finalized, ht.finalizedPayload = testBlock2.Header(), testFinal2
|
||||||
|
ts.Run(9)
|
||||||
|
expHeadEvent(testBlock2, testFinal2)
|
||||||
}
|
}
|
||||||
|
|
||||||
type testHeadTracker struct {
|
type testHeadTracker struct {
|
||||||
prefetch types.HeadInfo
|
prefetch types.HeadInfo
|
||||||
validated types.SignedHeader
|
validated types.SignedHeader
|
||||||
|
finalized types.Header
|
||||||
|
finalizedPayload *types.ExecutionHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testHeadTracker) PrefetchHead() types.HeadInfo {
|
func (h *testHeadTracker) PrefetchHead() types.HeadInfo {
|
||||||
|
|
@ -151,13 +177,14 @@ func (h *testHeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) {
|
||||||
}, h.validated.Header != (types.Header{})
|
}, h.validated.Header != (types.Header{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add test case for finality
|
|
||||||
func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
|
||||||
finalized := types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader))
|
if h.validated.Header == (types.Header{}) || h.finalizedPayload == nil {
|
||||||
|
return types.FinalityUpdate{}, false
|
||||||
|
}
|
||||||
return types.FinalityUpdate{
|
return types.FinalityUpdate{
|
||||||
Attested: types.HeaderWithExecProof{Header: h.validated.Header},
|
Attested: types.HeaderWithExecProof{Header: h.finalized},
|
||||||
Finalized: types.HeaderWithExecProof{PayloadHeader: finalized},
|
Finalized: types.HeaderWithExecProof{Header: h.finalized, PayloadHeader: h.finalizedPayload},
|
||||||
Signature: h.validated.Signature,
|
Signature: h.validated.Signature,
|
||||||
SignatureSlot: h.validated.SignatureSlot,
|
SignatureSlot: h.validated.SignatureSlot,
|
||||||
}, h.validated.Header != (types.Header{})
|
}, true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue