github.com/onflow/flow-go@v0.33.17/ledger/complete/ledger_test.go (about)

     1  package complete_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"math/rand"
     9  	"testing"
    10  
    11  	"github.com/rs/zerolog"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"go.uber.org/atomic"
    15  
    16  	"github.com/onflow/flow-go/ledger"
    17  	"github.com/onflow/flow-go/ledger/common/pathfinder"
    18  	"github.com/onflow/flow-go/ledger/common/proof"
    19  	"github.com/onflow/flow-go/ledger/common/testutils"
    20  	"github.com/onflow/flow-go/ledger/complete"
    21  	"github.com/onflow/flow-go/ledger/complete/wal"
    22  	"github.com/onflow/flow-go/ledger/complete/wal/fixtures"
    23  	"github.com/onflow/flow-go/ledger/partial/ptrie"
    24  	"github.com/onflow/flow-go/module/metrics"
    25  	"github.com/onflow/flow-go/utils/unittest"
    26  )
    27  
    28  func TestNewLedger(t *testing.T) {
    29  	metricsCollector := &metrics.NoopCollector{}
    30  	wal := &fixtures.NoopWAL{}
    31  	_, err := complete.NewLedger(wal, 100, metricsCollector, zerolog.Logger{}, complete.DefaultPathFinderVersion)
    32  	assert.NoError(t, err)
    33  
    34  }
    35  
    36  func TestLedger_Update(t *testing.T) {
    37  	t.Run("empty update", func(t *testing.T) {
    38  
    39  		wal := &fixtures.NoopWAL{}
    40  
    41  		l, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
    42  		require.NoError(t, err)
    43  
    44  		compactor := fixtures.NewNoopCompactor(l)
    45  		<-compactor.Ready()
    46  		defer func() {
    47  			<-l.Done()
    48  			<-compactor.Done()
    49  		}()
    50  
    51  		// create empty update
    52  		currentState := l.InitialState()
    53  		up, err := ledger.NewEmptyUpdate(currentState)
    54  		require.NoError(t, err)
    55  
    56  		newState, trieUpdate, err := l.Set(up)
    57  		require.NoError(t, err)
    58  		require.True(t, trieUpdate.IsEmpty())
    59  
    60  		// state shouldn't change
    61  		assert.Equal(t, currentState, newState)
    62  	})
    63  
    64  	t.Run("non-empty update and query", func(t *testing.T) {
    65  
    66  		// UpdateFixture
    67  		wal := &fixtures.NoopWAL{}
    68  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
    69  		require.NoError(t, err)
    70  
    71  		compactor := fixtures.NewNoopCompactor(led)
    72  		<-compactor.Ready()
    73  		defer func() {
    74  			<-led.Done()
    75  			<-compactor.Done()
    76  		}()
    77  
    78  		curSC := led.InitialState()
    79  
    80  		u := testutils.UpdateFixture()
    81  		u.SetState(curSC)
    82  
    83  		newSc, _, err := led.Set(u)
    84  		require.NoError(t, err)
    85  		assert.NotEqual(t, curSC, newSc)
    86  
    87  		q, err := ledger.NewQuery(newSc, u.Keys())
    88  		require.NoError(t, err)
    89  
    90  		retValues, err := led.Get(q)
    91  		require.NoError(t, err)
    92  
    93  		for i, v := range u.Values() {
    94  			assert.Equal(t, v, retValues[i])
    95  		}
    96  	})
    97  }
    98  
    99  func TestLedger_Get(t *testing.T) {
   100  	t.Run("empty query", func(t *testing.T) {
   101  
   102  		wal := &fixtures.NoopWAL{}
   103  
   104  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   105  		require.NoError(t, err)
   106  
   107  		compactor := fixtures.NewNoopCompactor(led)
   108  		<-compactor.Ready()
   109  		defer func() {
   110  			<-led.Done()
   111  			<-compactor.Done()
   112  		}()
   113  
   114  		curSC := led.InitialState()
   115  		q, err := ledger.NewEmptyQuery(curSC)
   116  		require.NoError(t, err)
   117  
   118  		retValues, err := led.Get(q)
   119  		require.NoError(t, err)
   120  		assert.Equal(t, len(retValues), 0)
   121  	})
   122  
   123  	t.Run("empty keys", func(t *testing.T) {
   124  
   125  		wal := &fixtures.NoopWAL{}
   126  
   127  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   128  		require.NoError(t, err)
   129  
   130  		compactor := fixtures.NewNoopCompactor(led)
   131  		<-compactor.Ready()
   132  		defer func() {
   133  			<-led.Done()
   134  			<-compactor.Done()
   135  		}()
   136  
   137  		curS := led.InitialState()
   138  
   139  		q := testutils.QueryFixture()
   140  		q.SetState(curS)
   141  
   142  		retValues, err := led.Get(q)
   143  		require.NoError(t, err)
   144  
   145  		assert.Equal(t, 2, len(retValues))
   146  		assert.Equal(t, 0, len(retValues[0]))
   147  		assert.Equal(t, 0, len(retValues[1]))
   148  
   149  	})
   150  }
   151  
   152  // TestLedger_GetSingleValue tests reading value from a single path.
   153  func TestLedger_GetSingleValue(t *testing.T) {
   154  
   155  	wal := &fixtures.NoopWAL{}
   156  	led, err := complete.NewLedger(
   157  		wal,
   158  		100,
   159  		&metrics.NoopCollector{},
   160  		zerolog.Logger{},
   161  		complete.DefaultPathFinderVersion,
   162  	)
   163  	require.NoError(t, err)
   164  
   165  	compactor := fixtures.NewNoopCompactor(led)
   166  	<-compactor.Ready()
   167  	defer func() {
   168  		<-led.Done()
   169  		<-compactor.Done()
   170  	}()
   171  
   172  	state := led.InitialState()
   173  
   174  	t.Run("non-existent key", func(t *testing.T) {
   175  
   176  		keys := testutils.RandomUniqueKeys(10, 2, 1, 10)
   177  
   178  		for _, k := range keys {
   179  			qs, err := ledger.NewQuerySingleValue(state, k)
   180  			require.NoError(t, err)
   181  
   182  			retValue, err := led.GetSingleValue(qs)
   183  			require.NoError(t, err)
   184  			assert.Equal(t, 0, len(retValue))
   185  		}
   186  	})
   187  
   188  	t.Run("existent key", func(t *testing.T) {
   189  
   190  		u := testutils.UpdateFixture()
   191  		u.SetState(state)
   192  
   193  		newState, _, err := led.Set(u)
   194  		require.NoError(t, err)
   195  		assert.NotEqual(t, state, newState)
   196  
   197  		for i, k := range u.Keys() {
   198  			q, err := ledger.NewQuerySingleValue(newState, k)
   199  			require.NoError(t, err)
   200  
   201  			retValue, err := led.GetSingleValue(q)
   202  			require.NoError(t, err)
   203  			assert.Equal(t, u.Values()[i], retValue)
   204  		}
   205  	})
   206  
   207  	t.Run("mix of existent and non-existent keys", func(t *testing.T) {
   208  
   209  		u := testutils.UpdateFixture()
   210  		u.SetState(state)
   211  
   212  		newState, _, err := led.Set(u)
   213  		require.NoError(t, err)
   214  		assert.NotEqual(t, state, newState)
   215  
   216  		// Save expected values for existent keys
   217  		expectedValues := make(map[string]ledger.Value)
   218  		for i, key := range u.Keys() {
   219  			encKey := ledger.EncodeKey(&key)
   220  			expectedValues[string(encKey)] = u.Values()[i]
   221  		}
   222  
   223  		// Create a randomly ordered mix of existent and non-existent keys
   224  		var queryKeys []ledger.Key
   225  		queryKeys = append(queryKeys, u.Keys()...)
   226  		queryKeys = append(queryKeys, testutils.RandomUniqueKeys(10, 2, 1, 10)...)
   227  
   228  		rand.Shuffle(len(queryKeys), func(i, j int) {
   229  			queryKeys[i], queryKeys[j] = queryKeys[j], queryKeys[i]
   230  		})
   231  
   232  		for _, k := range queryKeys {
   233  			qs, err := ledger.NewQuerySingleValue(newState, k)
   234  			require.NoError(t, err)
   235  
   236  			retValue, err := led.GetSingleValue(qs)
   237  			require.NoError(t, err)
   238  
   239  			encKey := ledger.EncodeKey(&k)
   240  			if value, ok := expectedValues[string(encKey)]; ok {
   241  				require.Equal(t, value, retValue)
   242  			} else {
   243  				require.Equal(t, 0, len(retValue))
   244  			}
   245  		}
   246  	})
   247  }
   248  
   249  func TestLedgerValueSizes(t *testing.T) {
   250  	t.Run("empty query", func(t *testing.T) {
   251  
   252  		wal := &fixtures.NoopWAL{}
   253  		led, err := complete.NewLedger(
   254  			wal,
   255  			100,
   256  			&metrics.NoopCollector{},
   257  			zerolog.Logger{},
   258  			complete.DefaultPathFinderVersion,
   259  		)
   260  		require.NoError(t, err)
   261  
   262  		compactor := fixtures.NewNoopCompactor(led)
   263  		<-compactor.Ready()
   264  		defer func() {
   265  			<-led.Done()
   266  			<-compactor.Done()
   267  		}()
   268  
   269  		curState := led.InitialState()
   270  		q, err := ledger.NewEmptyQuery(curState)
   271  		require.NoError(t, err)
   272  
   273  		retSizes, err := led.ValueSizes(q)
   274  		require.NoError(t, err)
   275  		require.Equal(t, 0, len(retSizes))
   276  	})
   277  
   278  	t.Run("non-existent keys", func(t *testing.T) {
   279  
   280  		wal := &fixtures.NoopWAL{}
   281  		led, err := complete.NewLedger(
   282  			wal,
   283  			100,
   284  			&metrics.NoopCollector{},
   285  			zerolog.Logger{},
   286  			complete.DefaultPathFinderVersion,
   287  		)
   288  		require.NoError(t, err)
   289  
   290  		compactor := fixtures.NewNoopCompactor(led)
   291  		<-compactor.Ready()
   292  		defer func() {
   293  			<-led.Done()
   294  			<-compactor.Done()
   295  		}()
   296  
   297  		curState := led.InitialState()
   298  		q := testutils.QueryFixture()
   299  		q.SetState(curState)
   300  
   301  		retSizes, err := led.ValueSizes(q)
   302  		require.NoError(t, err)
   303  		require.Equal(t, len(q.Keys()), len(retSizes))
   304  		for _, size := range retSizes {
   305  			assert.Equal(t, 0, size)
   306  		}
   307  	})
   308  
   309  	t.Run("existent keys", func(t *testing.T) {
   310  
   311  		wal := &fixtures.NoopWAL{}
   312  		led, err := complete.NewLedger(
   313  			wal,
   314  			100,
   315  			&metrics.NoopCollector{},
   316  			zerolog.Logger{},
   317  			complete.DefaultPathFinderVersion,
   318  		)
   319  		require.NoError(t, err)
   320  
   321  		compactor := fixtures.NewNoopCompactor(led)
   322  		<-compactor.Ready()
   323  		defer func() {
   324  			<-led.Done()
   325  			<-compactor.Done()
   326  		}()
   327  
   328  		curState := led.InitialState()
   329  		u := testutils.UpdateFixture()
   330  		u.SetState(curState)
   331  
   332  		newState, _, err := led.Set(u)
   333  		require.NoError(t, err)
   334  		assert.NotEqual(t, curState, newState)
   335  
   336  		q, err := ledger.NewQuery(newState, u.Keys())
   337  		require.NoError(t, err)
   338  
   339  		retSizes, err := led.ValueSizes(q)
   340  		require.NoError(t, err)
   341  		require.Equal(t, len(q.Keys()), len(retSizes))
   342  		for i, size := range retSizes {
   343  			assert.Equal(t, u.Values()[i].Size(), size)
   344  		}
   345  	})
   346  
   347  	t.Run("mix of existent and non-existent keys", func(t *testing.T) {
   348  
   349  		wal := &fixtures.NoopWAL{}
   350  		led, err := complete.NewLedger(
   351  			wal,
   352  			100,
   353  			&metrics.NoopCollector{},
   354  			zerolog.Logger{},
   355  			complete.DefaultPathFinderVersion,
   356  		)
   357  		require.NoError(t, err)
   358  
   359  		compactor := fixtures.NewNoopCompactor(led)
   360  		<-compactor.Ready()
   361  		defer func() {
   362  			<-led.Done()
   363  			<-compactor.Done()
   364  		}()
   365  
   366  		curState := led.InitialState()
   367  		u := testutils.UpdateFixture()
   368  		u.SetState(curState)
   369  
   370  		newState, _, err := led.Set(u)
   371  		require.NoError(t, err)
   372  		assert.NotEqual(t, curState, newState)
   373  
   374  		// Save expected value sizes for existent keys
   375  		expectedValueSizes := make(map[string]int)
   376  		for i, key := range u.Keys() {
   377  			encKey := ledger.EncodeKey(&key)
   378  			expectedValueSizes[string(encKey)] = len(u.Values()[i])
   379  		}
   380  
   381  		// Create a randomly ordered mix of existent and non-existent keys
   382  		var queryKeys []ledger.Key
   383  		queryKeys = append(queryKeys, u.Keys()...)
   384  		queryKeys = append(queryKeys, testutils.RandomUniqueKeys(10, 2, 1, 10)...)
   385  
   386  		rand.Shuffle(len(queryKeys), func(i, j int) {
   387  			queryKeys[i], queryKeys[j] = queryKeys[j], queryKeys[i]
   388  		})
   389  
   390  		q, err := ledger.NewQuery(newState, queryKeys)
   391  		require.NoError(t, err)
   392  
   393  		retSizes, err := led.ValueSizes(q)
   394  		require.NoError(t, err)
   395  		require.Equal(t, len(q.Keys()), len(retSizes))
   396  		for i, key := range q.Keys() {
   397  			encKey := ledger.EncodeKey(&key)
   398  			assert.Equal(t, expectedValueSizes[string(encKey)], retSizes[i])
   399  		}
   400  	})
   401  }
   402  
   403  func TestLedger_Proof(t *testing.T) {
   404  	t.Run("empty query", func(t *testing.T) {
   405  		wal := &fixtures.NoopWAL{}
   406  
   407  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   408  		require.NoError(t, err)
   409  
   410  		compactor := fixtures.NewNoopCompactor(led)
   411  		<-compactor.Ready()
   412  		defer func() {
   413  			<-led.Done()
   414  			<-compactor.Done()
   415  		}()
   416  
   417  		curSC := led.InitialState()
   418  		q, err := ledger.NewEmptyQuery(curSC)
   419  		require.NoError(t, err)
   420  
   421  		retProof, err := led.Prove(q)
   422  		require.NoError(t, err)
   423  
   424  		proof, err := ledger.DecodeTrieBatchProof(retProof)
   425  		require.NoError(t, err)
   426  		assert.Equal(t, 0, len(proof.Proofs))
   427  	})
   428  
   429  	t.Run("non-existing keys", func(t *testing.T) {
   430  
   431  		wal := &fixtures.NoopWAL{}
   432  
   433  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   434  		require.NoError(t, err)
   435  
   436  		compactor := fixtures.NewNoopCompactor(led)
   437  		<-compactor.Ready()
   438  		defer func() {
   439  			<-led.Done()
   440  			<-compactor.Done()
   441  		}()
   442  
   443  		curS := led.InitialState()
   444  		q := testutils.QueryFixture()
   445  		q.SetState(curS)
   446  		require.NoError(t, err)
   447  
   448  		retProof, err := led.Prove(q)
   449  		require.NoError(t, err)
   450  
   451  		trieProof, err := ledger.DecodeTrieBatchProof(retProof)
   452  		require.NoError(t, err)
   453  		assert.Equal(t, 2, len(trieProof.Proofs))
   454  		assert.True(t, proof.VerifyTrieBatchProof(trieProof, curS))
   455  
   456  	})
   457  
   458  	t.Run("existing keys", func(t *testing.T) {
   459  
   460  		wal := &fixtures.NoopWAL{}
   461  		led, err := complete.NewLedger(wal, 100, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   462  		require.NoError(t, err)
   463  
   464  		compactor := fixtures.NewNoopCompactor(led)
   465  		<-compactor.Ready()
   466  		defer func() {
   467  			<-led.Done()
   468  			<-compactor.Done()
   469  		}()
   470  
   471  		curS := led.InitialState()
   472  
   473  		u := testutils.UpdateFixture()
   474  		u.SetState(curS)
   475  
   476  		newSc, _, err := led.Set(u)
   477  		require.NoError(t, err)
   478  		assert.NotEqual(t, curS, newSc)
   479  
   480  		q, err := ledger.NewQuery(newSc, u.Keys())
   481  		require.NoError(t, err)
   482  
   483  		retProof, err := led.Prove(q)
   484  		require.NoError(t, err)
   485  
   486  		trieProof, err := ledger.DecodeTrieBatchProof(retProof)
   487  		require.NoError(t, err)
   488  		assert.Equal(t, 2, len(trieProof.Proofs))
   489  		assert.True(t, proof.VerifyTrieBatchProof(trieProof, newSc))
   490  	})
   491  }
   492  
   493  func Test_WAL(t *testing.T) {
   494  	const (
   495  		numInsPerStep      = 2
   496  		keyNumberOfParts   = 10
   497  		keyPartMinByteSize = 1
   498  		keyPartMaxByteSize = 100
   499  		valueMaxByteSize   = 2 << 16 //16kB
   500  		size               = 10
   501  		checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation.
   502  		checkpointsToKeep  = 1
   503  	)
   504  
   505  	metricsCollector := &metrics.NoopCollector{}
   506  	logger := zerolog.Logger{}
   507  
   508  	unittest.RunWithTempDir(t, func(dir string) {
   509  
   510  		diskWal, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, size, pathfinder.PathByteSize, wal.SegmentSize)
   511  		require.NoError(t, err)
   512  
   513  		// cache size intentionally is set to size to test deletion
   514  		led, err := complete.NewLedger(diskWal, size, metricsCollector, logger, complete.DefaultPathFinderVersion)
   515  		require.NoError(t, err)
   516  
   517  		compactor, err := complete.NewCompactor(led, diskWal, zerolog.Nop(), size, checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector())
   518  		require.NoError(t, err)
   519  
   520  		<-compactor.Ready()
   521  
   522  		var state = led.InitialState()
   523  
   524  		//saved data after updates
   525  		savedData := make(map[string]map[string]ledger.Value)
   526  
   527  		for i := 0; i < size; i++ {
   528  
   529  			keys := testutils.RandomUniqueKeys(numInsPerStep, keyNumberOfParts, keyPartMinByteSize, keyPartMaxByteSize)
   530  			values := testutils.RandomValues(numInsPerStep, 1, valueMaxByteSize)
   531  			update, err := ledger.NewUpdate(state, keys, values)
   532  			assert.NoError(t, err)
   533  			state, _, err = led.Set(update)
   534  			require.NoError(t, err)
   535  
   536  			data := make(map[string]ledger.Value, len(keys))
   537  			for j, key := range keys {
   538  				encKey := ledger.EncodeKey(&key)
   539  				data[string(encKey)] = values[j]
   540  			}
   541  
   542  			savedData[string(state[:])] = data
   543  		}
   544  
   545  		<-led.Done()
   546  		<-compactor.Done()
   547  
   548  		diskWal2, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, size, pathfinder.PathByteSize, wal.SegmentSize)
   549  		require.NoError(t, err)
   550  
   551  		led2, err := complete.NewLedger(diskWal2, size+10, metricsCollector, logger, complete.DefaultPathFinderVersion)
   552  		require.NoError(t, err)
   553  
   554  		compactor2, err := complete.NewCompactor(led2, diskWal2, zerolog.Nop(), uint(size), checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector())
   555  		require.NoError(t, err)
   556  
   557  		<-compactor2.Ready()
   558  
   559  		// random map iteration order is a benefit here
   560  		for state, data := range savedData {
   561  
   562  			keys := make([]ledger.Key, 0, len(data))
   563  			for encKey := range data {
   564  				key, err := ledger.DecodeKey([]byte(encKey))
   565  				assert.NoError(t, err)
   566  				keys = append(keys, *key)
   567  			}
   568  
   569  			var ledgerState ledger.State
   570  			copy(ledgerState[:], state)
   571  			query, err := ledger.NewQuery(ledgerState, keys)
   572  			assert.NoError(t, err)
   573  			registerValues, err := led2.Get(query)
   574  			require.NoError(t, err)
   575  
   576  			for i, key := range keys {
   577  				registerValue := registerValues[i]
   578  				encKey := ledger.EncodeKey(&key)
   579  				assert.True(t, data[string(encKey)].Equals(registerValue))
   580  			}
   581  		}
   582  
   583  		<-led2.Done()
   584  		<-compactor2.Done()
   585  	})
   586  }
   587  
   588  func TestLedgerFunctionality(t *testing.T) {
   589  	const (
   590  		checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation.
   591  		checkpointsToKeep  = 1
   592  	)
   593  
   594  	// You can manually increase this for more coverage
   595  	experimentRep := 2
   596  	metricsCollector := &metrics.NoopCollector{}
   597  	logger := zerolog.Logger{}
   598  
   599  	for e := 0; e < experimentRep; e++ {
   600  		numInsPerStep := 100
   601  		numHistLookupPerStep := 10
   602  		keyNumberOfParts := 10
   603  		keyPartMinByteSize := 1
   604  		keyPartMaxByteSize := 100
   605  		stateComSize := 32
   606  		valueMaxByteSize := 2 << 16 //16kB
   607  		activeTries := 1000
   608  		steps := 40                                  // number of steps
   609  		histStorage := make(map[string]ledger.Value) // historic storage string(key, state) -> value
   610  		latestValue := make(map[string]ledger.Value) // key to value
   611  		unittest.RunWithTempDir(t, func(dbDir string) {
   612  			diskWal, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dbDir, activeTries, pathfinder.PathByteSize, wal.SegmentSize)
   613  			require.NoError(t, err)
   614  			led, err := complete.NewLedger(diskWal, activeTries, metricsCollector, logger, complete.DefaultPathFinderVersion)
   615  			assert.NoError(t, err)
   616  			compactor, err := complete.NewCompactor(led, diskWal, zerolog.Nop(), uint(activeTries), checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector())
   617  			require.NoError(t, err)
   618  			<-compactor.Ready()
   619  
   620  			state := led.InitialState()
   621  			for i := 0; i < steps; i++ {
   622  				// add new keys
   623  				// TODO update some of the existing keys and shuffle them
   624  				keys := testutils.RandomUniqueKeys(numInsPerStep, keyNumberOfParts, keyPartMinByteSize, keyPartMaxByteSize)
   625  				values := testutils.RandomValues(numInsPerStep, 1, valueMaxByteSize)
   626  				update, err := ledger.NewUpdate(state, keys, values)
   627  				assert.NoError(t, err)
   628  				newState, _, err := led.Set(update)
   629  				assert.NoError(t, err)
   630  
   631  				// capture new values for future query
   632  				for j, k := range keys {
   633  					encKey := ledger.EncodeKey(&k)
   634  					histStorage[string(newState[:])+string(encKey)] = values[j]
   635  					latestValue[string(encKey)] = values[j]
   636  				}
   637  
   638  				// read values and compare values
   639  				query, err := ledger.NewQuery(newState, keys)
   640  				assert.NoError(t, err)
   641  				retValues, err := led.Get(query)
   642  				assert.NoError(t, err)
   643  				// byte{} is returned as nil
   644  				assert.True(t, valuesMatches(values, retValues))
   645  
   646  				// get value sizes and compare them
   647  				retSizes, err := led.ValueSizes(query)
   648  				assert.NoError(t, err)
   649  				assert.Equal(t, len(query.Keys()), len(retSizes))
   650  				for i, size := range retSizes {
   651  					assert.Equal(t, values[i].Size(), size)
   652  				}
   653  
   654  				// validate proofs (check individual proof and batch proof)
   655  				proofs, err := led.Prove(query)
   656  				assert.NoError(t, err)
   657  
   658  				bProof, err := ledger.DecodeTrieBatchProof(proofs)
   659  				assert.NoError(t, err)
   660  
   661  				// validate batch proofs
   662  				isValid := proof.VerifyTrieBatchProof(bProof, newState)
   663  				assert.True(t, isValid)
   664  
   665  				// validate proofs as a batch
   666  				_, err = ptrie.NewPSMT(ledger.RootHash(newState), bProof)
   667  				assert.NoError(t, err)
   668  
   669  				// query all exising keys (check no drop)
   670  				for ek, v := range latestValue {
   671  					k, err := ledger.DecodeKey([]byte(ek))
   672  					assert.NoError(t, err)
   673  					query, err := ledger.NewQuery(newState, []ledger.Key{*k})
   674  					assert.NoError(t, err)
   675  					rv, err := led.Get(query)
   676  					assert.NoError(t, err)
   677  					assert.True(t, v.Equals(rv[0]))
   678  				}
   679  
   680  				// query some of historic values (map return is random)
   681  				j := 0
   682  				for s := range histStorage {
   683  					value := histStorage[s]
   684  					var state ledger.State
   685  					copy(state[:], s[:stateComSize])
   686  					enk := []byte(s[stateComSize:])
   687  					key, err := ledger.DecodeKey(enk)
   688  					assert.NoError(t, err)
   689  					query, err := ledger.NewQuery(state, []ledger.Key{*key})
   690  					assert.NoError(t, err)
   691  					rv, err := led.Get(query)
   692  					assert.NoError(t, err)
   693  					assert.True(t, value.Equals(rv[0]))
   694  					j++
   695  					if j >= numHistLookupPerStep {
   696  						break
   697  					}
   698  				}
   699  				state = newState
   700  			}
   701  			<-led.Done()
   702  			<-compactor.Done()
   703  		})
   704  	}
   705  }
   706  
   707  func TestWALUpdateFailuresBubbleUp(t *testing.T) {
   708  	unittest.RunWithTempDir(t, func(dir string) {
   709  
   710  		const (
   711  			capacity           = 100
   712  			checkpointDistance = math.MaxInt // A large number to prevent checkpoint creation.
   713  			checkpointsToKeep  = 1
   714  		)
   715  
   716  		theError := fmt.Errorf("error error")
   717  
   718  		metricsCollector := &metrics.NoopCollector{}
   719  
   720  		diskWAL, err := wal.NewDiskWAL(zerolog.Nop(), nil, metricsCollector, dir, capacity, pathfinder.PathByteSize, wal.SegmentSize)
   721  		require.NoError(t, err)
   722  
   723  		w := &CustomUpdateWAL{
   724  			DiskWAL: diskWAL,
   725  			updateFn: func(update *ledger.TrieUpdate) (int, bool, error) {
   726  				return 0, false, theError
   727  			},
   728  		}
   729  
   730  		led, err := complete.NewLedger(w, capacity, &metrics.NoopCollector{}, zerolog.Logger{}, complete.DefaultPathFinderVersion)
   731  		require.NoError(t, err)
   732  
   733  		compactor, err := complete.NewCompactor(led, w, zerolog.Nop(), capacity, checkpointDistance, checkpointsToKeep, atomic.NewBool(false), metrics.NewNoopCollector())
   734  		require.NoError(t, err)
   735  
   736  		<-compactor.Ready()
   737  
   738  		defer func() {
   739  			<-led.Done()
   740  			<-compactor.Done()
   741  		}()
   742  
   743  		key := ledger.NewKey([]ledger.KeyPart{ledger.NewKeyPart(0, []byte{1, 2, 3})})
   744  
   745  		values := []ledger.Value{[]byte{1, 2, 3}}
   746  		update, err := ledger.NewUpdate(led.InitialState(), []ledger.Key{key}, values)
   747  		require.NoError(t, err)
   748  
   749  		_, _, err = led.Set(update)
   750  		require.Error(t, err)
   751  		require.True(t, errors.Is(err, theError))
   752  	})
   753  }
   754  
   755  func valuesMatches(expected []ledger.Value, got []ledger.Value) bool {
   756  	if len(expected) != len(got) {
   757  		return false
   758  	}
   759  	// replace nils
   760  	for i, v := range got {
   761  		if v == nil {
   762  			got[i] = []byte{}
   763  		}
   764  		if !bytes.Equal(expected[i], got[i]) {
   765  			return false
   766  		}
   767  	}
   768  	return true
   769  }
   770  
   771  func noOpMigration(p []ledger.Payload) ([]ledger.Payload, error) {
   772  	return p, nil
   773  }
   774  
   775  func migrationByValue(p []ledger.Payload) ([]ledger.Payload, error) {
   776  	ret := make([]ledger.Payload, 0, len(p))
   777  	for _, p := range p {
   778  		if p.Value().Equals([]byte{'A'}) {
   779  			k, err := p.Key()
   780  			if err != nil {
   781  				return nil, err
   782  			}
   783  			pp := *ledger.NewPayload(k, ledger.Value([]byte{'C'}))
   784  			ret = append(ret, pp)
   785  		} else {
   786  			ret = append(ret, p)
   787  		}
   788  	}
   789  	return ret, nil
   790  }
   791  
   792  type CustomUpdateWAL struct {
   793  	*wal.DiskWAL
   794  	updateFn func(update *ledger.TrieUpdate) (int, bool, error)
   795  }
   796  
   797  func (w *CustomUpdateWAL) RecordUpdate(update *ledger.TrieUpdate) (int, bool, error) {
   798  	return w.updateFn(update)
   799  }
   800  
   801  func migrationByKey(p []ledger.Payload) ([]ledger.Payload, error) {
   802  	ret := make([]ledger.Payload, 0, len(p))
   803  	for _, p := range p {
   804  		k, err := p.Key()
   805  		if err != nil {
   806  			return nil, err
   807  		}
   808  		if k.String() == "/1/1/22/2" {
   809  			pp := *ledger.NewPayload(k, ledger.Value([]byte{'D'}))
   810  			ret = append(ret, pp)
   811  		} else {
   812  			ret = append(ret, p)
   813  		}
   814  	}
   815  
   816  	return ret, nil
   817  }