github.com/koko1123/flow-go-1@v0.29.6/consensus/integration/integration_test.go (about) 1 package integration_test 2 3 import ( 4 "context" 5 "sort" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/require" 10 11 "github.com/koko1123/flow-go-1/model/flow" 12 "github.com/koko1123/flow-go-1/module/irrecoverable" 13 "github.com/koko1123/flow-go-1/module/util" 14 "github.com/koko1123/flow-go-1/network/channels" 15 "github.com/koko1123/flow-go-1/utils/unittest" 16 ) 17 18 func runNodes(signalerCtx irrecoverable.SignalerContext, nodes []*Node) { 19 for _, n := range nodes { 20 go func(n *Node) { 21 n.aggregator.Start(signalerCtx) 22 <-util.AllReady(n.aggregator, n.compliance, n.sync) 23 }(n) 24 } 25 } 26 27 func stopNodes(t *testing.T, cancel context.CancelFunc, nodes []*Node) { 28 stoppingNodes := make([]<-chan struct{}, 0) 29 cancel() 30 for _, n := range nodes { 31 stoppingNodes = append(stoppingNodes, util.AllDone(n.aggregator, n.compliance, n.sync)) 32 } 33 unittest.RequireCloseBefore(t, util.AllClosed(stoppingNodes...), time.Minute, "requiring nodes to stop") 34 } 35 36 // happy path: with 3 nodes, they can reach consensus 37 func Test3Nodes(t *testing.T) { 38 stopper := NewStopper(5, 0) 39 participantsData := createConsensusIdentities(t, 3) 40 rootSnapshot := createRootSnapshot(t, participantsData) 41 nodes, hub := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper) 42 43 hub.WithFilter(blockNothing) 44 45 ctx, cancel := context.WithCancel(context.Background()) 46 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 47 48 runNodes(signalerCtx, nodes) 49 50 unittest.AssertClosesBefore(t, stopper.stopped, 30*time.Second) 51 52 allViews := allFinalizedViews(t, nodes) 53 assertSafety(t, allViews) 54 55 stopNodes(t, cancel, nodes) 56 cleanupNodes(nodes) 57 } 58 59 // with 5 nodes, and one node completely blocked, the other 4 nodes can still reach consensus 60 func Test5Nodes(t *testing.T) { 61 // 4 nodes should be able finalize at least 3 blocks. 62 stopper := NewStopper(2, 1) 63 participantsData := createConsensusIdentities(t, 5) 64 rootSnapshot := createRootSnapshot(t, participantsData) 65 nodes, hub := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper) 66 67 hub.WithFilter(blockNodes(nodes[0])) 68 ctx, cancel := context.WithCancel(context.Background()) 69 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 70 71 runNodes(signalerCtx, nodes) 72 73 <-stopper.stopped 74 75 header, err := nodes[0].state.Final().Head() 76 require.NoError(t, err) 77 78 // the first node was blocked, never finalize any block 79 require.Equal(t, uint64(0), header.View) 80 81 allViews := allFinalizedViews(t, nodes[1:]) 82 assertSafety(t, allViews) 83 84 stopNodes(t, cancel, nodes) 85 cleanupNodes(nodes) 86 } 87 88 // TODO: verify if each receiver lost 50% messages, the network can't reach consensus 89 90 func allFinalizedViews(t *testing.T, nodes []*Node) [][]uint64 { 91 allViews := make([][]uint64, 0) 92 93 // verify all nodes arrive at the same state 94 for _, node := range nodes { 95 views := chainViews(t, node) 96 allViews = append(allViews, views) 97 } 98 99 // sort all Views by chain length 100 sort.Slice(allViews, func(i, j int) bool { 101 return len(allViews[i]) < len(allViews[j]) 102 }) 103 104 return allViews 105 } 106 107 func assertSafety(t *testing.T, allViews [][]uint64) { 108 // find the longest chain of finalized views 109 longest := allViews[len(allViews)-1] 110 111 for _, views := range allViews { 112 // each view in a chain should match with the longest chain 113 for j, view := range views { 114 require.Equal(t, longest[j], view, "each view in a chain must match with the view in longest chain at the same height, but didn't") 115 } 116 } 117 } 118 119 func chainViews(t *testing.T, node *Node) []uint64 { 120 views := make([]uint64, 0) 121 122 head, err := node.state.Final().Head() 123 require.NoError(t, err) 124 for head != nil && head.View > 0 { 125 views = append(views, head.View) 126 head, err = node.headers.ByBlockID(head.ParentID) 127 require.NoError(t, err) 128 } 129 130 // reverse all views to start from lower view to higher view 131 132 low2high := make([]uint64, 0) 133 for i := len(views) - 1; i >= 0; i-- { 134 low2high = append(low2high, views[i]) 135 } 136 return low2high 137 } 138 139 type BlockOrDelayFunc func(channel channels.Channel, event interface{}, sender, receiver *Node) (bool, time.Duration) 140 141 // block nothing 142 func blockNothing(channel channels.Channel, event interface{}, sender, receiver *Node) (bool, time.Duration) { 143 return false, 0 144 } 145 146 // block all messages sent by or received by a list of denied nodes 147 func blockNodes(denyList ...*Node) BlockOrDelayFunc { 148 blackList := make(map[flow.Identifier]*Node, len(denyList)) 149 for _, n := range denyList { 150 blackList[n.id.ID()] = n 151 } 152 return func(channel channels.Channel, event interface{}, sender, receiver *Node) (bool, time.Duration) { 153 block, notBlock := true, false 154 if _, ok := blackList[sender.id.ID()]; ok { 155 return block, 0 156 } 157 if _, ok := blackList[receiver.id.ID()]; ok { 158 return block, 0 159 } 160 return notBlock, 0 161 } 162 }