github.com/koko1123/flow-go-1@v0.29.6/ledger/complete/mtrie/forest_test.go (about)

     1  package mtrie
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/koko1123/flow-go-1/ledger"
    14  	prf "github.com/koko1123/flow-go-1/ledger/common/proof"
    15  	"github.com/koko1123/flow-go-1/ledger/common/testutils"
    16  	"github.com/koko1123/flow-go-1/ledger/complete/mtrie/trie"
    17  	"github.com/koko1123/flow-go-1/ledger/partial/ptrie"
    18  	"github.com/koko1123/flow-go-1/module/metrics"
    19  )
    20  
    21  // TestTrieOperations tests adding removing and retrieving Trie from Forest
    22  func TestTrieOperations(t *testing.T) {
    23  
    24  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
    25  	require.NoError(t, err)
    26  
    27  	// Make new Trie (independently of MForest):
    28  	nt := trie.NewEmptyMTrie()
    29  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
    30  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
    31  
    32  	updatedTrie, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p1}, []ledger.Payload{*v1}, true)
    33  	require.NoError(t, err)
    34  
    35  	// Add trie
    36  	err = forest.AddTrie(updatedTrie)
    37  	require.NoError(t, err)
    38  
    39  	// Get trie
    40  	retnt, err := forest.GetTrie(updatedTrie.RootHash())
    41  	require.NoError(t, err)
    42  	require.Equal(t, retnt.RootHash(), updatedTrie.RootHash())
    43  	require.Equal(t, 2, forest.Size())
    44  }
    45  
    46  // TestTrieUpdate updates the empty trie with some values and verifies that the
    47  // written values can be retrieved from the updated trie.
    48  func TestTrieUpdate(t *testing.T) {
    49  
    50  	metricsCollector := &metrics.NoopCollector{}
    51  	forest, err := NewForest(5, metricsCollector, nil)
    52  	require.NoError(t, err)
    53  	rootHash := forest.GetEmptyRootHash()
    54  
    55  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
    56  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
    57  
    58  	paths := []ledger.Path{p1}
    59  	payloads := []*ledger.Payload{v1}
    60  	update := &ledger.TrieUpdate{RootHash: rootHash, Paths: paths, Payloads: payloads}
    61  	updatedRoot, err := forest.Update(update)
    62  	require.NoError(t, err)
    63  
    64  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
    65  	retValues, err := forest.Read(read)
    66  	require.NoError(t, err)
    67  	require.Equal(t, retValues[0], payloads[0].Value())
    68  }
    69  
    70  // TestLeftEmptyInsert tests inserting a new value into an empty sub-trie:
    71  //  1. we first construct a baseTrie holding a couple of values on the right branch [~]
    72  //  2. we update a previously non-existent register on the left branch (X)
    73  //
    74  // We verify that values for _all_ paths in the updated Trie have correct payloads
    75  func TestLeftEmptyInsert(t *testing.T) {
    76  	//////////////////////
    77  	//     insert X     //
    78  	//       ()         //
    79  	//      /  \        //
    80  	//    (X)  [~]      //
    81  	//////////////////////
    82  
    83  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
    84  	require.NoError(t, err)
    85  
    86  	// path: 1000...
    87  	p1 := pathByUint8s([]uint8{uint8(129), uint8(1)})
    88  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
    89  
    90  	// path: 1100...
    91  	p2 := pathByUint8s([]uint8{uint8(193), uint8(1)})
    92  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
    93  
    94  	paths := []ledger.Path{p1, p2}
    95  	payloads := []*ledger.Payload{v1, v2}
    96  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
    97  	baseRoot, err := forest.Update(update)
    98  	require.NoError(t, err)
    99  
   100  	baseTrie, err := forest.GetTrie(baseRoot)
   101  	require.NoError(t, err)
   102  	require.Equal(t, uint64(2), baseTrie.AllocatedRegCount())
   103  	require.Equal(t, uint64(v1.Size()+v2.Size()), baseTrie.AllocatedRegSize())
   104  
   105  	p3 := pathByUint8s([]uint8{uint8(1), uint8(1)})
   106  	v3 := payloadBySlices([]byte{'C'}, []byte{'C'})
   107  
   108  	paths = []ledger.Path{p3}
   109  	payloads = []*ledger.Payload{v3}
   110  	update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads}
   111  	updatedRoot, err := forest.Update(update)
   112  	require.NoError(t, err)
   113  
   114  	updatedTrie, err := forest.GetTrie(updatedRoot)
   115  	require.NoError(t, err)
   116  	require.Equal(t, uint64(3), updatedTrie.AllocatedRegCount())
   117  	require.Equal(t, uint64(v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize())
   118  	paths = []ledger.Path{p1, p2, p3}
   119  	payloads = []*ledger.Payload{v1, v2, v3}
   120  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   121  	retValues, err := forest.Read(read)
   122  	require.NoError(t, err)
   123  	for i := range paths {
   124  		require.Equal(t, retValues[i], payloads[i].Value())
   125  	}
   126  }
   127  
   128  // TestRightEmptyInsert tests inserting a new value into an empty sub-trie:
   129  //  1. we first construct a baseTrie holding a couple of values on the left branch [~]
   130  //  2. we update a previously non-existent register on the right branch (X)
   131  //
   132  // We verify that values for _all_ paths in the updated Trie have correct payloads
   133  func TestRightEmptyInsert(t *testing.T) {
   134  	///////////////////////
   135  	//     insert X      //
   136  	//       ()          //
   137  	//      /  \         //
   138  	//    [~]  (X)       //
   139  	///////////////////////
   140  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   141  	require.NoError(t, err)
   142  
   143  	// path: 0000...
   144  	p1 := pathByUint8s([]uint8{uint8(1), uint8(1)})
   145  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   146  
   147  	// path: 0100...
   148  	p2 := pathByUint8s([]uint8{uint8(64), uint8(1)})
   149  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   150  
   151  	paths := []ledger.Path{p1, p2}
   152  	payloads := []*ledger.Payload{v1, v2}
   153  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   154  	baseRoot, err := forest.Update(update)
   155  	require.NoError(t, err)
   156  
   157  	baseTrie, err := forest.GetTrie(baseRoot)
   158  	require.NoError(t, err)
   159  	require.Equal(t, uint64(2), baseTrie.AllocatedRegCount())
   160  	require.Equal(t, uint64(v1.Size()+v2.Size()), baseTrie.AllocatedRegSize())
   161  
   162  	// path: 1000...
   163  	p3 := pathByUint8s([]uint8{uint8(129), uint8(1)})
   164  	v3 := payloadBySlices([]byte{'C'}, []byte{'C'})
   165  
   166  	paths = []ledger.Path{p3}
   167  	payloads = []*ledger.Payload{v3}
   168  	update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads}
   169  	updatedRoot, err := forest.Update(update)
   170  	require.NoError(t, err)
   171  
   172  	updatedTrie, err := forest.GetTrie(updatedRoot)
   173  	require.NoError(t, err)
   174  	require.Equal(t, uint64(3), updatedTrie.AllocatedRegCount())
   175  	require.Equal(t, uint64(v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize())
   176  
   177  	paths = []ledger.Path{p1, p2, p3}
   178  	payloads = []*ledger.Payload{v1, v2, v3}
   179  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   180  	retValues, err := forest.Read(read)
   181  	require.NoError(t, err)
   182  	for i := range paths {
   183  		require.Equal(t, retValues[i], payloads[i].Value())
   184  	}
   185  }
   186  
   187  // TestExpansionInsert tests inserting a new value into a populated sub-trie, where a
   188  // leaf (holding a single value) would be replaced by an expanded sub-trie holding multiple value
   189  //  1. we first construct a baseTrie holding a couple of values on the right branch [~]
   190  //  2. we update a previously non-existent register on the right branch turning [~] to [~']
   191  //
   192  // We verify that values for _all_ paths in the updated Trie have correct payloads
   193  func TestExpansionInsert(t *testing.T) {
   194  	////////////////////////
   195  	// modify [~] -> [~'] //
   196  	//       ()           //
   197  	//      /  \          //
   198  	//         [~]        //
   199  	////////////////////////
   200  
   201  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   202  	require.NoError(t, err)
   203  
   204  	// path: 100000...
   205  	p1 := pathByUint8s([]uint8{uint8(129), uint8(1)})
   206  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   207  
   208  	paths := []ledger.Path{p1}
   209  	payloads := []*ledger.Payload{v1}
   210  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   211  	baseRoot, err := forest.Update(update)
   212  	require.NoError(t, err)
   213  
   214  	baseTrie, err := forest.GetTrie(baseRoot)
   215  	require.NoError(t, err)
   216  	require.Equal(t, uint64(1), baseTrie.AllocatedRegCount())
   217  	require.Equal(t, uint64(v1.Size()), baseTrie.AllocatedRegSize())
   218  
   219  	// path: 1000001...
   220  	p2 := pathByUint8s([]uint8{uint8(130), uint8(1)})
   221  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   222  
   223  	paths = []ledger.Path{p2}
   224  	payloads = []*ledger.Payload{v2}
   225  	update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads}
   226  	updatedRoot, err := forest.Update(update)
   227  	require.NoError(t, err)
   228  
   229  	updatedTrie, err := forest.GetTrie(updatedRoot)
   230  	require.NoError(t, err)
   231  	require.Equal(t, uint64(2), updatedTrie.AllocatedRegCount())
   232  	require.Equal(t, uint64(v1.Size()+v2.Size()), updatedTrie.AllocatedRegSize())
   233  
   234  	paths = []ledger.Path{p1, p2}
   235  	payloads = []*ledger.Payload{v1, v2}
   236  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   237  	retValues, err := forest.Read(read)
   238  	require.NoError(t, err)
   239  	for i := range paths {
   240  		require.Equal(t, retValues[i], payloads[i].Value())
   241  	}
   242  }
   243  
   244  // TestFullHouseInsert tests inserting a new value into a populated sub-trie, where a
   245  // leaf's value is overridden _and_ further values are added which all fall into a subtree that
   246  // replaces the leaf:
   247  //  1. we first construct a baseTrie holding a couple of values on the right branch [~]
   248  //  2. we update a previously non-existent register on the right branch turning [~] to [~']
   249  //
   250  // We verify that values for _all_ paths in the updated Trie have correct payloads
   251  func TestFullHouseInsert(t *testing.T) {
   252  	///////////////////////
   253  	//   insert ~1<X<~2  //
   254  	//       ()          //
   255  	//      /  \         //
   256  	//    [~1]  [~2]     //
   257  	///////////////////////
   258  
   259  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   260  	require.NoError(t, err)
   261  
   262  	// paths p0 forms [~1]; p1 and p2 form [~2]
   263  	// path: 0100...
   264  	p0 := pathByUint8s([]uint8{uint8(64), uint8(1)})
   265  	v0 := payloadBySlices([]byte{'0'}, []byte{'0'})
   266  	// path: 1000...
   267  	p1 := pathByUint8s([]uint8{uint8(129), uint8(1)})
   268  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   269  
   270  	// path: 1100...
   271  	p2 := pathByUint8s([]uint8{uint8(193), uint8(1)})
   272  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   273  
   274  	paths := []ledger.Path{p0, p1, p2}
   275  	payloads := []*ledger.Payload{v0, v1, v2}
   276  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   277  	baseRoot, err := forest.Update(update)
   278  	require.NoError(t, err)
   279  
   280  	baseTrie, err := forest.GetTrie(baseRoot)
   281  	require.NoError(t, err)
   282  	require.Equal(t, uint64(3), baseTrie.AllocatedRegCount())
   283  	require.Equal(t, uint64(v0.Size()+v1.Size()+v2.Size()), baseTrie.AllocatedRegSize())
   284  
   285  	// we update value for path p1 and in addition add p3 that has the same prefix `10` as p1
   286  	v1 = payloadBySlices([]byte{'X'}, []byte{'X'})
   287  
   288  	// path: 1010...
   289  	p3 := pathByUint8s([]uint8{uint8(160), uint8(1)})
   290  	v3 := payloadBySlices([]byte{'C'}, []byte{'C'})
   291  
   292  	paths = []ledger.Path{p1, p3}
   293  	payloads = []*ledger.Payload{v1, v3}
   294  	update = &ledger.TrieUpdate{RootHash: baseTrie.RootHash(), Paths: paths, Payloads: payloads}
   295  	updatedRoot, err := forest.Update(update)
   296  	require.NoError(t, err)
   297  
   298  	updatedTrie, err := forest.GetTrie(updatedRoot)
   299  	require.NoError(t, err)
   300  	require.Equal(t, uint64(4), updatedTrie.AllocatedRegCount())
   301  	require.Equal(t, uint64(v0.Size()+v1.Size()+v2.Size()+v3.Size()), updatedTrie.AllocatedRegSize())
   302  
   303  	paths = []ledger.Path{p1, p2, p3}
   304  	payloads = []*ledger.Payload{v1, v2, v3}
   305  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   306  	retValues, err := forest.Read(read)
   307  	require.NoError(t, err)
   308  	for i := range paths {
   309  		require.Equal(t, retValues[i], payloads[i].Value())
   310  	}
   311  }
   312  
   313  // TestLeafInsert inserts two keys, which only differ in their last bit.
   314  // I.e. the trie needs to be expanded to its hull depth
   315  // We verify that values for _all_ paths in the updated Trie have correct payloads
   316  func TestLeafInsert(t *testing.T) {
   317  	///////////////////////
   318  	//   insert 1, 2     //
   319  	//       ()          //
   320  	//      /  \         //
   321  	//     ()   ...      //
   322  	//          /  \     //
   323  	//         ()  ()    //
   324  	///////////////////////
   325  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   326  	require.NoError(t, err)
   327  
   328  	// path: 000...0000000100000000
   329  	p1 := testutils.PathByUint16LeftPadded(256)
   330  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   331  
   332  	// path: 000...0000000100000001
   333  	p2 := testutils.PathByUint16LeftPadded(257)
   334  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   335  
   336  	paths := []ledger.Path{p1, p2}
   337  	payloads := []*ledger.Payload{v1, v2}
   338  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   339  	updatedRoot, err := forest.Update(update)
   340  	require.NoError(t, err)
   341  
   342  	updatedTrie, err := forest.GetTrie(updatedRoot)
   343  	require.NoError(t, err)
   344  	require.Equal(t, uint64(2), updatedTrie.AllocatedRegCount())
   345  	require.Equal(t, uint64(v1.Size()+v2.Size()), updatedTrie.AllocatedRegSize())
   346  
   347  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   348  	retValues, err := forest.Read(read)
   349  	require.NoError(t, err)
   350  	for i := range paths {
   351  		require.Equal(t, retValues[i], payloads[i].Value())
   352  	}
   353  }
   354  
   355  // TestOverrideValue overrides an existing value in the trie (without any expansion)
   356  // We verify that values for _all_ paths in the updated Trie have correct payloads
   357  func TestOverrideValue(t *testing.T) {
   358  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   359  	require.NoError(t, err)
   360  
   361  	// path: 1000...
   362  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   363  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   364  
   365  	// path: 0111...
   366  	p2 := pathByUint8s([]uint8{uint8(116), uint8(129)})
   367  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   368  
   369  	paths := []ledger.Path{p1, p2}
   370  	payloads := []*ledger.Payload{v1, v2}
   371  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   372  	baseRoot, err := forest.Update(update)
   373  	require.NoError(t, err)
   374  
   375  	// path: 1000...
   376  	p3 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   377  	v3 := payloadBySlices([]byte{'C'}, []byte{'C'})
   378  
   379  	paths = []ledger.Path{p3}
   380  	payloads = []*ledger.Payload{v3}
   381  	update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: paths, Payloads: payloads}
   382  	updatedRoot, err := forest.Update(update)
   383  	require.NoError(t, err)
   384  
   385  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   386  	retValues, err := forest.Read(read)
   387  	require.NoError(t, err)
   388  	require.Equal(t, retValues[0], payloads[0].Value())
   389  
   390  }
   391  
   392  // TestDuplicateOverride tests behaviour when the updates contain two different payloads for the
   393  // same path. I.e. we update with (p0, v0) and (p0, v1)
   394  // We expect that the _last_ written value is persisted in the Trie
   395  func TestDuplicateOverride(t *testing.T) {
   396  
   397  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   398  	require.NoError(t, err)
   399  
   400  	// path: 1000...
   401  	p0 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   402  	v0 := payloadBySlices([]byte{'A'}, []byte{'A'})
   403  	paths := []ledger.Path{p0}
   404  	payloads := []*ledger.Payload{v0}
   405  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   406  	baseRoot, err := forest.Update(update)
   407  	require.NoError(t, err)
   408  
   409  	v1 := payloadBySlices([]byte{'B'}, []byte{'B'})
   410  	v2 := payloadBySlices([]byte{'C'}, []byte{'C'})
   411  	paths = []ledger.Path{p0, p0}
   412  	payloads = []*ledger.Payload{v1, v2}
   413  	update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: paths, Payloads: payloads}
   414  	updatedRoot, err := forest.Update(update)
   415  	require.NoError(t, err)
   416  
   417  	paths = []ledger.Path{p0}
   418  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   419  	retValues, err := forest.Read(read)
   420  	require.NoError(t, err)
   421  	require.Equal(t, retValues[0], v2.Value())
   422  
   423  }
   424  
   425  // TestReadSafety check if payload returned from a forest are safe against modification,
   426  // ie. copy of the data is returned, instead of a slice
   427  func TestReadSafety(t *testing.T) {
   428  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   429  	require.NoError(t, err)
   430  
   431  	// path: 1000...
   432  	p0 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   433  	v0 := payloadBySlices([]byte{'A'}, []byte{'A'})
   434  	paths := []ledger.Path{p0}
   435  	payloads := []*ledger.Payload{v0}
   436  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   437  	baseRoot, err := forest.Update(update)
   438  	require.NoError(t, err)
   439  
   440  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: paths}
   441  	data, err := forest.Read(read)
   442  	require.NoError(t, err)
   443  
   444  	require.Len(t, data, 1)
   445  	require.Equal(t, v0.Value(), data[0])
   446  
   447  	// modify returned slice
   448  	data[0] = []byte("new value")
   449  
   450  	// read again
   451  	data2, err := forest.Read(read)
   452  	require.NoError(t, err)
   453  	require.Len(t, data2, 1)
   454  	require.Equal(t, v0.Value(), data2[0])
   455  }
   456  
   457  // TestReadOrder tests that payloads from reading a trie are delivered in the order as specified by the paths
   458  func TestReadOrder(t *testing.T) {
   459  
   460  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   461  	require.NoError(t, err)
   462  
   463  	p1 := pathByUint8s([]uint8{uint8(116), uint8(74)})
   464  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   465  
   466  	p2 := pathByUint8s([]uint8{uint8(53), uint8(129)})
   467  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   468  
   469  	paths := []ledger.Path{p1, p2}
   470  	payloads := []*ledger.Payload{v1, v2}
   471  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   472  	testRoot, err := forest.Update(update)
   473  	require.NoError(t, err)
   474  
   475  	read := &ledger.TrieRead{RootHash: testRoot, Paths: []ledger.Path{p1, p2}}
   476  	retValues, err := forest.Read(read)
   477  	require.NoError(t, err)
   478  	require.Equal(t, len(read.Paths), len(retValues))
   479  	require.Equal(t, retValues[0], payloads[0].Value())
   480  	require.Equal(t, retValues[1], payloads[1].Value())
   481  
   482  	read = &ledger.TrieRead{RootHash: testRoot, Paths: []ledger.Path{p2, p1}}
   483  	retValues, err = forest.Read(read)
   484  	require.NoError(t, err)
   485  	require.Equal(t, len(read.Paths), len(retValues))
   486  	require.Equal(t, retValues[1], payloads[0].Value())
   487  	require.Equal(t, retValues[0], payloads[1].Value())
   488  }
   489  
   490  // TestMixRead tests reading a mixture of set and unset registers.
   491  // We expect the default payload (nil) to be returned for unset registers.
   492  func TestMixRead(t *testing.T) {
   493  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   494  	require.NoError(t, err)
   495  
   496  	// path: 01111101...
   497  	p1 := pathByUint8s([]uint8{uint8(125), uint8(23)})
   498  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   499  
   500  	// path: 10110010...
   501  	p2 := pathByUint8s([]uint8{uint8(178), uint8(152)})
   502  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   503  
   504  	paths := []ledger.Path{p1, p2}
   505  	payloads := []*ledger.Payload{v1, v2}
   506  
   507  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   508  	baseRoot, err := forest.Update(update)
   509  	require.NoError(t, err)
   510  
   511  	// path: 01101110...
   512  	p3 := pathByUint8s([]uint8{uint8(110), uint8(48)})
   513  	v3 := ledger.EmptyPayload()
   514  
   515  	// path: 00010111...
   516  	p4 := pathByUint8s([]uint8{uint8(23), uint8(82)})
   517  	v4 := ledger.EmptyPayload()
   518  
   519  	readPaths := []ledger.Path{p1, p2, p3, p4}
   520  	expectedPayloads := []*ledger.Payload{v1, v2, v3, v4}
   521  
   522  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths}
   523  	retValues, err := forest.Read(read)
   524  	require.NoError(t, err)
   525  	for i := range paths {
   526  		require.Equal(t, retValues[i], expectedPayloads[i].Value())
   527  	}
   528  }
   529  
   530  // TestReadWithDuplicatedKeys reads a the values for two keys, where both keys have the same value.
   531  // We expect that we receive the respective value twice in the return.
   532  func TestReadWithDuplicatedKeys(t *testing.T) {
   533  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   534  	require.NoError(t, err)
   535  
   536  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   537  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   538  	p2 := pathByUint8s([]uint8{uint8(116), uint8(129)})
   539  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   540  	p3 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   541  
   542  	paths := []ledger.Path{p1, p2}
   543  	payloads := []*ledger.Payload{v1, v2}
   544  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   545  	updatedRoot, err := forest.Update(update)
   546  	require.NoError(t, err)
   547  
   548  	paths = []ledger.Path{p1, p2, p3}
   549  	expectedPayloads := []*ledger.Payload{v1, v2, v1}
   550  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   551  	retValues, err := forest.Read(read)
   552  	require.NoError(t, err)
   553  	require.Equal(t, len(read.Paths), len(retValues))
   554  	for i := range paths {
   555  		require.Equal(t, retValues[i], expectedPayloads[i].Value())
   556  	}
   557  }
   558  
   559  // TestReadNonExistingPath tests reading an unset path.
   560  func TestReadNonExistingPath(t *testing.T) {
   561  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   562  	require.NoError(t, err)
   563  
   564  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   565  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   566  	paths := []ledger.Path{p1}
   567  	payloads := []*ledger.Payload{v1}
   568  
   569  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   570  	updatedRoot, err := forest.Update(update)
   571  	require.NoError(t, err)
   572  
   573  	p2 := pathByUint8s([]uint8{uint8(116), uint8(129)})
   574  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: []ledger.Path{p2}}
   575  	retValues, err := forest.Read(read)
   576  	require.NoError(t, err)
   577  	require.Equal(t, 0, len(retValues[0]))
   578  }
   579  
   580  // TestReadSinglePayload tests reading a single payload of set/unset register.
   581  func TestReadSinglePayload(t *testing.T) {
   582  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   583  	require.NoError(t, err)
   584  
   585  	// path: 01111101...
   586  	path1 := pathByUint8s([]uint8{uint8(125), uint8(23)})
   587  	payload1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   588  
   589  	// path: 10110010...
   590  	path2 := pathByUint8s([]uint8{uint8(178), uint8(152)})
   591  	payload2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   592  
   593  	paths := []ledger.Path{path1, path2}
   594  	payloads := []*ledger.Payload{payload1, payload2}
   595  
   596  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   597  	baseRoot, err := forest.Update(update)
   598  	require.NoError(t, err)
   599  
   600  	// path: 01101110...
   601  	path3 := pathByUint8s([]uint8{uint8(110), uint8(48)})
   602  	payload3 := ledger.EmptyPayload()
   603  
   604  	// path: 00010111...
   605  	path4 := pathByUint8s([]uint8{uint8(23), uint8(82)})
   606  	payload4 := ledger.EmptyPayload()
   607  
   608  	expectedPayloads := make(map[ledger.Path]*ledger.Payload)
   609  	expectedPayloads[path1] = payload1
   610  	expectedPayloads[path2] = payload2
   611  	expectedPayloads[path3] = payload3
   612  	expectedPayloads[path4] = payload4
   613  
   614  	// Batch read one payload at a time (less efficient)
   615  	for path, payload := range expectedPayloads {
   616  		read := &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{path}}
   617  		retValues, err := forest.Read(read)
   618  		require.NoError(t, err)
   619  		require.Equal(t, 1, len(retValues))
   620  		if payload.IsEmpty() {
   621  			require.Equal(t, 0, len(retValues[0]))
   622  		} else {
   623  			require.Equal(t, payload.Value(), retValues[0])
   624  		}
   625  	}
   626  
   627  	// Read single value
   628  	for path, payload := range expectedPayloads {
   629  		read := &ledger.TrieReadSingleValue{RootHash: baseRoot, Path: path}
   630  		retValue, err := forest.ReadSingleValue(read)
   631  		require.NoError(t, err)
   632  		if payload.IsEmpty() {
   633  			require.Equal(t, 0, len(retValue))
   634  		} else {
   635  			require.Equal(t, payload.Value(), retValue)
   636  		}
   637  	}
   638  }
   639  
   640  // TestForkingUpdates updates a base trie in two different ways. We expect
   641  // that for each update, a new trie is added to the forest preserving the
   642  // updated values independently of the other update.
   643  func TestForkingUpdates(t *testing.T) {
   644  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   645  	require.NoError(t, err)
   646  
   647  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   648  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   649  	p2 := pathByUint8s([]uint8{uint8(116), uint8(129)})
   650  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   651  	paths := []ledger.Path{p1, p2}
   652  	payloads := []*ledger.Payload{v1, v2}
   653  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   654  	baseRoot, err := forest.Update(update)
   655  	require.NoError(t, err)
   656  
   657  	// update baseTrie -> updatedTrieA
   658  	v1a := payloadBySlices([]byte{'C'}, []byte{'C'})
   659  	p3a := pathByUint8s([]uint8{uint8(116), uint8(22)})
   660  	v3a := payloadBySlices([]byte{'D'}, []byte{'D'})
   661  	pathsA := []ledger.Path{p1, p3a}
   662  	payloadsA := []*ledger.Payload{v1a, v3a}
   663  	updateA := &ledger.TrieUpdate{RootHash: baseRoot, Paths: pathsA, Payloads: payloadsA}
   664  	updatedRootA, err := forest.Update(updateA)
   665  	require.NoError(t, err)
   666  
   667  	// update baseTrie -> updatedTrieB
   668  	v1b := payloadBySlices([]byte{'E'}, []byte{'E'})
   669  	p3b := pathByUint8s([]uint8{uint8(116), uint8(22)})
   670  	v3b := payloadBySlices([]byte{'F'}, []byte{'F'})
   671  	pathsB := []ledger.Path{p1, p3b}
   672  	payloadsB := []*ledger.Payload{v1b, v3b}
   673  	updateB := &ledger.TrieUpdate{RootHash: baseRoot, Paths: pathsB, Payloads: payloadsB}
   674  	updatedRootB, err := forest.Update(updateB)
   675  	require.NoError(t, err)
   676  
   677  	// Verify payloads are preserved
   678  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: paths}
   679  	retValues, err := forest.Read(read) // reading from original Trie
   680  	require.NoError(t, err)
   681  	for i := range paths {
   682  		require.Equal(t, retValues[i], payloads[i].Value())
   683  	}
   684  
   685  	readA := &ledger.TrieRead{RootHash: updatedRootA, Paths: pathsA}
   686  	retValues, err = forest.Read(readA) // reading from updatedTrieA
   687  	require.NoError(t, err)
   688  	for i := range paths {
   689  		require.Equal(t, retValues[i], payloadsA[i].Value())
   690  	}
   691  
   692  	readB := &ledger.TrieRead{RootHash: updatedRootB, Paths: pathsB}
   693  	retValues, err = forest.Read(readB) // reading from updatedTrieB
   694  	require.NoError(t, err)
   695  	for i := range paths {
   696  		require.Equal(t, retValues[i], payloadsB[i].Value())
   697  	}
   698  }
   699  
   700  // TestIdenticalUpdateAppliedTwice updates a base trie in the same way twice.
   701  // Hence, the forest should de-duplicate the resulting two version of the identical trie
   702  // without an error.
   703  func TestIdenticalUpdateAppliedTwice(t *testing.T) {
   704  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   705  	require.NoError(t, err)
   706  
   707  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
   708  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   709  	p2 := pathByUint8s([]uint8{uint8(116), uint8(129)})
   710  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   711  	paths := []ledger.Path{p1, p2}
   712  	payloads := []*ledger.Payload{v1, v2}
   713  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   714  	baseRoot, err := forest.Update(update)
   715  	require.NoError(t, err)
   716  
   717  	p3 := pathByUint8s([]uint8{uint8(116), uint8(22)})
   718  	v3 := payloadBySlices([]byte{'D'}, []byte{'D'})
   719  
   720  	update = &ledger.TrieUpdate{RootHash: baseRoot, Paths: []ledger.Path{p3}, Payloads: []*ledger.Payload{v3}}
   721  	updatedRootA, err := forest.Update(update)
   722  	require.NoError(t, err)
   723  	updatedRootB, err := forest.Update(update)
   724  	require.NoError(t, err)
   725  	require.Equal(t, updatedRootA, updatedRootB)
   726  
   727  	paths = []ledger.Path{p1, p2, p3}
   728  	payloads = []*ledger.Payload{v1, v2, v3}
   729  	read := &ledger.TrieRead{RootHash: updatedRootA, Paths: paths}
   730  	retValuesA, err := forest.Read(read)
   731  	require.NoError(t, err)
   732  	for i := range paths {
   733  		require.Equal(t, retValuesA[i], payloads[i].Value())
   734  	}
   735  
   736  	read = &ledger.TrieRead{RootHash: updatedRootB, Paths: paths}
   737  	retValuesB, err := forest.Read(read)
   738  	require.NoError(t, err)
   739  	for i := range paths {
   740  		require.Equal(t, retValuesB[i], payloads[i].Value())
   741  	}
   742  }
   743  
   744  // TestRandomUpdateReadProofValueSizes repeats a sequence of actions update, read, get value sizes, and proof random paths
   745  // this simulates the common pattern of actions on flow
   746  func TestRandomUpdateReadProofValueSizes(t *testing.T) {
   747  
   748  	minPayloadByteSize := 2
   749  	maxPayloadByteSize := 10
   750  	rep := 10
   751  	maxNumPathsPerStep := 10
   752  	seed := time.Now().UnixNano()
   753  	rand.Seed(seed)
   754  	t.Log(seed)
   755  
   756  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   757  	require.NoError(t, err)
   758  
   759  	activeRoot := forest.GetEmptyRootHash()
   760  	require.NoError(t, err)
   761  	latestPayloadByPath := make(map[ledger.Path]*ledger.Payload) // map store
   762  
   763  	for e := 0; e < rep; e++ {
   764  		paths := testutils.RandomPathsRandLen(maxNumPathsPerStep)
   765  		payloads := testutils.RandomPayloads(len(paths), minPayloadByteSize, maxPayloadByteSize)
   766  
   767  		// update map store with key values
   768  		// we use this at the end of each step to check all existing keys
   769  		for i, p := range paths {
   770  			latestPayloadByPath[p] = payloads[i]
   771  		}
   772  
   773  		// test reading for non-existing keys
   774  		nonExistingPaths := make([]ledger.Path, 0)
   775  		otherPaths := testutils.RandomPathsRandLen(maxNumPathsPerStep)
   776  		for _, p := range otherPaths {
   777  			if _, ok := latestPayloadByPath[p]; !ok {
   778  				nonExistingPaths = append(nonExistingPaths, p)
   779  			}
   780  		}
   781  		read := &ledger.TrieRead{RootHash: activeRoot, Paths: nonExistingPaths}
   782  		retValues, err := forest.Read(read)
   783  		require.NoError(t, err, "error reading - non existing paths")
   784  		for _, p := range retValues {
   785  			require.Equal(t, 0, len(p))
   786  		}
   787  
   788  		// test value sizes for non-existent keys
   789  		retValueSizes, err := forest.ValueSizes(read)
   790  		require.NoError(t, err, "error value sizes - non existent paths")
   791  		require.Equal(t, len(read.Paths), len(retValueSizes))
   792  		for _, size := range retValueSizes {
   793  			require.Equal(t, 0, size)
   794  		}
   795  
   796  		// test update
   797  		update := &ledger.TrieUpdate{RootHash: activeRoot, Paths: paths, Payloads: payloads}
   798  		activeRoot, err = forest.Update(update)
   799  		require.NoError(t, err, "error updating")
   800  
   801  		// test read
   802  		read = &ledger.TrieRead{RootHash: activeRoot, Paths: paths}
   803  		retValues, err = forest.Read(read)
   804  		require.NoError(t, err, "error reading")
   805  		for i := range payloads {
   806  			require.Equal(t, retValues[i], payloads[i].Value())
   807  		}
   808  
   809  		// test value sizes for existing keys
   810  		retValueSizes, err = forest.ValueSizes(read)
   811  		require.NoError(t, err, "error value sizes")
   812  		require.Equal(t, len(read.Paths), len(retValueSizes))
   813  		for i := range payloads {
   814  			require.Equal(t, payloads[i].Value().Size(), retValueSizes[i])
   815  		}
   816  
   817  		// test proof (mix of existing and non existing keys)
   818  		proofPaths := make([]ledger.Path, 0)
   819  		proofPaths = append(proofPaths, paths...)
   820  		proofPaths = append(proofPaths, nonExistingPaths...)
   821  
   822  		// shuffle the order of `proofPaths` to run `Proofs` on non-sorted input paths
   823  		rand.Shuffle(len(proofPaths), func(i, j int) {
   824  			proofPaths[i], proofPaths[j] = proofPaths[j], proofPaths[i]
   825  		})
   826  
   827  		// sort `proofPaths` into another slice
   828  		sortedPaths := sortedCopy(proofPaths)
   829  
   830  		read = &ledger.TrieRead{RootHash: activeRoot, Paths: proofPaths}
   831  		batchProof, err := forest.Proofs(read)
   832  		require.NoError(t, err, "error generating proofs")
   833  		assert.True(t, prf.VerifyTrieBatchProof(batchProof, ledger.State(activeRoot)))
   834  
   835  		// check `Proofs` has sorted the input paths.
   836  		// this check is needed to not weaken the SPoCK secret entropy,
   837  		// when `Proofs` is used to generate chunk data.
   838  		assert.Equal(t, sortedPaths, read.Paths)
   839  
   840  		// build a partial trie from batch proofs and check the root hash is equal
   841  		psmt, err := ptrie.NewPSMT(activeRoot, batchProof)
   842  		require.NoError(t, err, "error building partial trie")
   843  		assert.Equal(t, psmt.RootHash(), activeRoot)
   844  
   845  		// check payloads for all existing paths
   846  		allPaths := make([]ledger.Path, 0, len(latestPayloadByPath))
   847  		allPayloads := make([]*ledger.Payload, 0, len(latestPayloadByPath))
   848  		for p, v := range latestPayloadByPath {
   849  			allPaths = append(allPaths, p)
   850  			allPayloads = append(allPayloads, v)
   851  		}
   852  
   853  		read = &ledger.TrieRead{RootHash: activeRoot, Paths: allPaths}
   854  		retValues, err = forest.Read(read)
   855  		require.NoError(t, err)
   856  		for i, v := range allPayloads {
   857  			assert.Equal(t, retValues[i], v.Value())
   858  		}
   859  
   860  		// check value sizes for all existing paths
   861  		retValueSizes, err = forest.ValueSizes(read)
   862  		require.NoError(t, err)
   863  		require.Equal(t, len(read.Paths), len(retValueSizes))
   864  		for i, v := range allPayloads {
   865  			assert.Equal(t, v.Value().Size(), retValueSizes[i])
   866  		}
   867  	}
   868  }
   869  
   870  func sortedCopy(paths []ledger.Path) []ledger.Path {
   871  	sortedPaths := make([]ledger.Path, len(paths))
   872  	copy(sortedPaths, paths)
   873  	sort.Slice(sortedPaths, func(i, j int) bool {
   874  		return bytes.Compare(sortedPaths[i][:], sortedPaths[j][:]) < 0
   875  	})
   876  	return sortedPaths
   877  }
   878  
   879  // TestProofGenerationInclusion tests that inclusion proofs generated by a Trie pass verification
   880  func TestProofGenerationInclusion(t *testing.T) {
   881  
   882  	metricsCollector := &metrics.NoopCollector{}
   883  	forest, err := NewForest(5, metricsCollector, nil)
   884  	require.NoError(t, err)
   885  	emptyRoot := forest.GetEmptyRootHash()
   886  
   887  	p1 := pathByUint8s([]uint8{uint8(1), uint8(74)})
   888  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
   889  	p2 := pathByUint8s([]uint8{uint8(2), uint8(74)})
   890  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
   891  	p3 := pathByUint8s([]uint8{uint8(130), uint8(74)})
   892  	v3 := payloadBySlices([]byte{'C'}, []byte{'C'})
   893  	p4 := pathByUint8s([]uint8{uint8(131), uint8(74)})
   894  	v4 := payloadBySlices([]byte{'D'}, []byte{'D'})
   895  	paths := []ledger.Path{p1, p2, p3, p4}
   896  	payloads := []*ledger.Payload{v1, v2, v3, v4}
   897  
   898  	// shuffle the order of `proofPaths` to run `Proofs` on non-sorted input paths
   899  	rand.Shuffle(len(paths), func(i, j int) {
   900  		paths[i], paths[j] = paths[j], paths[i]
   901  	})
   902  
   903  	// sort `proofPaths` into another slice
   904  	sortedPaths := sortedCopy(paths)
   905  
   906  	update := &ledger.TrieUpdate{RootHash: emptyRoot, Paths: paths, Payloads: payloads}
   907  	updatedRoot, err := forest.Update(update)
   908  	require.NoError(t, err)
   909  	read := &ledger.TrieRead{RootHash: updatedRoot, Paths: paths}
   910  	proof, err := forest.Proofs(read)
   911  	require.NoError(t, err)
   912  
   913  	// verify batch proofs.
   914  	assert.True(t, prf.VerifyTrieBatchProof(proof, ledger.State(updatedRoot)))
   915  
   916  	// check `Proofs` has sorted the input paths.
   917  	// this check is needed to not weaken the SPoCK secret entropy,
   918  	// when `Proofs` is used to generate chunk data.
   919  	assert.Equal(t, sortedPaths, read.Paths)
   920  }
   921  
   922  func payloadBySlices(keydata []byte, valuedata []byte) *ledger.Payload {
   923  	key := ledger.Key{KeyParts: []ledger.KeyPart{{Type: 0, Value: keydata}}}
   924  	value := ledger.Value(valuedata)
   925  	return ledger.NewPayload(key, value)
   926  }
   927  
   928  func pathByUint8s(inputs []uint8) ledger.Path {
   929  	var b ledger.Path
   930  	copy(b[:], inputs)
   931  	return b
   932  }
   933  
   934  // TestValueSizesOrder tests returned value sizes are in the order as specified by the paths
   935  func TestValueSizesOrder(t *testing.T) {
   936  
   937  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   938  	require.NoError(t, err)
   939  
   940  	// path: 01111101...
   941  	p1 := pathByUint8s([]uint8{uint8(125), uint8(23)})
   942  	v1 := testutils.RandomPayload(1, 100)
   943  
   944  	// path: 10110010...
   945  	p2 := pathByUint8s([]uint8{uint8(178), uint8(152)})
   946  	v2 := testutils.RandomPayload(1, 100)
   947  
   948  	paths := []ledger.Path{p1, p2}
   949  	payloads := []*ledger.Payload{v1, v2}
   950  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   951  	baseRoot, err := forest.Update(update)
   952  	require.NoError(t, err)
   953  
   954  	// Get value sizes for paths {p1, p2}
   955  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{p1, p2}}
   956  	retValueSizes, err := forest.ValueSizes(read)
   957  	require.NoError(t, err)
   958  	require.Equal(t, len(read.Paths), len(retValueSizes))
   959  	require.Equal(t, v1.Value().Size(), retValueSizes[0])
   960  	require.Equal(t, v2.Value().Size(), retValueSizes[1])
   961  
   962  	// Get value sizes for paths {p2, p1}
   963  	read = &ledger.TrieRead{RootHash: baseRoot, Paths: []ledger.Path{p2, p1}}
   964  	retValueSizes, err = forest.ValueSizes(read)
   965  	require.NoError(t, err)
   966  	require.Equal(t, len(read.Paths), len(retValueSizes))
   967  	require.Equal(t, v2.Value().Size(), retValueSizes[0])
   968  	require.Equal(t, v1.Value().Size(), retValueSizes[1])
   969  }
   970  
   971  // TestMixGetValueSizes tests value sizes of a mix of set and unset registers.
   972  // We expect value size 0 to be returned for unset registers.
   973  func TestMixGetValueSizes(t *testing.T) {
   974  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
   975  	require.NoError(t, err)
   976  
   977  	// path: 01111101...
   978  	p1 := pathByUint8s([]uint8{uint8(125), uint8(23)})
   979  	v1 := testutils.RandomPayload(1, 100)
   980  
   981  	// path: 10110010...
   982  	p2 := pathByUint8s([]uint8{uint8(178), uint8(152)})
   983  	v2 := testutils.RandomPayload(1, 100)
   984  
   985  	paths := []ledger.Path{p1, p2}
   986  	payloads := []*ledger.Payload{v1, v2}
   987  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
   988  	baseRoot, err := forest.Update(update)
   989  	require.NoError(t, err)
   990  
   991  	// path: 01101110...
   992  	p3 := pathByUint8s([]uint8{uint8(110), uint8(48)})
   993  
   994  	// path: 00010111...
   995  	p4 := pathByUint8s([]uint8{uint8(23), uint8(82)})
   996  
   997  	readPaths := []ledger.Path{p1, p2, p3, p4}
   998  	expectedValueSizes := []int{v1.Value().Size(), v2.Value().Size(), 0, 0}
   999  
  1000  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths}
  1001  	retValueSizes, err := forest.ValueSizes(read)
  1002  	require.NoError(t, err)
  1003  	require.Equal(t, len(read.Paths), len(retValueSizes))
  1004  	for i := range read.Paths {
  1005  		require.Equal(t, expectedValueSizes[i], retValueSizes[i])
  1006  	}
  1007  }
  1008  
  1009  // TestValueSizesWithDuplicatedKeys gets value sizes for two keys,
  1010  // where both keys have the same value.
  1011  // We expect to receive same value sizes twice.
  1012  func TestValueSizesWithDuplicatedKeys(t *testing.T) {
  1013  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
  1014  	require.NoError(t, err)
  1015  
  1016  	// path: 01111101...
  1017  	p1 := pathByUint8s([]uint8{uint8(125), uint8(23)})
  1018  	v1 := testutils.RandomPayload(1, 100)
  1019  
  1020  	// path: 10110010...
  1021  	p2 := pathByUint8s([]uint8{uint8(178), uint8(152)})
  1022  	v2 := testutils.RandomPayload(1, 100)
  1023  
  1024  	// same path as p1
  1025  	p3 := pathByUint8s([]uint8{uint8(125), uint8(23)})
  1026  
  1027  	paths := []ledger.Path{p1, p2}
  1028  	payloads := []*ledger.Payload{v1, v2}
  1029  	update := &ledger.TrieUpdate{RootHash: forest.GetEmptyRootHash(), Paths: paths, Payloads: payloads}
  1030  	baseRoot, err := forest.Update(update)
  1031  	require.NoError(t, err)
  1032  
  1033  	readPaths := []ledger.Path{p1, p2, p3}
  1034  	expectedValueSizes := []int{v1.Value().Size(), v2.Value().Size(), v1.Value().Size()}
  1035  
  1036  	read := &ledger.TrieRead{RootHash: baseRoot, Paths: readPaths}
  1037  	retValueSizes, err := forest.ValueSizes(read)
  1038  	require.NoError(t, err)
  1039  	require.Equal(t, len(read.Paths), len(retValueSizes))
  1040  	for i := range read.Paths {
  1041  		require.Equal(t, expectedValueSizes[i], retValueSizes[i])
  1042  	}
  1043  }
  1044  
  1045  func TestPurgeCacheExcept(t *testing.T) {
  1046  	forest, err := NewForest(5, &metrics.NoopCollector{}, nil)
  1047  	require.NoError(t, err)
  1048  
  1049  	nt := trie.NewEmptyMTrie()
  1050  	p1 := pathByUint8s([]uint8{uint8(53), uint8(74)})
  1051  	v1 := payloadBySlices([]byte{'A'}, []byte{'A'})
  1052  
  1053  	updatedTrie1, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p1}, []ledger.Payload{*v1}, true)
  1054  	require.NoError(t, err)
  1055  
  1056  	err = forest.AddTrie(updatedTrie1)
  1057  	require.NoError(t, err)
  1058  
  1059  	p2 := pathByUint8s([]uint8{uint8(12), uint8(34)})
  1060  	v2 := payloadBySlices([]byte{'B'}, []byte{'B'})
  1061  
  1062  	updatedTrie2, _, err := trie.NewTrieWithUpdatedRegisters(nt, []ledger.Path{p2}, []ledger.Payload{*v2}, true)
  1063  	require.NoError(t, err)
  1064  
  1065  	err = forest.AddTrie(updatedTrie2)
  1066  	require.NoError(t, err)
  1067  	require.Equal(t, 3, forest.tries.Count())
  1068  
  1069  	err = forest.PurgeCacheExcept(updatedTrie2.RootHash())
  1070  	require.NoError(t, err)
  1071  	require.Equal(t, 1, forest.tries.Count())
  1072  
  1073  	ret, err := forest.GetTrie(updatedTrie2.RootHash())
  1074  	require.NoError(t, err)
  1075  	require.Equal(t, ret, updatedTrie2)
  1076  
  1077  	_, err = forest.GetTrie(updatedTrie1.RootHash())
  1078  	require.Error(t, err)
  1079  
  1080  	// test purge with non existing trie
  1081  	err = forest.PurgeCacheExcept(updatedTrie1.RootHash())
  1082  	require.Error(t, err)
  1083  
  1084  	ret, err = forest.GetTrie(updatedTrie2.RootHash())
  1085  	require.NoError(t, err)
  1086  	require.Equal(t, ret, updatedTrie2)
  1087  
  1088  	_, err = forest.GetTrie(updatedTrie1.RootHash())
  1089  	require.Error(t, err)
  1090  
  1091  	// test purge when only a single target trie exist there
  1092  	err = forest.PurgeCacheExcept(updatedTrie2.RootHash())
  1093  	require.NoError(t, err)
  1094  	require.Equal(t, 1, forest.tries.Count())
  1095  }