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 }