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  }