github.com/MetalBlockchain/metalgo@v1.11.9/x/sync/sync_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package sync
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"math/rand"
    10  	"slices"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  	"go.uber.org/mock/gomock"
    16  
    17  	"github.com/MetalBlockchain/metalgo/database"
    18  	"github.com/MetalBlockchain/metalgo/database/memdb"
    19  	"github.com/MetalBlockchain/metalgo/ids"
    20  	"github.com/MetalBlockchain/metalgo/utils/logging"
    21  	"github.com/MetalBlockchain/metalgo/utils/maybe"
    22  	"github.com/MetalBlockchain/metalgo/x/merkledb"
    23  
    24  	pb "github.com/MetalBlockchain/metalgo/proto/pb/sync"
    25  )
    26  
    27  func newCallthroughSyncClient(ctrl *gomock.Controller, db merkledb.MerkleDB) *MockClient {
    28  	syncClient := NewMockClient(ctrl)
    29  	syncClient.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn(
    30  		func(_ context.Context, request *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) {
    31  			return db.GetRangeProof(
    32  				context.Background(),
    33  				maybeBytesToMaybe(request.StartKey),
    34  				maybeBytesToMaybe(request.EndKey),
    35  				int(request.KeyLimit),
    36  			)
    37  		}).AnyTimes()
    38  	syncClient.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
    39  		func(_ context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) {
    40  			startRoot, err := ids.ToID(request.StartRootHash)
    41  			if err != nil {
    42  				return nil, err
    43  			}
    44  
    45  			endRoot, err := ids.ToID(request.EndRootHash)
    46  			if err != nil {
    47  				return nil, err
    48  			}
    49  
    50  			changeProof, err := db.GetChangeProof(
    51  				context.Background(),
    52  				startRoot,
    53  				endRoot,
    54  				maybeBytesToMaybe(request.StartKey),
    55  				maybeBytesToMaybe(request.EndKey),
    56  				int(request.KeyLimit),
    57  			)
    58  			if err != nil {
    59  				return nil, err
    60  			}
    61  			return &merkledb.ChangeOrRangeProof{
    62  				ChangeProof: changeProof,
    63  			}, nil
    64  		}).AnyTimes()
    65  	return syncClient
    66  }
    67  
    68  func Test_Creation(t *testing.T) {
    69  	require := require.New(t)
    70  	ctrl := gomock.NewController(t)
    71  	defer ctrl.Finish()
    72  
    73  	db, err := merkledb.New(
    74  		context.Background(),
    75  		memdb.New(),
    76  		newDefaultDBConfig(),
    77  	)
    78  	require.NoError(err)
    79  
    80  	syncer, err := NewManager(ManagerConfig{
    81  		DB:                    db,
    82  		Client:                NewMockClient(ctrl),
    83  		TargetRoot:            ids.Empty,
    84  		SimultaneousWorkLimit: 5,
    85  		Log:                   logging.NoLog{},
    86  		BranchFactor:          merkledb.BranchFactor16,
    87  	})
    88  	require.NoError(err)
    89  	require.NotNil(syncer)
    90  }
    91  
    92  func Test_Completion(t *testing.T) {
    93  	require := require.New(t)
    94  	ctrl := gomock.NewController(t)
    95  	defer ctrl.Finish()
    96  
    97  	emptyDB, err := merkledb.New(
    98  		context.Background(),
    99  		memdb.New(),
   100  		newDefaultDBConfig(),
   101  	)
   102  	require.NoError(err)
   103  
   104  	emptyRoot, err := emptyDB.GetMerkleRoot(context.Background())
   105  	require.NoError(err)
   106  
   107  	db, err := merkledb.New(
   108  		context.Background(),
   109  		memdb.New(),
   110  		newDefaultDBConfig(),
   111  	)
   112  	require.NoError(err)
   113  
   114  	syncer, err := NewManager(ManagerConfig{
   115  		DB:                    db,
   116  		Client:                newCallthroughSyncClient(ctrl, emptyDB),
   117  		TargetRoot:            emptyRoot,
   118  		SimultaneousWorkLimit: 5,
   119  		Log:                   logging.NoLog{},
   120  		BranchFactor:          merkledb.BranchFactor16,
   121  	})
   122  	require.NoError(err)
   123  	require.NotNil(syncer)
   124  
   125  	require.NoError(syncer.Start(context.Background()))
   126  	require.NoError(syncer.Wait(context.Background()))
   127  
   128  	syncer.workLock.Lock()
   129  	require.Zero(syncer.unprocessedWork.Len())
   130  	require.Equal(1, syncer.processedWork.Len())
   131  	syncer.workLock.Unlock()
   132  }
   133  
   134  func Test_Midpoint(t *testing.T) {
   135  	require := require.New(t)
   136  
   137  	mid := midPoint(maybe.Some([]byte{1, 255}), maybe.Some([]byte{2, 1}))
   138  	require.Equal(maybe.Some([]byte{2, 0}), mid)
   139  
   140  	mid = midPoint(maybe.Nothing[[]byte](), maybe.Some([]byte{255, 255, 0}))
   141  	require.Equal(maybe.Some([]byte{127, 255, 128}), mid)
   142  
   143  	mid = midPoint(maybe.Some([]byte{255, 255, 255}), maybe.Some([]byte{255, 255}))
   144  	require.Equal(maybe.Some([]byte{255, 255, 127, 128}), mid)
   145  
   146  	mid = midPoint(maybe.Nothing[[]byte](), maybe.Some([]byte{255}))
   147  	require.Equal(maybe.Some([]byte{127, 127}), mid)
   148  
   149  	mid = midPoint(maybe.Some([]byte{1, 255}), maybe.Some([]byte{255, 1}))
   150  	require.Equal(maybe.Some([]byte{128, 128}), mid)
   151  
   152  	mid = midPoint(maybe.Some([]byte{140, 255}), maybe.Some([]byte{141, 0}))
   153  	require.Equal(maybe.Some([]byte{140, 255, 127}), mid)
   154  
   155  	mid = midPoint(maybe.Some([]byte{126, 255}), maybe.Some([]byte{127}))
   156  	require.Equal(maybe.Some([]byte{126, 255, 127}), mid)
   157  
   158  	mid = midPoint(maybe.Nothing[[]byte](), maybe.Nothing[[]byte]())
   159  	require.Equal(maybe.Some([]byte{127}), mid)
   160  
   161  	low := midPoint(maybe.Nothing[[]byte](), mid)
   162  	require.Equal(maybe.Some([]byte{63, 127}), low)
   163  
   164  	high := midPoint(mid, maybe.Nothing[[]byte]())
   165  	require.Equal(maybe.Some([]byte{191}), high)
   166  
   167  	mid = midPoint(maybe.Some([]byte{255, 255}), maybe.Nothing[[]byte]())
   168  	require.Equal(maybe.Some([]byte{255, 255, 127, 127}), mid)
   169  
   170  	mid = midPoint(maybe.Some([]byte{255}), maybe.Nothing[[]byte]())
   171  	require.Equal(maybe.Some([]byte{255, 127, 127}), mid)
   172  
   173  	for i := 0; i < 5000; i++ {
   174  		r := rand.New(rand.NewSource(int64(i))) // #nosec G404
   175  
   176  		start := make([]byte, r.Intn(99)+1)
   177  		_, err := r.Read(start)
   178  		require.NoError(err)
   179  
   180  		end := make([]byte, r.Intn(99)+1)
   181  		_, err = r.Read(end)
   182  		require.NoError(err)
   183  
   184  		for bytes.Equal(start, end) {
   185  			_, err = r.Read(end)
   186  			require.NoError(err)
   187  		}
   188  
   189  		if bytes.Compare(start, end) == 1 {
   190  			start, end = end, start
   191  		}
   192  
   193  		mid = midPoint(maybe.Some(start), maybe.Some(end))
   194  		require.Equal(-1, bytes.Compare(start, mid.Value()))
   195  		require.Equal(-1, bytes.Compare(mid.Value(), end))
   196  	}
   197  }
   198  
   199  func Test_Sync_FindNextKey_InSync(t *testing.T) {
   200  	require := require.New(t)
   201  	ctrl := gomock.NewController(t)
   202  	defer ctrl.Finish()
   203  
   204  	now := time.Now().UnixNano()
   205  	t.Logf("seed: %d", now)
   206  	r := rand.New(rand.NewSource(now)) // #nosec G404
   207  	dbToSync, err := generateTrie(t, r, 1000)
   208  	require.NoError(err)
   209  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   210  	require.NoError(err)
   211  
   212  	db, err := merkledb.New(
   213  		context.Background(),
   214  		memdb.New(),
   215  		newDefaultDBConfig(),
   216  	)
   217  	require.NoError(err)
   218  
   219  	syncer, err := NewManager(ManagerConfig{
   220  		DB:                    db,
   221  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   222  		TargetRoot:            syncRoot,
   223  		SimultaneousWorkLimit: 5,
   224  		Log:                   logging.NoLog{},
   225  		BranchFactor:          merkledb.BranchFactor16,
   226  	})
   227  	require.NoError(err)
   228  	require.NotNil(syncer)
   229  
   230  	require.NoError(syncer.Start(context.Background()))
   231  	require.NoError(syncer.Wait(context.Background()))
   232  
   233  	proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 500)
   234  	require.NoError(err)
   235  
   236  	// the two dbs should be in sync, so next key should be nil
   237  	lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key
   238  	nextKey, err := syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof)
   239  	require.NoError(err)
   240  	require.True(nextKey.IsNothing())
   241  
   242  	// add an extra value to sync db past the last key returned
   243  	newKey := midPoint(maybe.Some(lastKey), maybe.Nothing[[]byte]())
   244  	newKeyVal := newKey.Value()
   245  	require.NoError(db.Put(newKeyVal, []byte{1}))
   246  
   247  	// create a range endpoint that is before the newly added key, but after the last key
   248  	endPointBeforeNewKey := make([]byte, 0, 2)
   249  	for i := 0; i < len(newKeyVal); i++ {
   250  		endPointBeforeNewKey = append(endPointBeforeNewKey, newKeyVal[i])
   251  
   252  		// we need the new key to be after the last key
   253  		// don't subtract anything from the current byte if newkey and lastkey are equal
   254  		if lastKey[i] == newKeyVal[i] {
   255  			continue
   256  		}
   257  
   258  		// if the first nibble is > 0, subtract "1" from it
   259  		if endPointBeforeNewKey[i] >= 16 {
   260  			endPointBeforeNewKey[i] -= 16
   261  			break
   262  		}
   263  		// if the second nibble > 0, subtract 1 from it
   264  		if endPointBeforeNewKey[i] > 0 {
   265  			endPointBeforeNewKey[i] -= 1
   266  			break
   267  		}
   268  		// both nibbles were 0, so move onto the next byte
   269  	}
   270  
   271  	nextKey, err = syncer.findNextKey(context.Background(), lastKey, maybe.Some(endPointBeforeNewKey), proof.EndProof)
   272  	require.NoError(err)
   273  
   274  	// next key would be after the end of the range, so it returns Nothing instead
   275  	require.True(nextKey.IsNothing())
   276  }
   277  
   278  func Test_Sync_FindNextKey_Deleted(t *testing.T) {
   279  	require := require.New(t)
   280  	ctrl := gomock.NewController(t)
   281  	defer ctrl.Finish()
   282  
   283  	db, err := merkledb.New(
   284  		context.Background(),
   285  		memdb.New(),
   286  		newDefaultDBConfig(),
   287  	)
   288  	require.NoError(err)
   289  	require.NoError(db.Put([]byte{0x10}, []byte{1}))
   290  	require.NoError(db.Put([]byte{0x11, 0x11}, []byte{2}))
   291  
   292  	syncRoot, err := db.GetMerkleRoot(context.Background())
   293  	require.NoError(err)
   294  
   295  	syncer, err := NewManager(ManagerConfig{
   296  		DB:                    db,
   297  		Client:                NewMockClient(ctrl),
   298  		TargetRoot:            syncRoot,
   299  		SimultaneousWorkLimit: 5,
   300  		Log:                   logging.NoLog{},
   301  		BranchFactor:          merkledb.BranchFactor16,
   302  	})
   303  	require.NoError(err)
   304  
   305  	// 0x12 was "deleted" and there should be no extra node in the proof since there was nothing with a common prefix
   306  	noExtraNodeProof, err := db.GetProof(context.Background(), []byte{0x12})
   307  	require.NoError(err)
   308  
   309  	// 0x11 was "deleted" and 0x11.0x11 should be in the exclusion proof
   310  	extraNodeProof, err := db.GetProof(context.Background(), []byte{0x11})
   311  	require.NoError(err)
   312  
   313  	// there is now another value in the range that needs to be sync'ed
   314  	require.NoError(db.Put([]byte{0x13}, []byte{3}))
   315  
   316  	nextKey, err := syncer.findNextKey(context.Background(), []byte{0x12}, maybe.Some([]byte{0x20}), noExtraNodeProof.Path)
   317  	require.NoError(err)
   318  	require.Equal(maybe.Some([]byte{0x13}), nextKey)
   319  
   320  	nextKey, err = syncer.findNextKey(context.Background(), []byte{0x11}, maybe.Some([]byte{0x20}), extraNodeProof.Path)
   321  	require.NoError(err)
   322  	require.Equal(maybe.Some([]byte{0x13}), nextKey)
   323  }
   324  
   325  func Test_Sync_FindNextKey_BranchInLocal(t *testing.T) {
   326  	require := require.New(t)
   327  	ctrl := gomock.NewController(t)
   328  
   329  	db, err := merkledb.New(
   330  		context.Background(),
   331  		memdb.New(),
   332  		newDefaultDBConfig(),
   333  	)
   334  	require.NoError(err)
   335  	require.NoError(db.Put([]byte{0x11}, []byte{1}))
   336  	require.NoError(db.Put([]byte{0x11, 0x11}, []byte{2}))
   337  
   338  	targetRoot, err := db.GetMerkleRoot(context.Background())
   339  	require.NoError(err)
   340  
   341  	proof, err := db.GetProof(context.Background(), []byte{0x11, 0x11})
   342  	require.NoError(err)
   343  
   344  	syncer, err := NewManager(ManagerConfig{
   345  		DB:                    db,
   346  		Client:                NewMockClient(ctrl),
   347  		TargetRoot:            targetRoot,
   348  		SimultaneousWorkLimit: 5,
   349  		Log:                   logging.NoLog{},
   350  		BranchFactor:          merkledb.BranchFactor16,
   351  	})
   352  	require.NoError(err)
   353  	require.NoError(db.Put([]byte{0x11, 0x15}, []byte{4}))
   354  
   355  	nextKey, err := syncer.findNextKey(context.Background(), []byte{0x11, 0x11}, maybe.Some([]byte{0x20}), proof.Path)
   356  	require.NoError(err)
   357  	require.Equal(maybe.Some([]byte{0x11, 0x15}), nextKey)
   358  }
   359  
   360  func Test_Sync_FindNextKey_BranchInReceived(t *testing.T) {
   361  	require := require.New(t)
   362  	ctrl := gomock.NewController(t)
   363  
   364  	db, err := merkledb.New(
   365  		context.Background(),
   366  		memdb.New(),
   367  		newDefaultDBConfig(),
   368  	)
   369  	require.NoError(err)
   370  	require.NoError(db.Put([]byte{0x11}, []byte{1}))
   371  	require.NoError(db.Put([]byte{0x12}, []byte{2}))
   372  	require.NoError(db.Put([]byte{0x12, 0xA0}, []byte{4}))
   373  
   374  	targetRoot, err := db.GetMerkleRoot(context.Background())
   375  	require.NoError(err)
   376  
   377  	proof, err := db.GetProof(context.Background(), []byte{0x12})
   378  	require.NoError(err)
   379  
   380  	syncer, err := NewManager(ManagerConfig{
   381  		DB:                    db,
   382  		Client:                NewMockClient(ctrl),
   383  		TargetRoot:            targetRoot,
   384  		SimultaneousWorkLimit: 5,
   385  		Log:                   logging.NoLog{},
   386  		BranchFactor:          merkledb.BranchFactor16,
   387  	})
   388  	require.NoError(err)
   389  	require.NoError(db.Delete([]byte{0x12, 0xA0}))
   390  
   391  	nextKey, err := syncer.findNextKey(context.Background(), []byte{0x12}, maybe.Some([]byte{0x20}), proof.Path)
   392  	require.NoError(err)
   393  	require.Equal(maybe.Some([]byte{0x12, 0xA0}), nextKey)
   394  }
   395  
   396  func Test_Sync_FindNextKey_ExtraValues(t *testing.T) {
   397  	require := require.New(t)
   398  	ctrl := gomock.NewController(t)
   399  	defer ctrl.Finish()
   400  
   401  	now := time.Now().UnixNano()
   402  	t.Logf("seed: %d", now)
   403  	r := rand.New(rand.NewSource(now)) // #nosec G404
   404  	dbToSync, err := generateTrie(t, r, 1000)
   405  	require.NoError(err)
   406  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   407  	require.NoError(err)
   408  
   409  	db, err := merkledb.New(
   410  		context.Background(),
   411  		memdb.New(),
   412  		newDefaultDBConfig(),
   413  	)
   414  	require.NoError(err)
   415  	syncer, err := NewManager(ManagerConfig{
   416  		DB:                    db,
   417  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   418  		TargetRoot:            syncRoot,
   419  		SimultaneousWorkLimit: 5,
   420  		Log:                   logging.NoLog{},
   421  		BranchFactor:          merkledb.BranchFactor16,
   422  	})
   423  	require.NoError(err)
   424  	require.NotNil(syncer)
   425  
   426  	require.NoError(syncer.Start(context.Background()))
   427  	require.NoError(syncer.Wait(context.Background()))
   428  
   429  	proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 500)
   430  	require.NoError(err)
   431  
   432  	// add an extra value to local db
   433  	lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key
   434  	midpoint := midPoint(maybe.Some(lastKey), maybe.Nothing[[]byte]())
   435  	midPointVal := midpoint.Value()
   436  
   437  	require.NoError(db.Put(midPointVal, []byte{1}))
   438  
   439  	// next key at prefix of newly added point
   440  	nextKey, err := syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof)
   441  	require.NoError(err)
   442  	require.True(nextKey.HasValue())
   443  
   444  	require.True(isPrefix(midPointVal, nextKey.Value()))
   445  
   446  	require.NoError(db.Delete(midPointVal))
   447  
   448  	require.NoError(dbToSync.Put(midPointVal, []byte{1}))
   449  
   450  	proof, err = dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some(lastKey), 500)
   451  	require.NoError(err)
   452  
   453  	// next key at prefix of newly added point
   454  	nextKey, err = syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof)
   455  	require.NoError(err)
   456  	require.True(nextKey.HasValue())
   457  
   458  	// deal with odd length key
   459  	require.True(isPrefix(midPointVal, nextKey.Value()))
   460  }
   461  
   462  func TestFindNextKeyEmptyEndProof(t *testing.T) {
   463  	require := require.New(t)
   464  	now := time.Now().UnixNano()
   465  	t.Logf("seed: %d", now)
   466  	r := rand.New(rand.NewSource(now)) // #nosec G404
   467  	ctrl := gomock.NewController(t)
   468  	defer ctrl.Finish()
   469  
   470  	db, err := merkledb.New(
   471  		context.Background(),
   472  		memdb.New(),
   473  		newDefaultDBConfig(),
   474  	)
   475  	require.NoError(err)
   476  
   477  	syncer, err := NewManager(ManagerConfig{
   478  		DB:                    db,
   479  		Client:                NewMockClient(ctrl),
   480  		TargetRoot:            ids.Empty,
   481  		SimultaneousWorkLimit: 5,
   482  		Log:                   logging.NoLog{},
   483  		BranchFactor:          merkledb.BranchFactor16,
   484  	})
   485  	require.NoError(err)
   486  	require.NotNil(syncer)
   487  
   488  	for i := 0; i < 100; i++ {
   489  		lastReceivedKeyLen := r.Intn(16)
   490  		lastReceivedKey := make([]byte, lastReceivedKeyLen)
   491  		_, _ = r.Read(lastReceivedKey) // #nosec G404
   492  
   493  		rangeEndLen := r.Intn(16)
   494  		rangeEndBytes := make([]byte, rangeEndLen)
   495  		_, _ = r.Read(rangeEndBytes) // #nosec G404
   496  
   497  		rangeEnd := maybe.Nothing[[]byte]()
   498  		if rangeEndLen > 0 {
   499  			rangeEnd = maybe.Some(rangeEndBytes)
   500  		}
   501  
   502  		nextKey, err := syncer.findNextKey(
   503  			context.Background(),
   504  			lastReceivedKey,
   505  			rangeEnd,
   506  			nil, /* endProof */
   507  		)
   508  		require.NoError(err)
   509  		require.Equal(maybe.Some(append(lastReceivedKey, 0)), nextKey)
   510  	}
   511  }
   512  
   513  func isPrefix(data []byte, prefix []byte) bool {
   514  	if prefix[len(prefix)-1]%16 == 0 {
   515  		index := 0
   516  		for ; index < len(prefix)-1; index++ {
   517  			if data[index] != prefix[index] {
   518  				return false
   519  			}
   520  		}
   521  		return data[index]>>4 == prefix[index]>>4
   522  	}
   523  	return bytes.HasPrefix(data, prefix)
   524  }
   525  
   526  func Test_Sync_FindNextKey_DifferentChild(t *testing.T) {
   527  	require := require.New(t)
   528  	ctrl := gomock.NewController(t)
   529  	defer ctrl.Finish()
   530  
   531  	now := time.Now().UnixNano()
   532  	t.Logf("seed: %d", now)
   533  	r := rand.New(rand.NewSource(now)) // #nosec G404
   534  	dbToSync, err := generateTrie(t, r, 500)
   535  	require.NoError(err)
   536  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   537  	require.NoError(err)
   538  
   539  	db, err := merkledb.New(
   540  		context.Background(),
   541  		memdb.New(),
   542  		newDefaultDBConfig(),
   543  	)
   544  	require.NoError(err)
   545  	syncer, err := NewManager(ManagerConfig{
   546  		DB:                    db,
   547  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   548  		TargetRoot:            syncRoot,
   549  		SimultaneousWorkLimit: 5,
   550  		Log:                   logging.NoLog{},
   551  		BranchFactor:          merkledb.BranchFactor16,
   552  	})
   553  	require.NoError(err)
   554  	require.NotNil(syncer)
   555  	require.NoError(syncer.Start(context.Background()))
   556  	require.NoError(syncer.Wait(context.Background()))
   557  
   558  	proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100)
   559  	require.NoError(err)
   560  	lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key
   561  
   562  	// local db has a different child than remote db
   563  	lastKey = append(lastKey, 16)
   564  	require.NoError(db.Put(lastKey, []byte{1}))
   565  
   566  	require.NoError(dbToSync.Put(lastKey, []byte{2}))
   567  
   568  	proof, err = dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some(proof.KeyValues[len(proof.KeyValues)-1].Key), 100)
   569  	require.NoError(err)
   570  
   571  	nextKey, err := syncer.findNextKey(context.Background(), proof.KeyValues[len(proof.KeyValues)-1].Key, maybe.Nothing[[]byte](), proof.EndProof)
   572  	require.NoError(err)
   573  	require.True(nextKey.HasValue())
   574  	require.Equal(lastKey, nextKey.Value())
   575  }
   576  
   577  // Test findNextKey by computing the expected result in a naive, inefficient
   578  // way and comparing it to the actual result
   579  func TestFindNextKeyRandom(t *testing.T) {
   580  	now := time.Now().UnixNano()
   581  	t.Logf("seed: %d", now)
   582  	rand := rand.New(rand.NewSource(now)) // #nosec G404
   583  	require := require.New(t)
   584  	ctrl := gomock.NewController(t)
   585  	defer ctrl.Finish()
   586  
   587  	// Create a "remote" database and "local" database
   588  	remoteDB, err := merkledb.New(
   589  		context.Background(),
   590  		memdb.New(),
   591  		newDefaultDBConfig(),
   592  	)
   593  	require.NoError(err)
   594  
   595  	config := newDefaultDBConfig()
   596  	localDB, err := merkledb.New(
   597  		context.Background(),
   598  		memdb.New(),
   599  		config,
   600  	)
   601  	require.NoError(err)
   602  
   603  	var (
   604  		numProofsToTest  = 250
   605  		numKeyValues     = 250
   606  		maxKeyLen        = 256
   607  		maxValLen        = 256
   608  		maxRangeStartLen = 8
   609  		maxRangeEndLen   = 8
   610  		maxProofLen      = 128
   611  	)
   612  
   613  	// Put random keys into the databases
   614  	for _, db := range []database.Database{remoteDB, localDB} {
   615  		for i := 0; i < numKeyValues; i++ {
   616  			key := make([]byte, rand.Intn(maxKeyLen))
   617  			_, _ = rand.Read(key)
   618  			val := make([]byte, rand.Intn(maxValLen))
   619  			_, _ = rand.Read(val)
   620  			require.NoError(db.Put(key, val))
   621  		}
   622  	}
   623  
   624  	// Repeatedly generate end proofs from the remote database and compare
   625  	// the result of findNextKey to the expected result.
   626  	for proofIndex := 0; proofIndex < numProofsToTest; proofIndex++ {
   627  		// Generate a proof for a random key
   628  		var (
   629  			rangeStart []byte
   630  			rangeEnd   []byte
   631  		)
   632  		// Generate a valid range start and end
   633  		for rangeStart == nil || bytes.Compare(rangeStart, rangeEnd) == 1 {
   634  			rangeStart = make([]byte, rand.Intn(maxRangeStartLen)+1)
   635  			_, _ = rand.Read(rangeStart)
   636  			rangeEnd = make([]byte, rand.Intn(maxRangeEndLen)+1)
   637  			_, _ = rand.Read(rangeEnd)
   638  		}
   639  
   640  		startKey := maybe.Nothing[[]byte]()
   641  		if len(rangeStart) > 0 {
   642  			startKey = maybe.Some(rangeStart)
   643  		}
   644  		endKey := maybe.Nothing[[]byte]()
   645  		if len(rangeEnd) > 0 {
   646  			endKey = maybe.Some(rangeEnd)
   647  		}
   648  
   649  		remoteProof, err := remoteDB.GetRangeProof(
   650  			context.Background(),
   651  			startKey,
   652  			endKey,
   653  			rand.Intn(maxProofLen)+1,
   654  		)
   655  		require.NoError(err)
   656  
   657  		if len(remoteProof.KeyValues) == 0 {
   658  			continue
   659  		}
   660  		lastReceivedKey := remoteProof.KeyValues[len(remoteProof.KeyValues)-1].Key
   661  
   662  		// Commit the proof to the local database as we do
   663  		// in the actual syncer.
   664  		require.NoError(localDB.CommitRangeProof(
   665  			context.Background(),
   666  			startKey,
   667  			endKey,
   668  			remoteProof,
   669  		))
   670  
   671  		localProof, err := localDB.GetProof(
   672  			context.Background(),
   673  			lastReceivedKey,
   674  		)
   675  		require.NoError(err)
   676  
   677  		type keyAndID struct {
   678  			key merkledb.Key
   679  			id  ids.ID
   680  		}
   681  
   682  		// Set of key prefix/ID pairs proven by the remote database's end proof.
   683  		remoteKeyIDs := []keyAndID{}
   684  		for _, node := range remoteProof.EndProof {
   685  			for childIdx, childID := range node.Children {
   686  				remoteKeyIDs = append(remoteKeyIDs, keyAndID{
   687  					key: node.Key.Extend(merkledb.ToToken(childIdx, merkledb.BranchFactorToTokenSize[config.BranchFactor])),
   688  					id:  childID,
   689  				})
   690  			}
   691  		}
   692  
   693  		// Set of key prefix/ID pairs proven by the local database's proof.
   694  		localKeyIDs := []keyAndID{}
   695  		for _, node := range localProof.Path {
   696  			for childIdx, childID := range node.Children {
   697  				localKeyIDs = append(localKeyIDs, keyAndID{
   698  					key: node.Key.Extend(merkledb.ToToken(childIdx, merkledb.BranchFactorToTokenSize[config.BranchFactor])),
   699  					id:  childID,
   700  				})
   701  			}
   702  		}
   703  
   704  		// Sort in ascending order by key prefix.
   705  		serializedPathCompare := func(i, j keyAndID) int {
   706  			return i.key.Compare(j.key)
   707  		}
   708  		slices.SortFunc(remoteKeyIDs, serializedPathCompare)
   709  		slices.SortFunc(localKeyIDs, serializedPathCompare)
   710  
   711  		// Filter out keys that are before the last received key
   712  		findBounds := func(keyIDs []keyAndID) (int, int) {
   713  			var (
   714  				firstIdxInRange      = len(keyIDs)
   715  				firstIdxInRangeFound = false
   716  				firstIdxOutOfRange   = len(keyIDs)
   717  			)
   718  			for i, keyID := range keyIDs {
   719  				if !firstIdxInRangeFound && bytes.Compare(keyID.key.Bytes(), lastReceivedKey) > 0 {
   720  					firstIdxInRange = i
   721  					firstIdxInRangeFound = true
   722  					continue
   723  				}
   724  				if bytes.Compare(keyID.key.Bytes(), rangeEnd) > 0 {
   725  					firstIdxOutOfRange = i
   726  					break
   727  				}
   728  			}
   729  			return firstIdxInRange, firstIdxOutOfRange
   730  		}
   731  
   732  		remoteFirstIdxAfterLastReceived, remoteFirstIdxAfterEnd := findBounds(remoteKeyIDs)
   733  		remoteKeyIDs = remoteKeyIDs[remoteFirstIdxAfterLastReceived:remoteFirstIdxAfterEnd]
   734  
   735  		localFirstIdxAfterLastReceived, localFirstIdxAfterEnd := findBounds(localKeyIDs)
   736  		localKeyIDs = localKeyIDs[localFirstIdxAfterLastReceived:localFirstIdxAfterEnd]
   737  
   738  		// Find smallest difference between the set of key/ID pairs proven by
   739  		// the remote/local proofs for key/ID pairs after the last received key.
   740  		var (
   741  			smallestDiffKey merkledb.Key
   742  			foundDiff       bool
   743  		)
   744  		for i := 0; i < len(remoteKeyIDs) && i < len(localKeyIDs); i++ {
   745  			// See if the keys are different.
   746  			smaller, bigger := remoteKeyIDs[i], localKeyIDs[i]
   747  			if serializedPathCompare(localKeyIDs[i], remoteKeyIDs[i]) == -1 {
   748  				smaller, bigger = localKeyIDs[i], remoteKeyIDs[i]
   749  			}
   750  
   751  			if smaller.key != bigger.key || smaller.id != bigger.id {
   752  				smallestDiffKey = smaller.key
   753  				foundDiff = true
   754  				break
   755  			}
   756  		}
   757  		if !foundDiff {
   758  			// All the keys were equal. The smallest diff is the next key
   759  			// in the longer of the lists (if they're not same length.)
   760  			if len(remoteKeyIDs) < len(localKeyIDs) {
   761  				smallestDiffKey = localKeyIDs[len(remoteKeyIDs)].key
   762  			} else if len(remoteKeyIDs) > len(localKeyIDs) {
   763  				smallestDiffKey = remoteKeyIDs[len(localKeyIDs)].key
   764  			}
   765  		}
   766  
   767  		// Get the actual value from the syncer
   768  		syncer, err := NewManager(ManagerConfig{
   769  			DB:                    localDB,
   770  			Client:                NewMockClient(ctrl),
   771  			TargetRoot:            ids.GenerateTestID(),
   772  			SimultaneousWorkLimit: 5,
   773  			Log:                   logging.NoLog{},
   774  			BranchFactor:          merkledb.BranchFactor16,
   775  		})
   776  		require.NoError(err)
   777  
   778  		gotFirstDiff, err := syncer.findNextKey(
   779  			context.Background(),
   780  			lastReceivedKey,
   781  			endKey,
   782  			remoteProof.EndProof,
   783  		)
   784  		require.NoError(err)
   785  
   786  		if bytes.Compare(smallestDiffKey.Bytes(), rangeEnd) >= 0 {
   787  			// The smallest key which differs is after the range end so the
   788  			// next key to get should be nil because we're done fetching the range.
   789  			require.True(gotFirstDiff.IsNothing())
   790  		} else {
   791  			require.Equal(smallestDiffKey.Bytes(), gotFirstDiff.Value())
   792  		}
   793  	}
   794  }
   795  
   796  func Test_Sync_Result_Correct_Root(t *testing.T) {
   797  	require := require.New(t)
   798  	ctrl := gomock.NewController(t)
   799  	defer ctrl.Finish()
   800  
   801  	now := time.Now().UnixNano()
   802  	t.Logf("seed: %d", now)
   803  	r := rand.New(rand.NewSource(now)) // #nosec G404
   804  	dbToSync, err := generateTrie(t, r, 1000)
   805  	require.NoError(err)
   806  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   807  	require.NoError(err)
   808  
   809  	db, err := merkledb.New(
   810  		context.Background(),
   811  		memdb.New(),
   812  		newDefaultDBConfig(),
   813  	)
   814  	require.NoError(err)
   815  	syncer, err := NewManager(ManagerConfig{
   816  		DB:                    db,
   817  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   818  		TargetRoot:            syncRoot,
   819  		SimultaneousWorkLimit: 5,
   820  		Log:                   logging.NoLog{},
   821  		BranchFactor:          merkledb.BranchFactor16,
   822  	})
   823  	require.NoError(err)
   824  	require.NotNil(syncer)
   825  	require.NoError(syncer.Start(context.Background()))
   826  
   827  	require.NoError(syncer.Wait(context.Background()))
   828  	require.NoError(syncer.Error())
   829  
   830  	// new db has fully sync'ed and should be at the same root as the original db
   831  	newRoot, err := db.GetMerkleRoot(context.Background())
   832  	require.NoError(err)
   833  	require.Equal(syncRoot, newRoot)
   834  
   835  	// make sure they stay in sync
   836  	addkey := make([]byte, r.Intn(50))
   837  	_, err = r.Read(addkey)
   838  	require.NoError(err)
   839  	val := make([]byte, r.Intn(50))
   840  	_, err = r.Read(val)
   841  	require.NoError(err)
   842  
   843  	require.NoError(db.Put(addkey, val))
   844  
   845  	require.NoError(dbToSync.Put(addkey, val))
   846  
   847  	syncRoot, err = dbToSync.GetMerkleRoot(context.Background())
   848  	require.NoError(err)
   849  
   850  	newRoot, err = db.GetMerkleRoot(context.Background())
   851  	require.NoError(err)
   852  	require.Equal(syncRoot, newRoot)
   853  }
   854  
   855  func Test_Sync_Result_Correct_Root_With_Sync_Restart(t *testing.T) {
   856  	require := require.New(t)
   857  	ctrl := gomock.NewController(t)
   858  
   859  	now := time.Now().UnixNano()
   860  	t.Logf("seed: %d", now)
   861  	r := rand.New(rand.NewSource(now)) // #nosec G404
   862  	dbToSync, err := generateTrie(t, r, 3*maxKeyValuesLimit)
   863  	require.NoError(err)
   864  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   865  	require.NoError(err)
   866  
   867  	db, err := merkledb.New(
   868  		context.Background(),
   869  		memdb.New(),
   870  		newDefaultDBConfig(),
   871  	)
   872  	require.NoError(err)
   873  
   874  	syncer, err := NewManager(ManagerConfig{
   875  		DB:                    db,
   876  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   877  		TargetRoot:            syncRoot,
   878  		SimultaneousWorkLimit: 5,
   879  		Log:                   logging.NoLog{},
   880  		BranchFactor:          merkledb.BranchFactor16,
   881  	})
   882  	require.NoError(err)
   883  	require.NotNil(syncer)
   884  	require.NoError(syncer.Start(context.Background()))
   885  
   886  	// Wait until we've processed some work
   887  	// before updating the sync target.
   888  	require.Eventually(
   889  		func() bool {
   890  			syncer.workLock.Lock()
   891  			defer syncer.workLock.Unlock()
   892  
   893  			return syncer.processedWork.Len() > 0
   894  		},
   895  		5*time.Second,
   896  		5*time.Millisecond,
   897  	)
   898  	syncer.Close()
   899  
   900  	newSyncer, err := NewManager(ManagerConfig{
   901  		DB:                    db,
   902  		Client:                newCallthroughSyncClient(ctrl, dbToSync),
   903  		TargetRoot:            syncRoot,
   904  		SimultaneousWorkLimit: 5,
   905  		Log:                   logging.NoLog{},
   906  		BranchFactor:          merkledb.BranchFactor16,
   907  	})
   908  	require.NoError(err)
   909  	require.NotNil(newSyncer)
   910  
   911  	require.NoError(newSyncer.Start(context.Background()))
   912  	require.NoError(newSyncer.Error())
   913  	require.NoError(newSyncer.Wait(context.Background()))
   914  
   915  	newRoot, err := db.GetMerkleRoot(context.Background())
   916  	require.NoError(err)
   917  	require.Equal(syncRoot, newRoot)
   918  }
   919  
   920  func Test_Sync_Error_During_Sync(t *testing.T) {
   921  	require := require.New(t)
   922  	ctrl := gomock.NewController(t)
   923  	now := time.Now().UnixNano()
   924  	t.Logf("seed: %d", now)
   925  	r := rand.New(rand.NewSource(now)) // #nosec G404
   926  
   927  	dbToSync, err := generateTrie(t, r, 100)
   928  	require.NoError(err)
   929  
   930  	syncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   931  	require.NoError(err)
   932  
   933  	db, err := merkledb.New(
   934  		context.Background(),
   935  		memdb.New(),
   936  		newDefaultDBConfig(),
   937  	)
   938  	require.NoError(err)
   939  
   940  	client := NewMockClient(ctrl)
   941  	client.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn(
   942  		func(context.Context, *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) {
   943  			return nil, errInvalidRangeProof
   944  		},
   945  	).AnyTimes()
   946  	client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
   947  		func(ctx context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) {
   948  			startRoot, err := ids.ToID(request.StartRootHash)
   949  			require.NoError(err)
   950  
   951  			endRoot, err := ids.ToID(request.EndRootHash)
   952  			require.NoError(err)
   953  
   954  			changeProof, err := dbToSync.GetChangeProof(ctx, startRoot, endRoot, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit))
   955  			if err != nil {
   956  				return nil, err
   957  			}
   958  
   959  			return &merkledb.ChangeOrRangeProof{
   960  				ChangeProof: changeProof,
   961  			}, nil
   962  		},
   963  	).AnyTimes()
   964  
   965  	syncer, err := NewManager(ManagerConfig{
   966  		DB:                    db,
   967  		Client:                client,
   968  		TargetRoot:            syncRoot,
   969  		SimultaneousWorkLimit: 5,
   970  		Log:                   logging.NoLog{},
   971  		BranchFactor:          merkledb.BranchFactor16,
   972  	})
   973  	require.NoError(err)
   974  	require.NotNil(syncer)
   975  
   976  	require.NoError(syncer.Start(context.Background()))
   977  
   978  	err = syncer.Wait(context.Background())
   979  	require.ErrorIs(err, errInvalidRangeProof)
   980  }
   981  
   982  func Test_Sync_Result_Correct_Root_Update_Root_During(t *testing.T) {
   983  	require := require.New(t)
   984  	ctrl := gomock.NewController(t)
   985  
   986  	now := time.Now().UnixNano()
   987  	t.Logf("seed: %d", now)
   988  	r := rand.New(rand.NewSource(now)) // #nosec G404
   989  
   990  	dbToSync, err := generateTrie(t, r, 3*maxKeyValuesLimit)
   991  	require.NoError(err)
   992  
   993  	firstSyncRoot, err := dbToSync.GetMerkleRoot(context.Background())
   994  	require.NoError(err)
   995  
   996  	for x := 0; x < 100; x++ {
   997  		key := make([]byte, r.Intn(50))
   998  		_, err = r.Read(key)
   999  		require.NoError(err)
  1000  
  1001  		val := make([]byte, r.Intn(50))
  1002  		_, err = r.Read(val)
  1003  		require.NoError(err)
  1004  
  1005  		require.NoError(dbToSync.Put(key, val))
  1006  
  1007  		deleteKeyStart := make([]byte, r.Intn(50))
  1008  		_, err = r.Read(deleteKeyStart)
  1009  		require.NoError(err)
  1010  
  1011  		it := dbToSync.NewIteratorWithStart(deleteKeyStart)
  1012  		if it.Next() {
  1013  			require.NoError(dbToSync.Delete(it.Key()))
  1014  		}
  1015  		require.NoError(it.Error())
  1016  		it.Release()
  1017  	}
  1018  
  1019  	secondSyncRoot, err := dbToSync.GetMerkleRoot(context.Background())
  1020  	require.NoError(err)
  1021  
  1022  	db, err := merkledb.New(
  1023  		context.Background(),
  1024  		memdb.New(),
  1025  		newDefaultDBConfig(),
  1026  	)
  1027  	require.NoError(err)
  1028  
  1029  	// Only let one response go through until we update the root.
  1030  	updatedRootChan := make(chan struct{}, 1)
  1031  	updatedRootChan <- struct{}{}
  1032  
  1033  	client := NewMockClient(ctrl)
  1034  	client.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn(
  1035  		func(ctx context.Context, request *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) {
  1036  			<-updatedRootChan
  1037  			root, err := ids.ToID(request.RootHash)
  1038  			require.NoError(err)
  1039  			return dbToSync.GetRangeProofAtRoot(ctx, root, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit))
  1040  		},
  1041  	).AnyTimes()
  1042  	client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
  1043  		func(ctx context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) {
  1044  			<-updatedRootChan
  1045  
  1046  			startRoot, err := ids.ToID(request.StartRootHash)
  1047  			require.NoError(err)
  1048  
  1049  			endRoot, err := ids.ToID(request.EndRootHash)
  1050  			require.NoError(err)
  1051  
  1052  			changeProof, err := dbToSync.GetChangeProof(ctx, startRoot, endRoot, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit))
  1053  			if err != nil {
  1054  				return nil, err
  1055  			}
  1056  
  1057  			return &merkledb.ChangeOrRangeProof{
  1058  				ChangeProof: changeProof,
  1059  			}, nil
  1060  		},
  1061  	).AnyTimes()
  1062  
  1063  	syncer, err := NewManager(ManagerConfig{
  1064  		DB:                    db,
  1065  		Client:                client,
  1066  		TargetRoot:            firstSyncRoot,
  1067  		SimultaneousWorkLimit: 5,
  1068  		Log:                   logging.NoLog{},
  1069  		BranchFactor:          merkledb.BranchFactor16,
  1070  	})
  1071  	require.NoError(err)
  1072  	require.NotNil(syncer)
  1073  
  1074  	require.NoError(syncer.Start(context.Background()))
  1075  
  1076  	// Wait until we've processed some work
  1077  	// before updating the sync target.
  1078  	require.Eventually(
  1079  		func() bool {
  1080  			syncer.workLock.Lock()
  1081  			defer syncer.workLock.Unlock()
  1082  
  1083  			return syncer.processedWork.Len() > 0
  1084  		},
  1085  		5*time.Second,
  1086  		10*time.Millisecond,
  1087  	)
  1088  	require.NoError(syncer.UpdateSyncTarget(secondSyncRoot))
  1089  	close(updatedRootChan)
  1090  
  1091  	require.NoError(syncer.Wait(context.Background()))
  1092  	require.NoError(syncer.Error())
  1093  
  1094  	newRoot, err := db.GetMerkleRoot(context.Background())
  1095  	require.NoError(err)
  1096  	require.Equal(secondSyncRoot, newRoot)
  1097  }
  1098  
  1099  func Test_Sync_UpdateSyncTarget(t *testing.T) {
  1100  	require := require.New(t)
  1101  	ctrl := gomock.NewController(t)
  1102  
  1103  	m, err := NewManager(ManagerConfig{
  1104  		DB:                    merkledb.NewMockMerkleDB(ctrl), // Not used
  1105  		Client:                NewMockClient(ctrl),            // Not used
  1106  		TargetRoot:            ids.Empty,
  1107  		SimultaneousWorkLimit: 5,
  1108  		Log:                   logging.NoLog{},
  1109  		BranchFactor:          merkledb.BranchFactor16,
  1110  	})
  1111  	require.NoError(err)
  1112  
  1113  	// Populate [m.processWork] to ensure that UpdateSyncTarget
  1114  	// moves the work to [m.unprocessedWork].
  1115  	item := &workItem{
  1116  		start:       maybe.Some([]byte{1}),
  1117  		end:         maybe.Some([]byte{2}),
  1118  		localRootID: ids.GenerateTestID(),
  1119  	}
  1120  	m.processedWork.Insert(item)
  1121  
  1122  	// Make sure that [m.unprocessedWorkCond] is signaled.
  1123  	gotSignalChan := make(chan struct{})
  1124  	// Don't UpdateSyncTarget until we're waiting for the signal.
  1125  	startedWaiting := make(chan struct{})
  1126  	go func() {
  1127  		m.workLock.Lock()
  1128  		defer m.workLock.Unlock()
  1129  
  1130  		close(startedWaiting)
  1131  		m.unprocessedWorkCond.Wait()
  1132  		close(gotSignalChan)
  1133  	}()
  1134  
  1135  	<-startedWaiting
  1136  	newSyncRoot := ids.GenerateTestID()
  1137  	require.NoError(m.UpdateSyncTarget(newSyncRoot))
  1138  	<-gotSignalChan
  1139  
  1140  	require.Equal(newSyncRoot, m.config.TargetRoot)
  1141  	require.Zero(m.processedWork.Len())
  1142  	require.Equal(1, m.unprocessedWork.Len())
  1143  }
  1144  
  1145  func generateTrie(t *testing.T, r *rand.Rand, count int) (merkledb.MerkleDB, error) {
  1146  	db, _, err := generateTrieWithMinKeyLen(t, r, count, 0)
  1147  	return db, err
  1148  }
  1149  
  1150  func generateTrieWithMinKeyLen(t *testing.T, r *rand.Rand, count int, minKeyLen int) (merkledb.MerkleDB, [][]byte, error) {
  1151  	require := require.New(t)
  1152  
  1153  	db, err := merkledb.New(
  1154  		context.Background(),
  1155  		memdb.New(),
  1156  		newDefaultDBConfig(),
  1157  	)
  1158  	if err != nil {
  1159  		return nil, nil, err
  1160  	}
  1161  	var (
  1162  		allKeys  [][]byte
  1163  		seenKeys = make(map[string]struct{})
  1164  		batch    = db.NewBatch()
  1165  	)
  1166  	genKey := func() []byte {
  1167  		// new prefixed key
  1168  		if len(allKeys) > 2 && r.Intn(25) < 10 {
  1169  			prefix := allKeys[r.Intn(len(allKeys))]
  1170  			key := make([]byte, r.Intn(50)+len(prefix))
  1171  			copy(key, prefix)
  1172  			_, err := r.Read(key[len(prefix):])
  1173  			require.NoError(err)
  1174  			return key
  1175  		}
  1176  
  1177  		// new key
  1178  		key := make([]byte, r.Intn(50)+minKeyLen)
  1179  		_, err = r.Read(key)
  1180  		require.NoError(err)
  1181  		return key
  1182  	}
  1183  
  1184  	for i := 0; i < count; {
  1185  		value := make([]byte, r.Intn(51))
  1186  		if len(value) == 0 {
  1187  			value = nil
  1188  		} else {
  1189  			_, err = r.Read(value)
  1190  			require.NoError(err)
  1191  		}
  1192  		key := genKey()
  1193  		if _, seen := seenKeys[string(key)]; seen {
  1194  			continue // avoid duplicate keys so we always get the count
  1195  		}
  1196  		allKeys = append(allKeys, key)
  1197  		seenKeys[string(key)] = struct{}{}
  1198  		if err = batch.Put(key, value); err != nil {
  1199  			return db, nil, err
  1200  		}
  1201  		i++
  1202  	}
  1203  	slices.SortFunc(allKeys, bytes.Compare)
  1204  	return db, allKeys, batch.Write()
  1205  }