github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/state/state_storehouse_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/dgraph-io/badger/v2"
     8  	"github.com/ipfs/go-cid"
     9  	"github.com/rs/zerolog"
    10  	"github.com/stretchr/testify/mock"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/onflow/crypto"
    14  
    15  	"github.com/onflow/flow-go/engine/execution"
    16  	"github.com/onflow/flow-go/engine/execution/state"
    17  	"github.com/onflow/flow-go/engine/execution/storehouse"
    18  	"github.com/onflow/flow-go/engine/execution/testutil"
    19  	"github.com/onflow/flow-go/fvm/meter"
    20  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    21  	led "github.com/onflow/flow-go/ledger"
    22  	"github.com/onflow/flow-go/ledger/common/convert"
    23  	"github.com/onflow/flow-go/ledger/common/pathfinder"
    24  	ledger "github.com/onflow/flow-go/ledger/complete"
    25  	"github.com/onflow/flow-go/ledger/complete/wal/fixtures"
    26  	"github.com/onflow/flow-go/model/flow"
    27  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    28  	"github.com/onflow/flow-go/module/mempool/entity"
    29  	"github.com/onflow/flow-go/module/metrics"
    30  	"github.com/onflow/flow-go/module/trace"
    31  	badgerstorage "github.com/onflow/flow-go/storage/badger"
    32  	"github.com/onflow/flow-go/storage/badger/operation"
    33  	storage "github.com/onflow/flow-go/storage/mock"
    34  	"github.com/onflow/flow-go/storage/pebble"
    35  	"github.com/onflow/flow-go/utils/unittest"
    36  )
    37  
    38  func prepareStorehouseTest(f func(t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, commits *storage.Commits, finalized *testutil.MockFinalizedReader)) func(*testing.T) {
    39  	return func(t *testing.T) {
    40  		unittest.RunWithBadgerDB(t, func(badgerDB *badger.DB) {
    41  			metricsCollector := &metrics.NoopCollector{}
    42  			diskWal := &fixtures.NoopWAL{}
    43  			ls, err := ledger.NewLedger(diskWal, 100, metricsCollector, zerolog.Nop(), ledger.DefaultPathFinderVersion)
    44  			require.NoError(t, err)
    45  			compactor := fixtures.NewNoopCompactor(ls)
    46  			<-compactor.Ready()
    47  			defer func() {
    48  				<-ls.Done()
    49  				<-compactor.Done()
    50  			}()
    51  
    52  			stateCommitments := storage.NewCommits(t)
    53  			stateCommitments.On("BatchStore", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    54  			headers := storage.NewHeaders(t)
    55  			blocks := storage.NewBlocks(t)
    56  			collections := storage.NewCollections(t)
    57  			events := storage.NewEvents(t)
    58  			events.On("BatchStore", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    59  			serviceEvents := storage.NewServiceEvents(t)
    60  			serviceEvents.On("BatchStore", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    61  			txResults := storage.NewTransactionResults(t)
    62  			txResults.On("BatchStore", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    63  			chunkDataPacks := storage.NewChunkDataPacks(t)
    64  			chunkDataPacks.On("Store", mock.Anything).Return(nil)
    65  			results := storage.NewExecutionResults(t)
    66  			results.On("BatchStore", mock.Anything, mock.Anything).Return(nil)
    67  			results.On("BatchIndex", mock.Anything, mock.Anything, mock.Anything).Return(nil)
    68  			myReceipts := storage.NewMyExecutionReceipts(t)
    69  			myReceipts.On("BatchStoreMyReceipt", mock.Anything, mock.Anything).Return(nil)
    70  
    71  			withRegisterStore(t, func(t *testing.T,
    72  				rs *storehouse.RegisterStore,
    73  				diskStore execution.OnDiskRegisterStore,
    74  				finalized *testutil.MockFinalizedReader,
    75  				rootHeight uint64,
    76  				endHeight uint64,
    77  				finalizedHeaders map[uint64]*flow.Header,
    78  			) {
    79  
    80  				rootID, err := finalized.FinalizedBlockIDAtHeight(10)
    81  				require.NoError(t, err)
    82  				require.NoError(t,
    83  					badgerDB.Update(operation.InsertExecutedBlock(rootID)),
    84  				)
    85  
    86  				metrics := metrics.NewNoopCollector()
    87  				headersDB := badgerstorage.NewHeaders(metrics, badgerDB)
    88  				require.NoError(t, headersDB.Store(finalizedHeaders[10]))
    89  
    90  				es := state.NewExecutionState(
    91  					ls, stateCommitments, blocks, headers, collections, chunkDataPacks, results, myReceipts, events, serviceEvents, txResults, badgerDB, trace.NewNoopTracer(),
    92  					rs,
    93  					true,
    94  				)
    95  
    96  				f(t, es, ls, headers, stateCommitments, finalized)
    97  
    98  			})
    99  		})
   100  	}
   101  }
   102  
   103  func withRegisterStore(t *testing.T, fn func(
   104  	t *testing.T,
   105  	rs *storehouse.RegisterStore,
   106  	diskStore execution.OnDiskRegisterStore,
   107  	finalized *testutil.MockFinalizedReader,
   108  	rootHeight uint64,
   109  	endHeight uint64,
   110  	headers map[uint64]*flow.Header,
   111  )) {
   112  	// block 10 is executed block
   113  	pebble.RunWithRegistersStorageAtInitialHeights(t, 10, 10, func(diskStore *pebble.Registers) {
   114  		log := unittest.Logger()
   115  		var wal execution.ExecutedFinalizedWAL
   116  		finalized, headerByHeight, highest := testutil.NewMockFinalizedReader(10, 100)
   117  		rs, err := storehouse.NewRegisterStore(diskStore, wal, finalized, log, storehouse.NewNoopNotifier())
   118  		require.NoError(t, err)
   119  		fn(t, rs, diskStore, finalized, 10, highest, headerByHeight)
   120  	})
   121  }
   122  
   123  func TestExecutionStateWithStorehouse(t *testing.T) {
   124  	t.Run("commit write and read new state", prepareStorehouseTest(func(
   125  		t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits, finalized *testutil.MockFinalizedReader) {
   126  
   127  		// block 11 is the block to be executed
   128  		block11 := finalized.BlockAtHeight(11)
   129  		header11 := block11.Header
   130  		sc10 := flow.StateCommitment(l.InitialState())
   131  
   132  		reg1 := unittest.MakeOwnerReg("fruit", "apple")
   133  		reg2 := unittest.MakeOwnerReg("vegetable", "carrot")
   134  		executionSnapshot := &snapshot.ExecutionSnapshot{
   135  			WriteSet: map[flow.RegisterID]flow.RegisterValue{
   136  				reg1.Key: reg1.Value,
   137  				reg2.Key: reg2.Value,
   138  			},
   139  			Meter: meter.NewMeter(meter.DefaultParameters()),
   140  		}
   141  
   142  		// create Block 11's end statecommitment
   143  		sc2, update, sc2Snapshot, err := state.CommitDelta(l, executionSnapshot,
   144  			storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc10), sc10))
   145  		require.NoError(t, err)
   146  
   147  		// validate new snapshot
   148  		val, err := sc2Snapshot.Get(reg1.Key)
   149  		require.NoError(t, err)
   150  		require.Equal(t, reg1.Value, val)
   151  
   152  		val, err = sc2Snapshot.Get(reg2.Key)
   153  		require.NoError(t, err)
   154  		require.Equal(t, reg2.Value, val)
   155  
   156  		validateUpdate(t, update, sc10, executionSnapshot)
   157  
   158  		// validate storage snapshot
   159  		completeBlock := &entity.ExecutableBlock{
   160  			Block:               block11,
   161  			CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{},
   162  			StartState:          &sc10,
   163  			Executing:           false,
   164  		}
   165  
   166  		computationResult := makeComputationResult(t, completeBlock, executionSnapshot, sc2)
   167  
   168  		// save result and store registers
   169  		require.NoError(t, es.SaveExecutionResults(context.Background(), computationResult))
   170  
   171  		storageSnapshot := es.NewStorageSnapshot(sc2, header11.ID(), header11.Height)
   172  
   173  		// validate the storage snapshot has the registers
   174  		b1, err := storageSnapshot.Get(reg1.Key)
   175  		require.NoError(t, err)
   176  		b2, err := storageSnapshot.Get(reg2.Key)
   177  		require.NoError(t, err)
   178  
   179  		require.Equal(t, flow.RegisterValue("apple"), b1)
   180  		require.Equal(t, flow.RegisterValue("carrot"), b2)
   181  
   182  		// verify has state
   183  		require.True(t, l.HasState(led.State(sc2)))
   184  		require.False(t, l.HasState(led.State(unittest.StateCommitmentFixture())))
   185  	}))
   186  }
   187  
   188  func validateUpdate(t *testing.T, update *led.TrieUpdate, commit flow.StateCommitment, executionSnapshot *snapshot.ExecutionSnapshot) {
   189  	require.Equal(t, commit[:], update.RootHash[:])
   190  	require.Len(t, update.Paths, len(executionSnapshot.WriteSet))
   191  	require.Len(t, update.Payloads, len(executionSnapshot.WriteSet))
   192  
   193  	regs := executionSnapshot.UpdatedRegisters()
   194  	for i, reg := range regs {
   195  		key := convert.RegisterIDToLedgerKey(reg.Key)
   196  		path, err := pathfinder.KeyToPath(key, ledger.DefaultPathFinderVersion)
   197  		require.NoError(t, err)
   198  
   199  		require.Equal(t, path, update.Paths[i])
   200  		require.Equal(t, led.Value(reg.Value), update.Payloads[i].Value())
   201  	}
   202  }
   203  
   204  func makeComputationResult(
   205  	t *testing.T,
   206  	completeBlock *entity.ExecutableBlock,
   207  	executionSnapshot *snapshot.ExecutionSnapshot,
   208  	commit flow.StateCommitment,
   209  ) *execution.ComputationResult {
   210  
   211  	computationResult := execution.NewEmptyComputationResult(completeBlock)
   212  	numberOfChunks := 1
   213  	ceds := make([]*execution_data.ChunkExecutionData, numberOfChunks)
   214  	ceds[0] = unittest.ChunkExecutionDataFixture(t, 1024)
   215  	computationResult.CollectionExecutionResultAt(0).UpdateExecutionSnapshot(executionSnapshot)
   216  	computationResult.AppendCollectionAttestationResult(
   217  		*completeBlock.StartState,
   218  		commit,
   219  		nil,
   220  		unittest.IdentifierFixture(),
   221  		ceds[0],
   222  	)
   223  
   224  	bed := unittest.BlockExecutionDataFixture(
   225  		unittest.WithBlockExecutionDataBlockID(completeBlock.Block.ID()),
   226  		unittest.WithChunkExecutionDatas(ceds...),
   227  	)
   228  
   229  	executionDataID, err := execution_data.CalculateID(context.Background(), bed, execution_data.DefaultSerializer)
   230  	require.NoError(t, err)
   231  
   232  	executionResult := flow.NewExecutionResult(
   233  		unittest.IdentifierFixture(),
   234  		completeBlock.ID(),
   235  		computationResult.AllChunks(),
   236  		flow.ServiceEventList{},
   237  		executionDataID)
   238  
   239  	computationResult.BlockAttestationResult.BlockExecutionResult.ExecutionDataRoot = &flow.BlockExecutionDataRoot{
   240  		BlockID:               completeBlock.ID(),
   241  		ChunkExecutionDataIDs: []cid.Cid{flow.IdToCid(unittest.IdentifierFixture())},
   242  	}
   243  
   244  	computationResult.ExecutionReceipt = &flow.ExecutionReceipt{
   245  		ExecutionResult:   *executionResult,
   246  		Spocks:            make([]crypto.Signature, numberOfChunks),
   247  		ExecutorSignature: crypto.Signature{},
   248  	}
   249  	return computationResult
   250  }