@@ -15,6 +15,108 @@ import (
1515 "go.uber.org/zap/zapcore"
1616)
1717
18+ func TestEpochLeaderFailoverReceivesEmptyVotesEarly (t * testing.T ) {
19+ l := testutil .MakeLogger (t , 1 )
20+
21+ bb := & testBlockBuilder {out : make (chan * testBlock , 1 ), blockShouldBeBuilt : make (chan struct {}, 1 )}
22+ storage := newInMemStorage ()
23+
24+ nodes := []NodeID {{1 }, {2 }, {3 }, {4 }}
25+ quorum := Quorum (len (nodes ))
26+
27+ wal := newTestWAL (t )
28+
29+ start := time .Now ()
30+ conf := EpochConfig {
31+ MaxProposalWait : DefaultMaxProposalWaitTime ,
32+ StartTime : start ,
33+ Logger : l ,
34+ ID : nodes [0 ],
35+ Signer : & testSigner {},
36+ WAL : wal ,
37+ Verifier : & testVerifier {},
38+ Storage : storage ,
39+ Comm : noopComm (nodes ),
40+ BlockBuilder : bb ,
41+ SignatureAggregator : & testSignatureAggregator {},
42+ }
43+
44+ e , err := NewEpoch (conf )
45+ require .NoError (t , err )
46+
47+ require .NoError (t , e .Start ())
48+
49+ // Run through 3 blocks, to make the block proposals be:
50+ // 1 --> 2 --> 3 --> X (node 4 doesn't propose a block)
51+
52+ // Then, don't do anything and wait for our node
53+ // to start complaining about a block not being notarized
54+
55+ for round := uint64 (0 ); round < 3 ; round ++ {
56+ notarizeAndFinalizeRound (t , nodes , round , round , e , bb , quorum , storage , false )
57+ }
58+
59+ lastBlock , _ , ok := storage .Retrieve (storage .Height () - 1 )
60+ require .True (t , ok )
61+
62+ prev := lastBlock .BlockHeader ().Digest
63+
64+ emptyBlockMd := ProtocolMetadata {
65+ Round : 3 ,
66+ Seq : 2 ,
67+ Prev : prev ,
68+ }
69+
70+ emptyVoteFrom1 := createEmptyVote (emptyBlockMd , nodes [1 ])
71+ emptyVoteFrom2 := createEmptyVote (emptyBlockMd , nodes [2 ])
72+
73+ e .HandleMessage (& Message {
74+ EmptyVoteMessage : emptyVoteFrom1 ,
75+ }, nodes [1 ])
76+ e .HandleMessage (& Message {
77+ EmptyVoteMessage : emptyVoteFrom2 ,
78+ }, nodes [2 ])
79+
80+ bb .blockShouldBeBuilt <- struct {}{}
81+
82+ waitForBlockProposerTimeout (t , e , start )
83+
84+ wal .lock .Lock ()
85+ walContent , err := wal .ReadAll ()
86+ require .NoError (t , err )
87+ wal .lock .Unlock ()
88+
89+ rawEmptyVote , rawEmptyNotarization , rawProposal := walContent [len (walContent )- 3 ], walContent [len (walContent )- 2 ], walContent [len (walContent )- 1 ]
90+ emptyVote , err := ParseEmptyVoteRecord (rawEmptyVote )
91+ require .NoError (t , err )
92+ require .Equal (t , createEmptyVote (emptyBlockMd , nodes [0 ]).Vote , emptyVote )
93+
94+ emptyNotarization , err := EmptyNotarizationFromRecord (rawEmptyNotarization , & testQCDeserializer {t : t })
95+ require .NoError (t , err )
96+ require .Equal (t , emptyVoteFrom1 .Vote , emptyNotarization .Vote )
97+ require .Equal (t , uint64 (3 ), emptyNotarization .Vote .Round )
98+ require .Equal (t , uint64 (2 ), emptyNotarization .Vote .Seq )
99+ require .Equal (t , uint64 (3 ), storage .Height ())
100+
101+ header , _ , err := ParseBlockRecord (rawProposal )
102+ require .NoError (t , err )
103+ require .Equal (t , uint64 (4 ), header .Round )
104+ require .Equal (t , uint64 (3 ), header .Seq )
105+
106+ // Ensure our node proposes block with sequence 3 for round 4
107+ block := <- bb .out
108+
109+ for i := 1 ; i <= quorum ; i ++ {
110+ injectTestFinalization (t , e , block , nodes [i ])
111+ }
112+
113+ block2 := storage .waitForBlockCommit (3 )
114+ require .Equal (t , block , block2 )
115+ require .Equal (t , uint64 (4 ), storage .Height ())
116+ require .Equal (t , uint64 (4 ), block2 .BlockHeader ().Round )
117+ require .Equal (t , uint64 (3 ), block2 .BlockHeader ().Seq )
118+ }
119+
18120func TestEpochLeaderFailover (t * testing.T ) {
19121 l := testutil .MakeLogger (t , 1 )
20122
0 commit comments