github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/state/state_test.go (about) 1 package state_test 2 3 import ( 4 "errors" 5 "fmt" 6 "testing" 7 8 "github.com/dgraph-io/badger/v2" 9 "github.com/rs/zerolog" 10 "github.com/stretchr/testify/require" 11 12 "github.com/onflow/flow-go/engine/execution/state" 13 "github.com/onflow/flow-go/engine/execution/storehouse" 14 "github.com/onflow/flow-go/fvm/storage/snapshot" 15 led "github.com/onflow/flow-go/ledger" 16 ledger "github.com/onflow/flow-go/ledger/complete" 17 "github.com/onflow/flow-go/ledger/complete/wal/fixtures" 18 "github.com/onflow/flow-go/model/flow" 19 "github.com/onflow/flow-go/module/metrics" 20 "github.com/onflow/flow-go/module/trace" 21 storageerr "github.com/onflow/flow-go/storage" 22 storage "github.com/onflow/flow-go/storage/mock" 23 "github.com/onflow/flow-go/utils/unittest" 24 ) 25 26 func prepareTest(f func(t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, commits *storage.Commits)) func(*testing.T) { 27 return func(t *testing.T) { 28 unittest.RunWithBadgerDB(t, func(badgerDB *badger.DB) { 29 metricsCollector := &metrics.NoopCollector{} 30 diskWal := &fixtures.NoopWAL{} 31 ls, err := ledger.NewLedger(diskWal, 100, metricsCollector, zerolog.Nop(), ledger.DefaultPathFinderVersion) 32 require.NoError(t, err) 33 compactor := fixtures.NewNoopCompactor(ls) 34 <-compactor.Ready() 35 defer func() { 36 <-ls.Done() 37 <-compactor.Done() 38 }() 39 40 stateCommitments := storage.NewCommits(t) 41 headers := storage.NewHeaders(t) 42 blocks := storage.NewBlocks(t) 43 collections := storage.NewCollections(t) 44 events := storage.NewEvents(t) 45 serviceEvents := storage.NewServiceEvents(t) 46 txResults := storage.NewTransactionResults(t) 47 chunkDataPacks := storage.NewChunkDataPacks(t) 48 results := storage.NewExecutionResults(t) 49 myReceipts := storage.NewMyExecutionReceipts(t) 50 51 es := state.NewExecutionState( 52 ls, stateCommitments, blocks, headers, collections, chunkDataPacks, results, myReceipts, events, serviceEvents, txResults, badgerDB, trace.NewNoopTracer(), 53 nil, 54 false, 55 ) 56 57 f(t, es, ls, headers, stateCommitments) 58 }) 59 } 60 } 61 62 func TestExecutionStateWithTrieStorage(t *testing.T) { 63 t.Run("commit write and read new state", prepareTest(func( 64 t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits) { 65 header1 := unittest.BlockHeaderFixture() 66 sc1 := flow.StateCommitment(l.InitialState()) 67 68 reg1 := unittest.MakeOwnerReg("fruit", "apple") 69 reg2 := unittest.MakeOwnerReg("vegetable", "carrot") 70 executionSnapshot := &snapshot.ExecutionSnapshot{ 71 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 72 reg1.Key: reg1.Value, 73 reg2.Key: reg2.Value, 74 }, 75 } 76 77 sc2, update, sc2Snapshot, err := state.CommitDelta(l, executionSnapshot, 78 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1)) 79 require.NoError(t, err) 80 81 // validate new snapshot 82 val, err := sc2Snapshot.Get(reg1.Key) 83 require.NoError(t, err) 84 require.Equal(t, reg1.Value, val) 85 86 val, err = sc2Snapshot.Get(reg2.Key) 87 require.NoError(t, err) 88 require.Equal(t, reg2.Value, val) 89 90 require.Equal(t, sc1[:], update.RootHash[:]) 91 require.Len(t, update.Paths, 2) 92 require.Len(t, update.Payloads, 2) 93 94 // validate sc2 95 require.Equal(t, sc2, sc2Snapshot.Commitment()) 96 validateUpdate(t, update, sc1, executionSnapshot) 97 98 header2 := unittest.BlockHeaderWithParentFixture(header1) 99 storageSnapshot := es.NewStorageSnapshot(sc2, header2.ID(), header2.Height) 100 101 b1, err := storageSnapshot.Get(reg1.Key) 102 require.NoError(t, err) 103 b2, err := storageSnapshot.Get(reg2.Key) 104 require.NoError(t, err) 105 106 require.Equal(t, flow.RegisterValue("apple"), b1) 107 require.Equal(t, flow.RegisterValue("carrot"), b2) 108 109 // verify has state 110 require.True(t, l.HasState(led.State(sc2))) 111 require.False(t, l.HasState(led.State(unittest.StateCommitmentFixture()))) 112 })) 113 114 t.Run("commit write and read previous state", prepareTest(func( 115 t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits) { 116 header1 := unittest.BlockHeaderFixture() 117 sc1 := flow.StateCommitment(l.InitialState()) 118 119 reg1 := unittest.MakeOwnerReg("fruit", "apple") 120 executionSnapshot1 := &snapshot.ExecutionSnapshot{ 121 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 122 reg1.Key: reg1.Value, 123 }, 124 } 125 126 sc2, _, sc2Snapshot, err := state.CommitDelta(l, executionSnapshot1, 127 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1), 128 ) 129 require.NoError(t, err) 130 131 // update value and get resulting state commitment 132 executionSnapshot2 := &snapshot.ExecutionSnapshot{ 133 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 134 reg1.Key: flow.RegisterValue("orange"), 135 }, 136 } 137 138 sc3, _, _, err := state.CommitDelta(l, executionSnapshot2, sc2Snapshot) 139 require.NoError(t, err) 140 141 header2 := unittest.BlockHeaderWithParentFixture(header1) 142 // create a view for previous state version 143 storageSnapshot3 := es.NewStorageSnapshot(sc2, header2.ID(), header2.Height) 144 145 header3 := unittest.BlockHeaderWithParentFixture(header1) 146 // create a view for new state version 147 storageSnapshot4 := es.NewStorageSnapshot(sc3, header3.ID(), header3.Height) 148 149 // header2 and header3 are different blocks 150 require.True(t, header2.ID() != (header3.ID())) 151 152 // fetch the value at both versions 153 b1, err := storageSnapshot3.Get(reg1.Key) 154 require.NoError(t, err) 155 156 b2, err := storageSnapshot4.Get(reg1.Key) 157 require.NoError(t, err) 158 159 require.Equal(t, flow.RegisterValue("apple"), b1) 160 require.Equal(t, flow.RegisterValue("orange"), b2) 161 })) 162 163 t.Run("commit delta and read new state", prepareTest(func( 164 t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits) { 165 header1 := unittest.BlockHeaderFixture() 166 sc1 := flow.StateCommitment(l.InitialState()) 167 168 reg1 := unittest.MakeOwnerReg("fruit", "apple") 169 reg2 := unittest.MakeOwnerReg("vegetable", "carrot") 170 // set initial value 171 executionSnapshot1 := &snapshot.ExecutionSnapshot{ 172 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 173 reg1.Key: reg1.Value, 174 reg2.Key: reg2.Value, 175 }, 176 } 177 178 sc2, _, sc2Snapshot, err := state.CommitDelta(l, executionSnapshot1, 179 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1), 180 ) 181 require.NoError(t, err) 182 183 // update value and get resulting state commitment 184 executionSnapshot2 := &snapshot.ExecutionSnapshot{ 185 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 186 reg1.Key: nil, 187 }, 188 } 189 190 sc3, _, _, err := state.CommitDelta(l, executionSnapshot2, sc2Snapshot) 191 require.NoError(t, err) 192 193 header2 := unittest.BlockHeaderWithParentFixture(header1) 194 // create a view for previous state version 195 storageSnapshot3 := es.NewStorageSnapshot(sc2, header2.ID(), header2.Height) 196 197 header3 := unittest.BlockHeaderWithParentFixture(header2) 198 // create a view for new state version 199 storageSnapshot4 := es.NewStorageSnapshot(sc3, header3.ID(), header3.Height) 200 201 // fetch the value at both versions 202 b1, err := storageSnapshot3.Get(reg1.Key) 203 require.NoError(t, err) 204 205 b2, err := storageSnapshot4.Get(reg1.Key) 206 require.NoError(t, err) 207 208 require.Equal(t, flow.RegisterValue("apple"), b1) 209 require.Empty(t, b2) 210 })) 211 212 t.Run("commit delta and persist state commit for the second time should be OK", prepareTest(func( 213 t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits) { 214 sc1 := flow.StateCommitment(l.InitialState()) 215 216 reg1 := unittest.MakeOwnerReg("fruit", "apple") 217 reg2 := unittest.MakeOwnerReg("vegetable", "carrot") 218 // set initial value 219 executionSnapshot1 := &snapshot.ExecutionSnapshot{ 220 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 221 reg1.Key: reg1.Value, 222 reg2.Key: reg2.Value, 223 }, 224 } 225 226 sc2, _, _, err := state.CommitDelta(l, executionSnapshot1, 227 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1), 228 ) 229 require.NoError(t, err) 230 231 // committing for the second time should be OK 232 sc2Same, _, _, err := state.CommitDelta(l, executionSnapshot1, 233 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1), 234 ) 235 require.NoError(t, err) 236 237 require.Equal(t, sc2, sc2Same) 238 })) 239 240 t.Run("commit write and create snapshot", prepareTest(func( 241 t *testing.T, es state.ExecutionState, l *ledger.Ledger, headers *storage.Headers, stateCommitments *storage.Commits) { 242 header1 := unittest.BlockHeaderFixture() 243 header2 := unittest.BlockHeaderWithParentFixture(header1) 244 sc1 := flow.StateCommitment(l.InitialState()) 245 246 reg1 := unittest.MakeOwnerReg("fruit", "apple") 247 reg2 := unittest.MakeOwnerReg("vegetable", "carrot") 248 executionSnapshot := &snapshot.ExecutionSnapshot{ 249 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 250 reg1.Key: reg1.Value, 251 reg2.Key: reg2.Value, 252 }, 253 } 254 255 sc2, _, _, err := state.CommitDelta(l, executionSnapshot, 256 storehouse.NewExecutingBlockSnapshot(state.NewLedgerStorageSnapshot(l, sc1), sc1)) 257 require.NoError(t, err) 258 259 // test CreateStorageSnapshot for known and executed block 260 headers.On("ByBlockID", header2.ID()).Return(header2, nil) 261 stateCommitments.On("ByBlockID", header2.ID()).Return(sc2, nil) 262 snapshot2, h2, err := es.CreateStorageSnapshot(header2.ID()) 263 require.NoError(t, err) 264 require.Equal(t, header2.ID(), h2.ID()) 265 266 val, err := snapshot2.Get(reg1.Key) 267 require.NoError(t, err) 268 require.Equal(t, val, reg1.Value) 269 270 val, err = snapshot2.Get(reg2.Key) 271 require.NoError(t, err) 272 require.Equal(t, val, reg2.Value) 273 274 // test CreateStorageSnapshot for unknown block 275 unknown := unittest.BlockHeaderFixture() 276 headers.On("ByBlockID", unknown.ID()).Return(nil, fmt.Errorf("unknown: %w", storageerr.ErrNotFound)) 277 _, _, err = es.CreateStorageSnapshot(unknown.ID()) 278 require.Error(t, err) 279 require.True(t, errors.Is(err, storageerr.ErrNotFound)) 280 281 // test CreateStorageSnapshot for known and unexecuted block 282 unexecuted := unittest.BlockHeaderFixture() 283 headers.On("ByBlockID", unexecuted.ID()).Return(unexecuted, nil) 284 stateCommitments.On("ByBlockID", unexecuted.ID()).Return(nil, fmt.Errorf("not found: %w", storageerr.ErrNotFound)) 285 _, _, err = es.CreateStorageSnapshot(unexecuted.ID()) 286 require.Error(t, err) 287 require.True(t, errors.Is(err, state.ErrNotExecuted)) 288 289 // test CreateStorageSnapshot for pruned block 290 pruned := unittest.BlockHeaderFixture() 291 prunedState := unittest.StateCommitmentFixture() 292 headers.On("ByBlockID", pruned.ID()).Return(pruned, nil) 293 stateCommitments.On("ByBlockID", pruned.ID()).Return(prunedState, nil) 294 _, _, err = es.CreateStorageSnapshot(pruned.ID()) 295 require.Error(t, err) 296 require.True(t, errors.Is(err, state.ErrExecutionStatePruned)) 297 })) 298 299 }