github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/integration/slow_test.go (about)

     1  package integration_test
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/model/messages"
    10  	"github.com/onflow/flow-go/network/channels"
    11  )
    12  
    13  // TestMessagesLost verifies if a node lost some messages, it's still able to catch up.
    14  func TestMessagesLost(t *testing.T) {
    15  	stopper := NewStopper(30, 0)
    16  	participantsData := createConsensusIdentities(t, 4)
    17  	rootSnapshot := createRootSnapshot(t, participantsData)
    18  	nodes, hub, runFor := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper)
    19  
    20  	hub.WithFilter(blockNodesFirstMessages(10, nodes[0]))
    21  
    22  	runFor(time.Minute)
    23  
    24  	allViews := allFinalizedViews(t, nodes)
    25  	assertSafety(t, allViews)
    26  
    27  	cleanupNodes(nodes)
    28  }
    29  
    30  // TestMessagesLostAcrossNetwork verifies if each receiver lost 10% messages, the network can still reach consensus.
    31  func TestMessagesLostAcrossNetwork(t *testing.T) {
    32  	stopper := NewStopper(10, 0)
    33  	participantsData := createConsensusIdentities(t, 4)
    34  	rootSnapshot := createRootSnapshot(t, participantsData)
    35  	nodes, hub, runFor := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper)
    36  
    37  	hub.WithFilter(blockReceiverMessagesRandomly(0.1))
    38  
    39  	runFor(time.Minute)
    40  
    41  	allViews := allFinalizedViews(t, nodes)
    42  	assertSafety(t, allViews)
    43  
    44  	cleanupNodes(nodes)
    45  }
    46  
    47  // TestDelay verifies that we still reach consensus even if _all_ messages are significantly
    48  // delayed. Due to the delay, some proposals might be orphaned. The message delay is sampled
    49  // for each message from the interval [0.1*hotstuffTimeout, 0.5*hotstuffTimeout].
    50  func TestDelay(t *testing.T) {
    51  	stopper := NewStopper(30, 0)
    52  	participantsData := createConsensusIdentities(t, 4)
    53  	rootSnapshot := createRootSnapshot(t, participantsData)
    54  	nodes, hub, runFor := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper)
    55  
    56  	hub.WithFilter(delayReceiverMessagesByRange(hotstuffTimeout/10, hotstuffTimeout/2))
    57  
    58  	runFor(time.Minute)
    59  
    60  	allViews := allFinalizedViews(t, nodes)
    61  	assertSafety(t, allViews)
    62  
    63  	cleanupNodes(nodes)
    64  }
    65  
    66  // TestOneNodeBehind verifies that if one node (here node 0) consistently experiences a significant
    67  // delay receiving messages beyond the hotstuff timeout, the committee still can reach consensus.
    68  func TestOneNodeBehind(t *testing.T) {
    69  	stopper := NewStopper(30, 0)
    70  	participantsData := createConsensusIdentities(t, 3)
    71  	rootSnapshot := createRootSnapshot(t, participantsData)
    72  	nodes, hub, runFor := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper)
    73  
    74  	hub.WithFilter(func(channelID channels.Channel, event interface{}, sender, receiver *Node) (bool, time.Duration) {
    75  		if receiver == nodes[0] {
    76  			return false, hotstuffTimeout + time.Millisecond
    77  		}
    78  		// no block or delay to other nodes
    79  		return false, 0
    80  	})
    81  
    82  	runFor(time.Minute)
    83  
    84  	allViews := allFinalizedViews(t, nodes)
    85  	assertSafety(t, allViews)
    86  
    87  	cleanupNodes(nodes)
    88  }
    89  
    90  // TestTimeoutRebroadcast drops
    91  // * all proposals at view 5
    92  // * the first timeout object per view for _every_ sender
    93  // In this configuration, the _initial_ broadcast is insufficient for replicas to make
    94  // progress in view 5 (neither in the happy path, because the proposal is always dropped
    95  // nor on the unhappy path for the _first_ attempt to broadcast timeout objects). We
    96  // expect that replica will eventually broadcast its timeout object again.
    97  func TestTimeoutRebroadcast(t *testing.T) {
    98  	stopper := NewStopper(5, 0)
    99  	participantsData := createConsensusIdentities(t, 4)
   100  	rootSnapshot := createRootSnapshot(t, participantsData)
   101  	nodes, hub, runFor := createNodes(t, NewConsensusParticipants(participantsData), rootSnapshot, stopper)
   102  
   103  	// nodeID -> view -> numTimeoutMessages
   104  	lock := new(sync.Mutex)
   105  	blockedTimeoutObjectsTracker := make(map[flow.Identifier]map[uint64]uint64)
   106  	hub.WithFilter(func(channelID channels.Channel, event interface{}, sender, receiver *Node) (bool, time.Duration) {
   107  		switch m := event.(type) {
   108  		case *messages.BlockProposal:
   109  			return m.Block.Header.View == 5, 0 // drop proposals only for view 5
   110  		case *messages.TimeoutObject:
   111  			// drop first timeout object for every sender for every view
   112  			lock.Lock()
   113  			blockedPerView, found := blockedTimeoutObjectsTracker[sender.id.NodeID]
   114  			if !found {
   115  				blockedPerView = make(map[uint64]uint64)
   116  				blockedTimeoutObjectsTracker[sender.id.NodeID] = blockedPerView
   117  			}
   118  			blocked := blockedPerView[m.View] + 1
   119  			blockedPerView[m.View] = blocked
   120  			lock.Unlock()
   121  			return blocked == 1, 0
   122  		}
   123  		// no block or delay to other nodes
   124  		return false, 0
   125  	})
   126  
   127  	runFor(10 * time.Second)
   128  
   129  	allViews := allFinalizedViews(t, nodes)
   130  	assertSafety(t, allViews)
   131  
   132  	cleanupNodes(nodes)
   133  }