github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/integration/integration_test.go (about) 1 package integration 2 3 import ( 4 "errors" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/onflow/flow-go/consensus/hotstuff/pacemaker/timeout" 13 "github.com/onflow/flow-go/utils/unittest" 14 ) 15 16 // a pacemaker timeout to wait for proposals. Usually 10 ms is enough, 17 // but for slow environment like CI, a longer one is needed. 18 const safeTimeout = 2 * time.Second 19 20 // number of failed rounds before first timeout increase 21 const happyPathMaxRoundFailures = 6 22 23 func TestSingleInstance(t *testing.T) { 24 25 // set up a single instance to run 26 // NOTE: currently, the HotStuff logic will infinitely call back on itself 27 // with a single instance, leading to a boundlessly growing call stack, 28 // which slows down the mocks significantly due to splitting the callstack 29 // to find the calling function name; we thus keep it low for now 30 finalView := uint64(10) 31 in := NewInstance(t, 32 WithStopCondition(ViewFinalized(finalView)), 33 ) 34 35 // run the event handler until we reach a stop condition 36 err := in.Run() 37 require.ErrorIs(t, err, errStopCondition, "should run until stop condition") 38 39 // check if forks and pacemaker are in expected view state 40 assert.Equal(t, finalView, in.forks.FinalizedView(), "finalized view should be three lower than current view") 41 } 42 43 func TestThreeInstances(t *testing.T) { 44 // test parameters 45 // NOTE: block finalization seems to be rather slow on CI at the moment, 46 // needing around 1 minute on Travis for 1000 blocks and 10 minutes on 47 // TeamCity for 1000 blocks; in order to avoid test timeouts, we keep the 48 // number low here 49 num := 3 50 finalView := uint64(100) 51 52 // generate three hotstuff participants 53 participants := unittest.IdentityListFixture(num) 54 root := DefaultRoot() 55 timeouts, err := timeout.NewConfig(safeTimeout, safeTimeout, 1.5, happyPathMaxRoundFailures, safeTimeout) 56 require.NoError(t, err) 57 58 // set up three instances that are exactly the same 59 // since we don't block any messages we should have enough data to advance in happy path 60 // for that reason we will block all TO related communication. 61 instances := make([]*Instance, 0, num) 62 for n := 0; n < num; n++ { 63 in := NewInstance(t, 64 WithRoot(root), 65 WithParticipants(participants), 66 WithLocalID(participants[n].NodeID), 67 WithTimeouts(timeouts), 68 WithStopCondition(ViewFinalized(finalView)), 69 WithIncomingTimeoutObjects(BlockAllTimeoutObjects), 70 ) 71 instances = append(instances, in) 72 } 73 74 // connect the communicators of the instances together 75 Connect(t, instances) 76 77 // start the instances and wait for them to finish 78 var wg sync.WaitGroup 79 for _, in := range instances { 80 wg.Add(1) 81 go func(in *Instance) { 82 err := in.Run() 83 require.True(t, errors.Is(err, errStopCondition), "should run until stop condition") 84 wg.Done() 85 }(in) 86 } 87 wg.Wait() 88 89 // check that all instances have the same finalized block 90 in1 := instances[0] 91 in2 := instances[1] 92 in3 := instances[2] 93 // verify progress has been made 94 assert.GreaterOrEqual(t, in1.forks.FinalizedBlock().View, finalView, "the first instance 's finalized view should be four lower than current view") 95 // verify same progresses have been made 96 assert.Equal(t, in1.forks.FinalizedBlock(), in2.forks.FinalizedBlock(), "second instance should have same finalized block as first instance") 97 assert.Equal(t, in1.forks.FinalizedBlock(), in3.forks.FinalizedBlock(), "third instance should have same finalized block as first instance") 98 assert.Equal(t, FinalizedViews(in1), FinalizedViews(in2)) 99 assert.Equal(t, FinalizedViews(in1), FinalizedViews(in3)) 100 } 101 102 func TestSevenInstances(t *testing.T) { 103 // test parameters 104 // NOTE: block finalization seems to be rather slow on CI at the moment, 105 // needing around 1 minute on Travis for 1000 blocks and 10 minutes on 106 // TeamCity for 1000 blocks; in order to avoid test timeouts, we keep the 107 // number low here 108 numPass := 5 109 numFail := 2 110 111 // When using 100 as finalView, I often saw this tests fail on CI, because it only made to around 64-86 112 // so using 30 will still check that it's making progress and give enough buffer. 113 finalView := uint64(30) 114 115 // generate the seven hotstuff participants 116 participants := unittest.IdentityListFixture(numPass + numFail) 117 instances := make([]*Instance, 0, numPass+numFail) 118 root := DefaultRoot() 119 timeouts, err := timeout.NewConfig(safeTimeout, safeTimeout, 1.5, happyPathMaxRoundFailures, safeTimeout) 120 require.NoError(t, err) 121 122 // set up five instances that work fully 123 for n := 0; n < numPass; n++ { 124 in := NewInstance(t, 125 WithRoot(root), 126 WithParticipants(participants), 127 WithLocalID(participants[n].NodeID), 128 WithTimeouts(timeouts), 129 WithStopCondition(ViewFinalized(finalView)), 130 ) 131 instances = append(instances, in) 132 } 133 134 // set up two instances which can't vote 135 for n := numPass; n < numPass+numFail; n++ { 136 in := NewInstance(t, 137 WithRoot(root), 138 WithParticipants(participants), 139 WithLocalID(participants[n].NodeID), 140 WithTimeouts(timeouts), 141 WithStopCondition(ViewFinalized(finalView)), 142 WithOutgoingVotes(BlockAllVotes), 143 ) 144 instances = append(instances, in) 145 } 146 147 // connect the communicators of the instances together 148 Connect(t, instances) 149 150 // start all seven instances and wait for them to wrap up 151 var wg sync.WaitGroup 152 for _, in := range instances { 153 wg.Add(1) 154 go func(in *Instance) { 155 err := in.Run() 156 require.True(t, errors.Is(err, errStopCondition), "should run until stop condition") 157 wg.Done() 158 }(in) 159 } 160 wg.Wait() 161 162 // check that all instances have the same finalized block 163 ref := instances[0] 164 assert.Less(t, finalView-uint64(2*numPass+numFail), ref.forks.FinalizedBlock().View, "expect instance 0 should made enough progress, but didn't") 165 finalizedViews := FinalizedViews(ref) 166 for i := 1; i < numPass; i++ { 167 assert.Equal(t, ref.forks.FinalizedBlock(), instances[i].forks.FinalizedBlock(), "instance %d should have same finalized block as first instance") 168 assert.Equal(t, finalizedViews, FinalizedViews(instances[i]), "instance %d should have same finalized view as first instance") 169 } 170 }