github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/history_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 merkledb
     5  
     6  import (
     7  	"context"
     8  	"math/rand"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/MetalBlockchain/metalgo/database/memdb"
    15  	"github.com/MetalBlockchain/metalgo/ids"
    16  	"github.com/MetalBlockchain/metalgo/utils/maybe"
    17  )
    18  
    19  func Test_History_Simple(t *testing.T) {
    20  	require := require.New(t)
    21  
    22  	db, err := newDB(
    23  		context.Background(),
    24  		memdb.New(),
    25  		newDefaultConfig(),
    26  	)
    27  	require.NoError(err)
    28  	batch := db.NewBatch()
    29  	require.NoError(batch.Put([]byte("key"), []byte("value")))
    30  	require.NoError(batch.Write())
    31  
    32  	val, err := db.Get([]byte("key"))
    33  	require.NoError(err)
    34  	require.Equal([]byte("value"), val)
    35  
    36  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
    37  	require.NoError(err)
    38  	require.NotNil(origProof)
    39  	origRootID := db.rootID
    40  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
    41  
    42  	batch = db.NewBatch()
    43  	require.NoError(batch.Put([]byte("key"), []byte("value0")))
    44  	require.NoError(batch.Write())
    45  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
    46  	require.NoError(err)
    47  	require.NotNil(newProof)
    48  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
    49  
    50  	batch = db.NewBatch()
    51  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
    52  	require.NoError(batch.Put([]byte("key8"), []byte("value8")))
    53  	require.NoError(batch.Write())
    54  	newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
    55  	require.NoError(err)
    56  	require.NotNil(newProof)
    57  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
    58  
    59  	batch = db.NewBatch()
    60  	require.NoError(batch.Put([]byte("k"), []byte("v")))
    61  	require.NoError(batch.Write())
    62  	newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
    63  	require.NoError(err)
    64  	require.NotNil(newProof)
    65  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
    66  
    67  	batch = db.NewBatch()
    68  	require.NoError(batch.Delete([]byte("k")))
    69  	require.NoError(batch.Delete([]byte("ke")))
    70  	require.NoError(batch.Delete([]byte("key")))
    71  	require.NoError(batch.Delete([]byte("key1")))
    72  	require.NoError(batch.Put([]byte("key2"), []byte("value2")))
    73  	require.NoError(batch.Delete([]byte("key3")))
    74  	require.NoError(batch.Delete([]byte("key4")))
    75  	require.NoError(batch.Delete([]byte("key5")))
    76  	require.NoError(batch.Delete([]byte("key8")))
    77  	require.NoError(batch.Write())
    78  	newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
    79  	require.NoError(err)
    80  	require.NotNil(newProof)
    81  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
    82  }
    83  
    84  func Test_History_Large(t *testing.T) {
    85  	require := require.New(t)
    86  
    87  	numIters := 250
    88  
    89  	for i := 1; i < 5; i++ {
    90  		config := newDefaultConfig()
    91  		// History must be large enough to get the change proof
    92  		// after this loop.
    93  		config.HistoryLength = uint(numIters)
    94  		db, err := New(
    95  			context.Background(),
    96  			memdb.New(),
    97  			config,
    98  		)
    99  		require.NoError(err)
   100  		roots := []ids.ID{}
   101  
   102  		now := time.Now().UnixNano()
   103  		t.Logf("seed for iter %d: %d", i, now)
   104  		r := rand.New(rand.NewSource(now)) // #nosec G404
   105  		// make sure they stay in sync
   106  		for x := 0; x < numIters; x++ {
   107  			batch := db.NewBatch()
   108  			addkey := make([]byte, r.Intn(50))
   109  			_, err := r.Read(addkey)
   110  			require.NoError(err)
   111  			val := make([]byte, r.Intn(50))
   112  			_, err = r.Read(val)
   113  			require.NoError(err)
   114  
   115  			require.NoError(batch.Put(addkey, val))
   116  
   117  			addNilkey := make([]byte, r.Intn(50))
   118  			_, err = r.Read(addNilkey)
   119  			require.NoError(err)
   120  			require.NoError(batch.Put(addNilkey, nil))
   121  
   122  			deleteKeyStart := make([]byte, r.Intn(50))
   123  			_, err = r.Read(deleteKeyStart)
   124  			require.NoError(err)
   125  
   126  			it := db.NewIteratorWithStart(deleteKeyStart)
   127  			if it.Next() {
   128  				require.NoError(batch.Delete(it.Key()))
   129  			}
   130  			require.NoError(it.Error())
   131  			it.Release()
   132  
   133  			require.NoError(batch.Write())
   134  			root, err := db.GetMerkleRoot(context.Background())
   135  			require.NoError(err)
   136  			roots = append(roots, root)
   137  		}
   138  
   139  		for i := 0; i < numIters; i += numIters / 10 {
   140  			proof, err := db.GetRangeProofAtRoot(context.Background(), roots[i], maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10)
   141  			require.NoError(err)
   142  			require.NotNil(proof)
   143  
   144  			require.NoError(proof.Verify(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), roots[i], BranchFactorToTokenSize[config.BranchFactor], config.Hasher))
   145  		}
   146  	}
   147  }
   148  
   149  func Test_History_Bad_GetValueChanges_Input(t *testing.T) {
   150  	require := require.New(t)
   151  
   152  	config := newDefaultConfig()
   153  	config.HistoryLength = 5
   154  
   155  	db, err := newDB(
   156  		context.Background(),
   157  		memdb.New(),
   158  		config,
   159  	)
   160  	require.NoError(err)
   161  
   162  	// Do 5 puts (i.e. the history length)
   163  	batch := db.NewBatch()
   164  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   165  	require.NoError(batch.Write())
   166  
   167  	root1 := db.getMerkleRoot()
   168  
   169  	batch = db.NewBatch()
   170  	require.NoError(batch.Put([]byte("key"), []byte("value0")))
   171  	require.NoError(batch.Write())
   172  
   173  	root2 := db.getMerkleRoot()
   174  
   175  	batch = db.NewBatch()
   176  	require.NoError(batch.Put([]byte("key1"), []byte("value0")))
   177  	require.NoError(batch.Write())
   178  
   179  	batch = db.NewBatch()
   180  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
   181  	require.NoError(batch.Write())
   182  
   183  	batch = db.NewBatch()
   184  	require.NoError(batch.Put([]byte("key2"), []byte("value3")))
   185  	require.NoError(batch.Write())
   186  
   187  	root3 := db.getMerkleRoot()
   188  
   189  	// ensure these start as valid calls
   190  	_, err = db.history.getValueChanges(root1, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1)
   191  	require.NoError(err)
   192  	_, err = db.history.getValueChanges(root2, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1)
   193  	require.NoError(err)
   194  
   195  	_, err = db.history.getValueChanges(root2, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), -1)
   196  	require.ErrorIs(err, ErrInvalidMaxLength)
   197  
   198  	_, err = db.history.getValueChanges(root3, root2, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1)
   199  	require.ErrorIs(err, ErrInsufficientHistory)
   200  
   201  	// Cause root1 to be removed from the history
   202  	batch = db.NewBatch()
   203  	require.NoError(batch.Put([]byte("key2"), []byte("value4")))
   204  	require.NoError(batch.Write())
   205  
   206  	_, err = db.history.getValueChanges(root1, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 1)
   207  	require.ErrorIs(err, ErrInsufficientHistory)
   208  
   209  	// same start/end roots should yield an empty changelist
   210  	changes, err := db.history.getValueChanges(root3, root3, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10)
   211  	require.NoError(err)
   212  	require.Empty(changes.values)
   213  }
   214  
   215  func Test_History_Trigger_History_Queue_Looping(t *testing.T) {
   216  	require := require.New(t)
   217  
   218  	config := newDefaultConfig()
   219  	config.HistoryLength = 2
   220  
   221  	db, err := newDB(
   222  		context.Background(),
   223  		memdb.New(),
   224  		config,
   225  	)
   226  	require.NoError(err)
   227  
   228  	// Do 2 puts (i.e. the history length)
   229  	batch := db.NewBatch()
   230  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   231  	require.NoError(batch.Write())
   232  	origRootID := db.getMerkleRoot()
   233  
   234  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   235  	require.NoError(err)
   236  	require.NotNil(origProof)
   237  	require.NoError(origProof.Verify(
   238  		context.Background(),
   239  		maybe.Some([]byte("k")),
   240  		maybe.Some([]byte("key3")),
   241  		origRootID,
   242  		db.tokenSize,
   243  		db.hasher,
   244  	))
   245  
   246  	// write a new value into the db, now there should be 2 roots in the history
   247  	batch = db.NewBatch()
   248  	require.NoError(batch.Put([]byte("key"), []byte("value0")))
   249  	require.NoError(batch.Write())
   250  
   251  	// ensure that previous root is still present and generates a valid proof
   252  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   253  	require.NoError(err)
   254  	require.NotNil(newProof)
   255  	require.NoError(newProof.Verify(
   256  		context.Background(),
   257  		maybe.Some([]byte("k")),
   258  		maybe.Some([]byte("key3")),
   259  		origRootID,
   260  		db.tokenSize,
   261  		db.hasher,
   262  	))
   263  
   264  	// trigger a new root to be added to the history, which should cause rollover since there can only be 2
   265  	batch = db.NewBatch()
   266  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
   267  	require.NoError(batch.Write())
   268  
   269  	// proof from first root shouldn't be generatable since it should have been removed from the history
   270  	_, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   271  	require.ErrorIs(err, ErrInsufficientHistory)
   272  }
   273  
   274  func Test_History_Values_Lookup_Over_Queue_Break(t *testing.T) {
   275  	require := require.New(t)
   276  
   277  	config := newDefaultConfig()
   278  	config.HistoryLength = 4
   279  	db, err := newDB(
   280  		context.Background(),
   281  		memdb.New(),
   282  		config,
   283  	)
   284  	require.NoError(err)
   285  
   286  	// Do 4 puts (i.e. the history length)
   287  	batch := db.NewBatch()
   288  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   289  	require.NoError(batch.Write())
   290  
   291  	// write a new value into the db
   292  	batch = db.NewBatch()
   293  	require.NoError(batch.Put([]byte("key"), []byte("value0")))
   294  	require.NoError(batch.Write())
   295  
   296  	startRoot := db.getMerkleRoot()
   297  
   298  	// write a new value into the db
   299  	batch = db.NewBatch()
   300  	require.NoError(batch.Put([]byte("key1"), []byte("value0")))
   301  	require.NoError(batch.Write())
   302  
   303  	// write a new value into the db that overwrites key1
   304  	batch = db.NewBatch()
   305  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
   306  	require.NoError(batch.Write())
   307  
   308  	// trigger a new root to be added to the history, which should cause rollover since there can only be 3
   309  	batch = db.NewBatch()
   310  	require.NoError(batch.Put([]byte("key2"), []byte("value3")))
   311  	require.NoError(batch.Write())
   312  
   313  	endRoot := db.getMerkleRoot()
   314  
   315  	// changes should still be collectable even though the history has had to loop due to hitting max size
   316  	changes, err := db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 10)
   317  	require.NoError(err)
   318  	require.Contains(changes.values, ToKey([]byte("key1")))
   319  	require.Equal([]byte("value1"), changes.values[ToKey([]byte("key1"))].after.Value())
   320  	require.Contains(changes.values, ToKey([]byte("key2")))
   321  	require.Equal([]byte("value3"), changes.values[ToKey([]byte("key2"))].after.Value())
   322  }
   323  
   324  func Test_History_RepeatedRoot(t *testing.T) {
   325  	require := require.New(t)
   326  
   327  	db, err := newDB(
   328  		context.Background(),
   329  		memdb.New(),
   330  		newDefaultConfig(),
   331  	)
   332  	require.NoError(err)
   333  	batch := db.NewBatch()
   334  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
   335  	require.NoError(batch.Put([]byte("key2"), []byte("value2")))
   336  	require.NoError(batch.Put([]byte("key3"), []byte("value3")))
   337  	require.NoError(batch.Write())
   338  
   339  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   340  	require.NoError(err)
   341  	require.NotNil(origProof)
   342  	origRootID := db.rootID
   343  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   344  
   345  	batch = db.NewBatch()
   346  	require.NoError(batch.Put([]byte("key1"), []byte("other")))
   347  	require.NoError(batch.Put([]byte("key2"), []byte("other")))
   348  	require.NoError(batch.Put([]byte("key3"), []byte("other")))
   349  	require.NoError(batch.Write())
   350  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   351  	require.NoError(err)
   352  	require.NotNil(newProof)
   353  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   354  
   355  	// revert state to be the same as in orig proof
   356  	batch = db.NewBatch()
   357  	require.NoError(batch.Put([]byte("key1"), []byte("value1")))
   358  	require.NoError(batch.Put([]byte("key2"), []byte("value2")))
   359  	require.NoError(batch.Put([]byte("key3"), []byte("value3")))
   360  	require.NoError(batch.Write())
   361  
   362  	newProof, err = db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   363  	require.NoError(err)
   364  	require.NotNil(newProof)
   365  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   366  }
   367  
   368  func Test_History_ExcessDeletes(t *testing.T) {
   369  	require := require.New(t)
   370  
   371  	db, err := newDB(
   372  		context.Background(),
   373  		memdb.New(),
   374  		newDefaultConfig(),
   375  	)
   376  	require.NoError(err)
   377  	batch := db.NewBatch()
   378  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   379  	require.NoError(batch.Write())
   380  
   381  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   382  	require.NoError(err)
   383  	require.NotNil(origProof)
   384  	origRootID := db.rootID
   385  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   386  
   387  	batch = db.NewBatch()
   388  	require.NoError(batch.Delete([]byte("key1")))
   389  	require.NoError(batch.Delete([]byte("key2")))
   390  	require.NoError(batch.Delete([]byte("key3")))
   391  	require.NoError(batch.Delete([]byte("key4")))
   392  	require.NoError(batch.Delete([]byte("key5")))
   393  	require.NoError(batch.Write())
   394  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   395  	require.NoError(err)
   396  	require.NotNil(newProof)
   397  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   398  }
   399  
   400  func Test_History_DontIncludeAllNodes(t *testing.T) {
   401  	require := require.New(t)
   402  
   403  	db, err := newDB(
   404  		context.Background(),
   405  		memdb.New(),
   406  		newDefaultConfig(),
   407  	)
   408  	require.NoError(err)
   409  	batch := db.NewBatch()
   410  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   411  	require.NoError(batch.Write())
   412  
   413  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   414  	require.NoError(err)
   415  	require.NotNil(origProof)
   416  	origRootID := db.rootID
   417  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   418  
   419  	batch = db.NewBatch()
   420  	require.NoError(batch.Put([]byte("z"), []byte("z")))
   421  	require.NoError(batch.Write())
   422  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   423  	require.NoError(err)
   424  	require.NotNil(newProof)
   425  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   426  }
   427  
   428  func Test_History_Branching2Nodes(t *testing.T) {
   429  	require := require.New(t)
   430  
   431  	db, err := newDB(
   432  		context.Background(),
   433  		memdb.New(),
   434  		newDefaultConfig(),
   435  	)
   436  	require.NoError(err)
   437  	batch := db.NewBatch()
   438  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   439  	require.NoError(batch.Write())
   440  
   441  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   442  	require.NoError(err)
   443  	require.NotNil(origProof)
   444  	origRootID := db.rootID
   445  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   446  
   447  	batch = db.NewBatch()
   448  	require.NoError(batch.Put([]byte("k"), []byte("v")))
   449  	require.NoError(batch.Write())
   450  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   451  	require.NoError(err)
   452  	require.NotNil(newProof)
   453  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   454  }
   455  
   456  func Test_History_Branching3Nodes(t *testing.T) {
   457  	require := require.New(t)
   458  
   459  	db, err := newDB(
   460  		context.Background(),
   461  		memdb.New(),
   462  		newDefaultConfig(),
   463  	)
   464  	require.NoError(err)
   465  	batch := db.NewBatch()
   466  	require.NoError(batch.Put([]byte("key123"), []byte("value123")))
   467  	require.NoError(batch.Write())
   468  
   469  	origProof, err := db.GetRangeProof(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   470  	require.NoError(err)
   471  	require.NotNil(origProof)
   472  	origRootID := db.rootID
   473  	require.NoError(origProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   474  
   475  	batch = db.NewBatch()
   476  	require.NoError(batch.Put([]byte("key321"), []byte("value321")))
   477  	require.NoError(batch.Write())
   478  	newProof, err := db.GetRangeProofAtRoot(context.Background(), origRootID, maybe.Some([]byte("k")), maybe.Some([]byte("key3")), 10)
   479  	require.NoError(err)
   480  	require.NotNil(newProof)
   481  	require.NoError(newProof.Verify(context.Background(), maybe.Some([]byte("k")), maybe.Some([]byte("key3")), origRootID, db.tokenSize, db.hasher))
   482  }
   483  
   484  func Test_History_MaxLength(t *testing.T) {
   485  	require := require.New(t)
   486  
   487  	config := newDefaultConfig()
   488  	config.HistoryLength = 2
   489  	db, err := newDB(
   490  		context.Background(),
   491  		memdb.New(),
   492  		config,
   493  	)
   494  	require.NoError(err)
   495  
   496  	batch := db.NewBatch()
   497  	require.NoError(batch.Put([]byte("key"), []byte("value")))
   498  	require.NoError(batch.Write())
   499  
   500  	oldRoot, err := db.GetMerkleRoot(context.Background())
   501  	require.NoError(err)
   502  
   503  	batch = db.NewBatch()
   504  	require.NoError(batch.Put([]byte("k"), []byte("v")))
   505  	require.NoError(batch.Write())
   506  
   507  	require.Contains(db.history.lastChanges, oldRoot)
   508  
   509  	batch = db.NewBatch()
   510  	require.NoError(batch.Put([]byte("k1"), []byte("v2"))) // Overwrites oldest element in history
   511  	require.NoError(batch.Write())
   512  
   513  	require.NotContains(db.history.lastChanges, oldRoot)
   514  }
   515  
   516  func Test_Change_List(t *testing.T) {
   517  	require := require.New(t)
   518  
   519  	db, err := newDB(
   520  		context.Background(),
   521  		memdb.New(),
   522  		newDefaultConfig(),
   523  	)
   524  	require.NoError(err)
   525  
   526  	emptyRoot, err := db.GetMerkleRoot(context.Background())
   527  	require.NoError(err)
   528  
   529  	batch := db.NewBatch()
   530  	require.NoError(batch.Put([]byte("key20"), []byte("value20")))
   531  	require.NoError(batch.Put([]byte("key21"), []byte("value21")))
   532  	require.NoError(batch.Put([]byte("key22"), []byte("value22")))
   533  	require.NoError(batch.Put([]byte("key23"), []byte("value23")))
   534  	require.NoError(batch.Put([]byte("key24"), []byte("value24")))
   535  	require.NoError(batch.Write())
   536  	startRoot, err := db.GetMerkleRoot(context.Background())
   537  	require.NoError(err)
   538  
   539  	changes, err := db.history.getValueChanges(emptyRoot, startRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100)
   540  	require.NoError(err)
   541  	require.Len(changes.values, 5)
   542  
   543  	batch = db.NewBatch()
   544  	require.NoError(batch.Put([]byte("key25"), []byte("value25")))
   545  	require.NoError(batch.Put([]byte("key26"), []byte("value26")))
   546  	require.NoError(batch.Put([]byte("key27"), []byte("value27")))
   547  	require.NoError(batch.Put([]byte("key28"), []byte("value28")))
   548  	require.NoError(batch.Put([]byte("key29"), []byte("value29")))
   549  	require.NoError(batch.Write())
   550  
   551  	endRoot, err := db.GetMerkleRoot(context.Background())
   552  	require.NoError(err)
   553  
   554  	changes, err = db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100)
   555  	require.NoError(err)
   556  	require.Len(changes.values, 5)
   557  
   558  	batch = db.NewBatch()
   559  	require.NoError(batch.Put([]byte("key30"), []byte("value30")))
   560  	require.NoError(batch.Put([]byte("key31"), []byte("value31")))
   561  	require.NoError(batch.Put([]byte("key32"), []byte("value32")))
   562  	require.NoError(batch.Delete([]byte("key21")))
   563  	require.NoError(batch.Delete([]byte("key22")))
   564  	require.NoError(batch.Write())
   565  
   566  	endRoot, err = db.GetMerkleRoot(context.Background())
   567  	require.NoError(err)
   568  
   569  	changes, err = db.history.getValueChanges(startRoot, endRoot, maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 8)
   570  	require.NoError(err)
   571  	require.Len(changes.values, 8)
   572  }
   573  
   574  func TestHistoryRecord(t *testing.T) {
   575  	require := require.New(t)
   576  
   577  	maxHistoryLen := 3
   578  	th := newTrieHistory(maxHistoryLen)
   579  
   580  	changes := []*changeSummary{}
   581  	for i := 0; i < maxHistoryLen; i++ { // Fill the history
   582  		changes = append(changes, &changeSummary{rootID: ids.GenerateTestID()})
   583  
   584  		th.record(changes[i])
   585  		require.Equal(uint64(i+1), th.nextInsertNumber)
   586  		require.Equal(i+1, th.history.Len())
   587  		require.Len(th.lastChanges, i+1)
   588  		require.Contains(th.lastChanges, changes[i].rootID)
   589  		changeAndIndex := th.lastChanges[changes[i].rootID]
   590  		require.Equal(uint64(i), changeAndIndex.insertNumber)
   591  		got, ok := th.history.Index(int(changeAndIndex.insertNumber))
   592  		require.True(ok)
   593  		require.Equal(changes[i], got.changeSummary)
   594  	}
   595  	// history is [changes[0], changes[1], changes[2]]
   596  
   597  	// Add a new change
   598  	change3 := &changeSummary{rootID: ids.GenerateTestID()}
   599  	th.record(change3)
   600  	// history is [changes[1], changes[2], change3]
   601  	require.Equal(uint64(maxHistoryLen+1), th.nextInsertNumber)
   602  	require.Equal(maxHistoryLen, th.history.Len())
   603  	require.Len(th.lastChanges, maxHistoryLen)
   604  	require.Contains(th.lastChanges, change3.rootID)
   605  	changeAndIndex := th.lastChanges[change3.rootID]
   606  	require.Equal(uint64(maxHistoryLen), changeAndIndex.insertNumber)
   607  	got, ok := th.history.PeekRight()
   608  	require.True(ok)
   609  	require.Equal(change3, got.changeSummary)
   610  
   611  	// // Make sure the oldest change was evicted
   612  	require.NotContains(th.lastChanges, changes[0].rootID)
   613  	oldestChange, ok := th.history.PeekLeft()
   614  	require.True(ok)
   615  	require.Equal(uint64(1), oldestChange.insertNumber)
   616  
   617  	// Add another change which was the same root ID as changes[2]
   618  	change4 := &changeSummary{rootID: changes[2].rootID}
   619  	th.record(change4)
   620  	// history is [changes[2], change3, change4]
   621  
   622  	change5 := &changeSummary{rootID: ids.GenerateTestID()}
   623  	th.record(change5)
   624  	// history is [change3, change4, change5]
   625  
   626  	// Make sure that even though changes[2] was evicted, we still remember
   627  	// that the most recent change resulting in that change's root ID.
   628  	require.Len(th.lastChanges, maxHistoryLen)
   629  	require.Contains(th.lastChanges, changes[2].rootID)
   630  	changeAndIndex = th.lastChanges[changes[2].rootID]
   631  	require.Equal(uint64(maxHistoryLen+1), changeAndIndex.insertNumber)
   632  
   633  	// Make sure [t.history] is right.
   634  	require.Equal(maxHistoryLen, th.history.Len())
   635  	got, ok = th.history.PopLeft()
   636  	require.True(ok)
   637  	require.Equal(uint64(maxHistoryLen), got.insertNumber)
   638  	require.Equal(change3.rootID, got.rootID)
   639  	got, ok = th.history.PopLeft()
   640  	require.True(ok)
   641  	require.Equal(uint64(maxHistoryLen+1), got.insertNumber)
   642  	require.Equal(change4.rootID, got.rootID)
   643  	got, ok = th.history.PopLeft()
   644  	require.True(ok)
   645  	require.Equal(uint64(maxHistoryLen+2), got.insertNumber)
   646  	require.Equal(change5.rootID, got.rootID)
   647  }
   648  
   649  func TestHistoryGetChangesToRoot(t *testing.T) {
   650  	maxHistoryLen := 3
   651  	history := newTrieHistory(maxHistoryLen)
   652  
   653  	changes := []*changeSummary{}
   654  	for i := 0; i < maxHistoryLen; i++ { // Fill the history
   655  		changes = append(changes, &changeSummary{
   656  			rootID: ids.GenerateTestID(),
   657  			rootChange: change[maybe.Maybe[*node]]{
   658  				before: maybe.Some(&node{}),
   659  			},
   660  			nodes: map[Key]*change[*node]{
   661  				ToKey([]byte{byte(i)}): {
   662  					before: &node{},
   663  					after:  &node{},
   664  				},
   665  			},
   666  			values: map[Key]*change[maybe.Maybe[[]byte]]{
   667  				ToKey([]byte{byte(i)}): {
   668  					before: maybe.Some([]byte{byte(i)}),
   669  					after:  maybe.Some([]byte{byte(i + 1)}),
   670  				},
   671  			},
   672  		})
   673  		history.record(changes[i])
   674  	}
   675  
   676  	type test struct {
   677  		name         string
   678  		rootID       ids.ID
   679  		start        maybe.Maybe[[]byte]
   680  		end          maybe.Maybe[[]byte]
   681  		validateFunc func(*require.Assertions, *changeSummary)
   682  		expectedErr  error
   683  	}
   684  
   685  	tests := []test{
   686  		{
   687  			name:        "unknown root ID",
   688  			rootID:      ids.GenerateTestID(),
   689  			expectedErr: ErrInsufficientHistory,
   690  		},
   691  		{
   692  			name:   "most recent change",
   693  			rootID: changes[maxHistoryLen-1].rootID,
   694  			validateFunc: func(require *require.Assertions, got *changeSummary) {
   695  				expected := newChangeSummary(defaultPreallocationSize)
   696  				require.Equal(expected, got)
   697  			},
   698  		},
   699  		{
   700  			name:   "second most recent change",
   701  			rootID: changes[maxHistoryLen-2].rootID,
   702  			validateFunc: func(require *require.Assertions, got *changeSummary) {
   703  				// Ensure this is the reverse of the most recent change
   704  				require.Len(got.nodes, 1)
   705  				require.Len(got.values, 1)
   706  				reversedChanges := changes[maxHistoryLen-1]
   707  				removedKey := ToKey([]byte{byte(maxHistoryLen - 1)})
   708  				require.Equal(reversedChanges.nodes[removedKey].before, got.nodes[removedKey].after)
   709  				require.Equal(reversedChanges.values[removedKey].before, got.values[removedKey].after)
   710  				require.Equal(reversedChanges.values[removedKey].after, got.values[removedKey].before)
   711  			},
   712  		},
   713  		{
   714  			name:   "third most recent change",
   715  			rootID: changes[maxHistoryLen-3].rootID,
   716  			validateFunc: func(require *require.Assertions, got *changeSummary) {
   717  				require.Len(got.nodes, 2)
   718  				require.Len(got.values, 2)
   719  				reversedChanges1 := changes[maxHistoryLen-1]
   720  				removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)})
   721  				require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after)
   722  				require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after)
   723  				require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before)
   724  				reversedChanges2 := changes[maxHistoryLen-2]
   725  				removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)})
   726  				require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after)
   727  				require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after)
   728  				require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before)
   729  			},
   730  		},
   731  		{
   732  			name:   "third most recent change with start filter",
   733  			rootID: changes[maxHistoryLen-3].rootID,
   734  			start:  maybe.Some([]byte{byte(maxHistoryLen - 1)}), // Omit values from second most recent change
   735  			validateFunc: func(require *require.Assertions, got *changeSummary) {
   736  				require.Len(got.nodes, 2)
   737  				require.Len(got.values, 1)
   738  				reversedChanges1 := changes[maxHistoryLen-1]
   739  				removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)})
   740  				require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after)
   741  				require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after)
   742  				require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before)
   743  				reversedChanges2 := changes[maxHistoryLen-2]
   744  				removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)})
   745  				require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after)
   746  			},
   747  		},
   748  		{
   749  			name:   "third most recent change with end filter",
   750  			rootID: changes[maxHistoryLen-3].rootID,
   751  			end:    maybe.Some([]byte{byte(maxHistoryLen - 2)}), // Omit values from most recent change
   752  			validateFunc: func(require *require.Assertions, got *changeSummary) {
   753  				require.Len(got.nodes, 2)
   754  				require.Len(got.values, 1)
   755  				reversedChanges1 := changes[maxHistoryLen-1]
   756  				removedKey1 := ToKey([]byte{byte(maxHistoryLen - 1)})
   757  				require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after)
   758  				reversedChanges2 := changes[maxHistoryLen-2]
   759  				removedKey2 := ToKey([]byte{byte(maxHistoryLen - 2)})
   760  				require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after)
   761  				require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after)
   762  				require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before)
   763  			},
   764  		},
   765  	}
   766  
   767  	for _, tt := range tests {
   768  		t.Run(tt.name, func(t *testing.T) {
   769  			require := require.New(t)
   770  
   771  			got, err := history.getChangesToGetToRoot(tt.rootID, tt.start, tt.end)
   772  			require.ErrorIs(err, tt.expectedErr)
   773  			if tt.expectedErr != nil {
   774  				return
   775  			}
   776  			tt.validateFunc(require, got)
   777  		})
   778  	}
   779  }