github.com/celestiaorg/celestia-node@v0.15.0-beta.1/nodebuilder/tests/sync_test.go (about)

     1  //go:build sync || integration
     2  
     3  package tests
     4  
     5  import (
     6  	"context"
     7  	"testing"
     8  	"time"
     9  
    10  	ma "github.com/multiformats/go-multiaddr"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/celestiaorg/celestia-node/nodebuilder/tests/swamp"
    15  )
    16  
    17  // Common consts for tests producing filled blocks
    18  const (
    19  	numBlocks = 20
    20  	bsize     = 16
    21  	sbtime    = time.Millisecond * 300
    22  )
    23  
    24  /*
    25  Test-Case: Header and block/sample sync against a Bridge Node of non-empty blocks.
    26  
    27  Steps:
    28  1. Create a Bridge Node(BN)
    29  2. Start a BN
    30  3. Check BN is synced to height 20
    31  
    32  Light node:
    33  4. Create a Light Node (LN) with bridge as a trusted peer
    34  5. Start a LN with a defined connection to the BN
    35  6. Check LN is header-synced to height 20
    36  7. Wait until LN has sampled height 20
    37  8. Wait for LN DASer to catch up to network head
    38  
    39  Full node:
    40  4. Create a Full Node (FN) with bridge as a trusted peer
    41  5. Start a FN with a defined connection to the BN
    42  6. Check FN is header-synced to height 20
    43  7. Wait until FN has synced block at height 20
    44  8. Wait for FN DASer to catch up to network head
    45  */
    46  func TestSyncAgainstBridge_NonEmptyChain(t *testing.T) {
    47  	ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout)
    48  	t.Cleanup(cancel)
    49  
    50  	sw := swamp.NewSwamp(t, swamp.WithBlockTime(sbtime))
    51  	// wait for core network to fill 20 blocks
    52  	fillDn := swamp.FillBlocks(ctx, sw.ClientContext, sw.Accounts, bsize, numBlocks)
    53  	sw.WaitTillHeight(ctx, numBlocks)
    54  
    55  	// create a bridge node and set it as the bootstrapper for the suite
    56  	bridge := sw.NewBridgeNode()
    57  	sw.SetBootstrapper(t, bridge)
    58  	// start bridge and wait for it to sync to 20
    59  	err := bridge.Start(ctx)
    60  	require.NoError(t, err)
    61  	bridgeClient := getAdminClient(ctx, bridge, t)
    62  
    63  	h, err := bridgeClient.Header.WaitForHeight(ctx, numBlocks)
    64  	require.NoError(t, err)
    65  	require.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
    66  
    67  	t.Run("light sync against bridge", func(t *testing.T) {
    68  		// create a light node that is connected to the bridge node as
    69  		// a bootstrapper
    70  		light := sw.NewLightNode()
    71  		// start light node and wait for it to sync 20 blocks
    72  		err = light.Start(ctx)
    73  		require.NoError(t, err)
    74  		lightClient := getAdminClient(ctx, light, t)
    75  		h, err = lightClient.Header.WaitForHeight(ctx, numBlocks)
    76  		require.NoError(t, err)
    77  		assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
    78  
    79  		// check that the light node has also sampled over the block at height 20
    80  		err = lightClient.Share.SharesAvailable(ctx, h)
    81  		assert.NoError(t, err)
    82  
    83  		// wait until the entire chain (up to network head) has been sampled
    84  		err = lightClient.DAS.WaitCatchUp(ctx)
    85  		require.NoError(t, err)
    86  	})
    87  
    88  	t.Run("full sync against bridge", func(t *testing.T) {
    89  		// create a full node with bridge node as its bootstrapper
    90  		full := sw.NewFullNode()
    91  		// let full node sync 20 blocks
    92  		err = full.Start(ctx)
    93  		require.NoError(t, err)
    94  		fullClient := getAdminClient(ctx, full, t)
    95  		h, err = fullClient.Header.WaitForHeight(ctx, numBlocks)
    96  		require.NoError(t, err)
    97  		assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
    98  
    99  		// check to ensure the full node can sync the 20th block's data
   100  		err = fullClient.Share.SharesAvailable(ctx, h)
   101  		assert.NoError(t, err)
   102  
   103  		// wait for full node to sync up the blocks from genesis -> network head.
   104  		err = fullClient.DAS.WaitCatchUp(ctx)
   105  		require.NoError(t, err)
   106  	})
   107  
   108  	// wait for the core block filling process to exit
   109  	select {
   110  	case <-ctx.Done():
   111  		t.Fatal(ctx.Err())
   112  	case err := <-fillDn:
   113  		require.NoError(t, err)
   114  	}
   115  }
   116  
   117  /*
   118  Test-Case: Header and block/sample sync against a Bridge Node of empty blocks.
   119  
   120  Steps:
   121  1. Create a Bridge Node(BN)
   122  2. Start a BN
   123  3. Check BN is synced to height 20
   124  
   125  Light node:
   126  4. Create a Light Node (LN) with bridge as a trusted peer
   127  5. Start a LN with a defined connection to the BN
   128  6. Check LN is header-synced to height 20
   129  7. Wait until LN has sampled height 20
   130  8. Wait for LN DASer to catch up to network head
   131  
   132  Full node:
   133  4. Create a Full Node (FN) with bridge as a trusted peer
   134  5. Start a FN with a defined connection to the BN
   135  6. Check FN is header-synced to height 20
   136  7. Wait until FN has synced block at height 20
   137  8. Wait for FN DASer to catch up to network head
   138  */
   139  func TestSyncAgainstBridge_EmptyChain(t *testing.T) {
   140  	ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout)
   141  	t.Cleanup(cancel)
   142  
   143  	sw := swamp.NewSwamp(t, swamp.WithBlockTime(sbtime))
   144  	sw.WaitTillHeight(ctx, numBlocks)
   145  
   146  	// create bridge node and set it as the bootstrapper for the suite
   147  	bridge := sw.NewBridgeNode()
   148  	sw.SetBootstrapper(t, bridge)
   149  	// start  bridge and wait for it to sync to 20
   150  	err := bridge.Start(ctx)
   151  	require.NoError(t, err)
   152  	bridgeClient := getAdminClient(ctx, bridge, t)
   153  	h, err := bridgeClient.Header.WaitForHeight(ctx, numBlocks)
   154  	require.NoError(t, err)
   155  	require.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   156  
   157  	t.Run("light sync against bridge", func(t *testing.T) {
   158  		// create a light node that is connected to the bridge node as
   159  		// a bootstrapper
   160  		light := sw.NewLightNode()
   161  		// start light node and wait for it to sync 20 blocks
   162  		err = light.Start(ctx)
   163  		require.NoError(t, err)
   164  		lightClient := getAdminClient(ctx, light, t)
   165  		h, err = lightClient.Header.WaitForHeight(ctx, numBlocks)
   166  		require.NoError(t, err)
   167  		assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   168  
   169  		// check that the light node has also sampled over the block at height 20
   170  		err = lightClient.Share.SharesAvailable(ctx, h)
   171  		assert.NoError(t, err)
   172  
   173  		// wait until the entire chain (up to network head) has been sampled
   174  		err = lightClient.DAS.WaitCatchUp(ctx)
   175  		require.NoError(t, err)
   176  	})
   177  
   178  	t.Run("full sync against bridge", func(t *testing.T) {
   179  		// create a full node with bridge node as its bootstrapper
   180  		full := sw.NewFullNode()
   181  		// let full node sync 20 blocks
   182  		err = full.Start(ctx)
   183  		require.NoError(t, err)
   184  		fullClient := getAdminClient(ctx, full, t)
   185  		h, err = fullClient.Header.WaitForHeight(ctx, numBlocks)
   186  		require.NoError(t, err)
   187  		assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   188  
   189  		// check to ensure the full node can sync the 20th block's data
   190  		err = fullClient.Share.SharesAvailable(ctx, h)
   191  		assert.NoError(t, err)
   192  
   193  		// wait for full node to sync up the blocks from genesis -> network head.
   194  		err = fullClient.DAS.WaitCatchUp(ctx)
   195  		require.NoError(t, err)
   196  	})
   197  }
   198  
   199  /*
   200  Test-Case: Light Node continues sync after abrupt stop/start
   201  Pre-Requisites:
   202  - CoreClient is started by swamp
   203  - CoreClient has generated 50 blocks
   204  Steps:
   205  1. Create a Bridge Node(BN)
   206  2. Start a BN
   207  3. Check BN is synced to height 20
   208  4. Create a Light Node(LN) with a trusted peer
   209  5. Start a LN with a defined connection to the BN
   210  6. Check LN is synced to height 20
   211  7. Disconnect LN from BN for 3 seconds while BN continues broadcasting new blocks from core
   212  8. Re-connect LN and let it sync up again
   213  9. Check LN is synced to height 40
   214  */
   215  func TestSyncStartStopLightWithBridge(t *testing.T) {
   216  	if testing.Short() {
   217  		t.Skip("skipping TestSyncStartStopLightWithBridge test in short mode.")
   218  	}
   219  
   220  	ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout)
   221  	defer cancel()
   222  
   223  	sw := swamp.NewSwamp(t)
   224  	// wait for core network to fill 20 blocks
   225  	fillDn := swamp.FillBlocks(ctx, sw.ClientContext, sw.Accounts, bsize, numBlocks)
   226  	sw.WaitTillHeight(ctx, numBlocks)
   227  
   228  	// create bridge and set it as a bootstrapper
   229  	bridge := sw.NewBridgeNode()
   230  	sw.SetBootstrapper(t, bridge)
   231  	// and let bridge node sync up 20 blocks
   232  	err := bridge.Start(ctx)
   233  	require.NoError(t, err)
   234  	bridgeClient := getAdminClient(ctx, bridge, t)
   235  	h, err := bridgeClient.Header.WaitForHeight(ctx, numBlocks)
   236  	require.NoError(t, err)
   237  	require.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   238  
   239  	// create a light node and connect it to the bridge node as a bootstrapper
   240  	light := sw.NewLightNode()
   241  	// start light node and let it sync to 20
   242  	err = light.Start(ctx)
   243  	require.NoError(t, err)
   244  	lightClient := getAdminClient(ctx, light, t)
   245  	h, err = lightClient.Header.WaitForHeight(ctx, numBlocks)
   246  	require.NoError(t, err)
   247  	require.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   248  
   249  	sw.StopNode(ctx, light)
   250  
   251  	light = sw.NewLightNode()
   252  	require.NoError(t, light.Start(ctx))
   253  
   254  	// ensure when light node comes back up, it can sync the remainder of the chain it
   255  	// missed while sleeping
   256  	h, err = lightClient.Header.WaitForHeight(ctx, 40)
   257  	require.NoError(t, err)
   258  	assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, 40))
   259  
   260  	// wait for the core block filling process to exit
   261  	select {
   262  	case <-ctx.Done():
   263  		t.Fatal(ctx.Err())
   264  	case err := <-fillDn:
   265  		require.NoError(t, err)
   266  	}
   267  }
   268  
   269  /*
   270  Test-Case: Sync a Light Node from a Full Node
   271  Pre-Requisites:
   272  - CoreClient is started by swamp
   273  - CoreClient has generated 20 blocks
   274  Steps:
   275  1. Create a Bridge Node(BN)
   276  2. Start a BN
   277  3. Check BN is synced to height 20
   278  4. Create a Full Node(FN) with a connection to BN as a trusted peer
   279  5. Start a FN
   280  6. Check FN is synced to network head
   281  7. Create a Light Node(LN) with a connection to FN as a trusted peer
   282  8. Ensure LN is NOT connected to BN and only connected to FN
   283  9. Start LN
   284  10. Check LN is synced to network head
   285  */
   286  func TestSyncLightAgainstFull(t *testing.T) {
   287  	ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout)
   288  	t.Cleanup(cancel)
   289  
   290  	sw := swamp.NewSwamp(t)
   291  	// wait for the core network to fill up 20 blocks
   292  	fillDn := swamp.FillBlocks(ctx, sw.ClientContext, sw.Accounts, bsize, numBlocks)
   293  	sw.WaitTillHeight(ctx, numBlocks)
   294  
   295  	// create bridge and set it as a bootstrapper
   296  	bridge := sw.NewBridgeNode()
   297  	sw.SetBootstrapper(t, bridge)
   298  	// start a bridge node and wait for it to sync up 20 blocks
   299  	err := bridge.Start(ctx)
   300  	require.NoError(t, err)
   301  	bridgeClient := getAdminClient(ctx, bridge, t)
   302  	h, err := bridgeClient.Header.WaitForHeight(ctx, numBlocks)
   303  	require.NoError(t, err)
   304  	assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   305  
   306  	// create a FN with BN as a trusted peer
   307  	full := sw.NewFullNode()
   308  	// start FN and wait for it to sync up to head of BN
   309  	err = full.Start(ctx)
   310  	require.NoError(t, err)
   311  	fullClient := getAdminClient(ctx, full, t)
   312  	bridgeHead, err := bridgeClient.Header.LocalHead(ctx)
   313  	require.NoError(t, err)
   314  	_, err = fullClient.Header.WaitForHeight(ctx, bridgeHead.Height())
   315  	require.NoError(t, err)
   316  	assert.EqualValues(t, h.Commit.BlockID.Hash, sw.GetCoreBlockHashByHeight(ctx, numBlocks))
   317  
   318  	// reset suite bootstrapper list and set full node as a bootstrapper for
   319  	// LN to connect to
   320  	sw.Bootstrappers = make([]ma.Multiaddr, 0)
   321  	sw.SetBootstrapper(t, full)
   322  
   323  	// create an LN with FN as a trusted peer
   324  	light := sw.NewLightNode()
   325  
   326  	// ensure there is no direct connection between LN and BN so that
   327  	// LN relies only on FN for syncing
   328  	err = sw.Network.UnlinkPeers(bridge.Host.ID(), light.Host.ID())
   329  	require.NoError(t, err)
   330  
   331  	// start LN and wait for it to sync up to network head against the head of the FN
   332  	err = light.Start(ctx)
   333  	require.NoError(t, err)
   334  	lightClient := getAdminClient(ctx, light, t)
   335  	fullHead, err := fullClient.Header.LocalHead(ctx)
   336  	require.NoError(t, err)
   337  	_, err = lightClient.Header.WaitForHeight(ctx, fullHead.Height())
   338  	require.NoError(t, err)
   339  
   340  	// wait for the core block filling process to exit
   341  	select {
   342  	case <-ctx.Done():
   343  		t.Fatal(ctx.Err())
   344  	case err := <-fillDn:
   345  		require.NoError(t, err)
   346  	}
   347  }
   348  
   349  /*
   350  Test-Case: Sync a Light Node with multiple trusted peers
   351  Pre-Requisites:
   352  - CoreClient is started by swamp
   353  - CoreClient has generated 20 blocks
   354  Steps:
   355  1. Create a Bridge Node(BN)
   356  2. Start a BN
   357  3. Check BN is synced to height 20
   358  4. Create a Full Node(FN) with a connection to BN as a trusted peer
   359  5. Start a FN
   360  6. Check FN is synced to network head
   361  7. Create a Light Node(LN) with a connection to BN and FN as trusted peers
   362  8. Start LN
   363  9. Check LN is synced to network head.
   364  */
   365  func TestSyncLightWithTrustedPeers(t *testing.T) {
   366  	ctx, cancel := context.WithTimeout(context.Background(), swamp.DefaultTestTimeout)
   367  	t.Cleanup(cancel)
   368  
   369  	sw := swamp.NewSwamp(t)
   370  	fillDn := swamp.FillBlocks(ctx, sw.ClientContext, sw.Accounts, bsize, numBlocks)
   371  	sw.WaitTillHeight(ctx, numBlocks)
   372  
   373  	// create a BN and set as a bootstrapper
   374  	bridge := sw.NewBridgeNode()
   375  	sw.SetBootstrapper(t, bridge)
   376  	// let it sync to network head
   377  	err := bridge.Start(ctx)
   378  	require.NoError(t, err)
   379  	bridgeClient := getAdminClient(ctx, bridge, t)
   380  	_, err = bridgeClient.Header.WaitForHeight(ctx, numBlocks)
   381  	require.NoError(t, err)
   382  
   383  	// create a FN with BN as trusted peer
   384  	full := sw.NewFullNode()
   385  
   386  	// let FN sync to network head
   387  	err = full.Start(ctx)
   388  	require.NoError(t, err)
   389  	fullClient := getAdminClient(ctx, full, t)
   390  	err = fullClient.Header.SyncWait(ctx)
   391  	require.NoError(t, err)
   392  
   393  	// add full node as a bootstrapper for the suite
   394  	sw.SetBootstrapper(t, full)
   395  
   396  	// create a LN with both FN and BN as trusted peers
   397  	light := sw.NewLightNode()
   398  
   399  	// let LN sync to network head
   400  	err = light.Start(ctx)
   401  	require.NoError(t, err)
   402  	lightClient := getAdminClient(ctx, light, t)
   403  	err = lightClient.Header.SyncWait(ctx)
   404  	require.NoError(t, err)
   405  
   406  	// wait for the core block filling process to exit
   407  	select {
   408  	case <-ctx.Done():
   409  		t.Fatal(ctx.Err())
   410  	case err := <-fillDn:
   411  		require.NoError(t, err)
   412  	}
   413  }