Skip to content

Commit 632e73e

Browse files
authored
Merge branch 'main' into double-start
2 parents f2cb1a6 + f345849 commit 632e73e

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

epoch.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,11 @@ func (e *Epoch) triggerProposalWaitTimeExpired(round uint64) {
15871587
emptyVotes.votes[string(e.ID)] = &signedEV
15881588

15891589
e.Comm.Broadcast(&Message{EmptyVoteMessage: &signedEV})
1590+
1591+
if err := e.maybeAssembleEmptyNotarization(); err != nil {
1592+
e.Logger.Error("Failed assembling empty notarization", zap.Error(err))
1593+
e.haltedError = err
1594+
}
15901595
}
15911596

15921597
func (e *Epoch) monitorProgress(round uint64) {

epoch_failover_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
18120
func TestEpochLeaderFailover(t *testing.T) {
19121
l := testutil.MakeLogger(t, 1)
20122

0 commit comments

Comments
 (0)