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