Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions epoch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,17 @@ func (e *Epoch) monitorProgress(round uint64) {
return
}

// Check if we have advanced to a higher round in the meantime while this task was dispatched.
e.lock.Lock()
shouldAbort := round < e.round
e.lock.Unlock()

if shouldAbort {
e.Logger.Debug("Aborting monitoring progress for round because we advanced to a higher round",
zap.Uint64("monitored round", round), zap.Uint64("new round", e.round))
return
}

// This invocation blocks until the block builder tells us it's time to build a new block.
e.BlockBuilder.WaitForPendingBlock(ctx)
// While we waited, a block might have been notarized.
Expand Down
50 changes: 50 additions & 0 deletions epoch_failover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,56 @@ func TestEpochLeaderFailover(t *testing.T) {
})
}

func TestEpochLeaderRecursivelyFetchNotarizedBlocks(t *testing.T) {
nodes := []NodeID{{1}, {2}, {3}, {4}}
bb := &testutil.TestBlockBuilder{Out: make(chan *testutil.TestBlock, 1), BlockShouldBeBuilt: make(chan struct{}, 1)}

recordedMessages := make(chan *Message, 100)

comm := &recordingComm{Communication: testutil.NoopComm(nodes), SentMessages: recordedMessages}
conf, wal, _ := testutil.DefaultTestNodeEpochConfig(t, nodes[0], comm, bb)

e, err := NewEpoch(conf)
require.NoError(t, err)

nodeID := nodes[0]

// Send the last block and notarization

require.NoError(t, e.Start())

startTime := e.StartTime

// Feed the node empty notarizations until it advances to the round of the last block.
for round := uint64(0); round < 6; round++ {
emptyNotarization := testutil.NewEmptyNotarization(nodes[1:], round)

if LeaderForRound(nodes, round).Equals(nodeID) {
testutil.WaitToEnterRound(t, e, round)
t.Log("Triggering block to be built for round", round)
bb.BlockShouldBeBuilt <- struct{}{}
testutil.WaitForBlockProposerTimeout(t, e, &startTime, round)

err = e.HandleMessage(&Message{
EmptyNotarization: emptyNotarization,
}, nodes[2])
require.NoError(t, err)

wal.AssertNotarization(round)
continue
}

// Otherwise, just receive the empty notarization
// and advance to the next round
err = e.HandleMessage(&Message{
EmptyNotarization: emptyNotarization,
}, nodes[2])
require.NoError(t, err)
wal.AssertNotarization(round)
testutil.WaitToEnterRound(t, e, round)
}
}

func TestEpochLeaderFailoverInLeaderRound(t *testing.T) {
nodes := []NodeID{{1}, {2}, {3}, {4}}

Expand Down
Loading