@@ -331,3 +331,92 @@ func shutdownClonedNode(nodeDataDir string, f *fixtures.RestClientFixture, t *te
331331 os .RemoveAll (nodeDataDir )
332332 }
333333}
334+
335+ // TestBasicCatchupCompletes confirms the the catchup eventually completes and stops.
336+ func TestBasicCatchupCompletes (t * testing.T ) {
337+ partitiontest .PartitionTest (t )
338+ defer fixtures .ShutdownSynchronizedTest (t )
339+
340+ if testing .Short () {
341+ t .Skip ()
342+ }
343+ t .Parallel ()
344+ a := require .New (fixtures .SynchronizedTest (t ))
345+
346+ // Make the network progress faster
347+ consensus := make (config.ConsensusProtocols )
348+ fastProtocol := config .Consensus [protocol .ConsensusCurrentVersion ]
349+ fastProtocol .ApprovedUpgrades = map [protocol.ConsensusVersion ]uint64 {}
350+ fastProtocol .AgreementFilterTimeoutPeriod0 = 400 * time .Millisecond
351+ fastProtocol .AgreementFilterTimeout = 400 * time .Millisecond
352+ consensus [protocol .ConsensusCurrentVersion ] = fastProtocol
353+
354+ // Setup the fixture with the modified fast consensus
355+ var fixture fixtures.RestClientFixture
356+ fixture .SetConsensus (consensus )
357+ fixture .Setup (t , filepath .Join ("nettemplates" , "TwoNodes100Second.json" ))
358+ defer fixture .Shutdown ()
359+
360+ // Get 2nd node so we wait until we know they're at target block
361+ nc , err := fixture .GetNodeController ("Node" )
362+ a .NoError (err )
363+
364+ // Let the network make some progress.
365+ // Make it long enough so the catchup to it is longer than a single round agreement
366+ a .NoError (err )
367+ waitForRound := uint64 (100 )
368+
369+ // Now prepare a third node
370+ cloneDataDir := filepath .Join (fixture .PrimaryDataDir (), "../clone" )
371+ cloneLedger := false
372+ err = fixture .NC .Clone (cloneDataDir , cloneLedger )
373+ a .NoError (err )
374+
375+ // Wait for the network to make some progess.
376+ err = fixture .ClientWaitForRoundWithTimeout (fixture .GetAlgodClientForController (nc ), waitForRound )
377+ a .NoError (err )
378+
379+ // Start the third node to catchup.
380+ startTime := time .Now ()
381+ cloneClient , err := fixture .StartNode (cloneDataDir )
382+ a .NoError (err )
383+ defer shutdownClonedNode (cloneDataDir , & fixture , t )
384+
385+ // Wait for it to catchup
386+ err = fixture .LibGoalFixture .ClientWaitForRoundWithTimeout (cloneClient , waitForRound )
387+ a .NoError (err )
388+
389+ // Calculate the catchup time
390+ catchupTime := time .Since (startTime )
391+
392+ // Check if curStatus.CatchupTime, the "Time since last block" is less than the catchup time.
393+ // - If the catchup has not stopped, this value will keep on growing, and eventually be larger than the time
394+ // of a single round agreement.
395+ // - If the catchup stops after it completes, this value will be the time since the last round was
396+ // obtained through the agreement, and be much smaller than the catchup time.
397+ client := fixture .GetAlgodClientForController (fixture .LibGoalFixture .GetNodeControllerForDataDir (cloneDataDir ))
398+
399+ // Prevent false positive
400+ // - Since obtaining the exact catchup time is not possible, wait catchupTime again, to make sure curStatus.CatchupTime
401+ // will be at least our estimated catchupTime (since it keeps on growing if catchup has not stopped).
402+ time .Sleep (catchupTime )
403+
404+ // Prevent false negative
405+ // The network may have made some progress since waitForRound, it could be that the
406+ // third node is still catching up even after getting to waitForRound.
407+ // Moreover, it takes some time to transition from the catchup to agreement.
408+ // Give it some more time and check again..
409+ pass := false
410+ for x := 0 ; x < 100 ; x ++ {
411+ curStatus , statusErr := client .Status ()
412+ require .NoError (t , statusErr , "fixture should be able to get node status" )
413+ currentStateMsec := time .Duration (curStatus .CatchupTime ).Milliseconds ()
414+ catchupMsec := catchupTime .Milliseconds ()
415+ pass = currentStateMsec < catchupMsec
416+ if pass {
417+ break
418+ }
419+ time .Sleep (100 * time .Millisecond )
420+ }
421+ a .True (pass )
422+ }
0 commit comments