github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/chunks/chunkVerifier_test.go (about) 1 package chunks_test 2 3 import ( 4 "testing" 5 6 "github.com/ipfs/go-cid" 7 "github.com/onflow/cadence/runtime" 8 "github.com/rs/zerolog" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 "github.com/stretchr/testify/suite" 13 14 executionState "github.com/onflow/flow-go/engine/execution/state" 15 "github.com/onflow/flow-go/fvm" 16 "github.com/onflow/flow-go/fvm/blueprints" 17 fvmErrors "github.com/onflow/flow-go/fvm/errors" 18 fvmmock "github.com/onflow/flow-go/fvm/mock" 19 "github.com/onflow/flow-go/fvm/storage/snapshot" 20 "github.com/onflow/flow-go/ledger" 21 completeLedger "github.com/onflow/flow-go/ledger/complete" 22 "github.com/onflow/flow-go/ledger/complete/wal/fixtures" 23 chunksmodels "github.com/onflow/flow-go/model/chunks" 24 "github.com/onflow/flow-go/model/convert" 25 "github.com/onflow/flow-go/model/flow" 26 "github.com/onflow/flow-go/model/verification" 27 "github.com/onflow/flow-go/module/chunks" 28 "github.com/onflow/flow-go/module/executiondatasync/execution_data" 29 "github.com/onflow/flow-go/module/executiondatasync/provider" 30 "github.com/onflow/flow-go/module/metrics" 31 "github.com/onflow/flow-go/utils/unittest" 32 ) 33 34 var eventsList = flow.EventsList{ 35 { 36 Type: "event.someType", 37 TransactionID: flow.Identifier{2, 3, 2, 3}, 38 TransactionIndex: 1, 39 EventIndex: 2, 40 Payload: []byte{7, 3, 1, 2}, 41 }, 42 { 43 Type: "event.otherType", 44 TransactionID: flow.Identifier{3, 3, 3}, 45 TransactionIndex: 4, 46 EventIndex: 4, 47 Payload: []byte{7, 3, 1, 2}, 48 }, 49 } 50 51 const computationUsed = uint64(100) 52 53 var id0 = flow.NewRegisterID(unittest.RandomAddressFixture(), "") 54 var id5 = flow.NewRegisterID(unittest.RandomAddressFixture(), "") 55 56 // the chain we use for this test suite 57 var testChain = flow.Emulator 58 var epochSetupEvent, _ = unittest.EpochSetupFixtureByChainID(testChain) 59 var epochCommitEvent, _ = unittest.EpochCommitFixtureByChainID(testChain) 60 61 var systemEventsList = []flow.Event{ 62 epochSetupEvent, 63 } 64 65 var executionDataCIDProvider = provider.NewExecutionDataCIDProvider(execution_data.DefaultSerializer) 66 67 var serviceTxBody *flow.TransactionBody 68 69 type ChunkVerifierTestSuite struct { 70 suite.Suite 71 72 verifier *chunks.ChunkVerifier 73 ledger *completeLedger.Ledger 74 75 snapshots map[string]*snapshot.ExecutionSnapshot 76 outputs map[string]fvm.ProcedureOutput 77 } 78 79 // Make sure variables are set properly 80 // SetupTest is executed prior to each individual test in this test suite 81 func (s *ChunkVerifierTestSuite) SetupSuite() { 82 vmCtx := fvm.NewContext(fvm.WithChain(testChain.Chain())) 83 vmMock := fvmmock.NewVM(s.T()) 84 85 vmMock. 86 On("Run", 87 mock.AnythingOfType("fvm.Context"), 88 mock.AnythingOfType("*fvm.TransactionProcedure"), 89 mock.AnythingOfType("snapshot.SnapshotTree")). 90 Return( 91 func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) *snapshot.ExecutionSnapshot { 92 tx, ok := proc.(*fvm.TransactionProcedure) 93 if !ok { 94 s.Fail("unexpected procedure type") 95 return nil 96 } 97 98 if snapshot, ok := s.snapshots[string(tx.Transaction.Script)]; ok { 99 return snapshot 100 } 101 return generateDefaultSnapshot() 102 }, 103 func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) fvm.ProcedureOutput { 104 tx, ok := proc.(*fvm.TransactionProcedure) 105 if !ok { 106 s.Fail("unexpected procedure type") 107 return fvm.ProcedureOutput{} 108 } 109 110 if output, ok := s.outputs[string(tx.Transaction.Script)]; ok { 111 return output 112 } 113 return generateDefaultOutput() 114 }, 115 func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) error { 116 return nil 117 }, 118 ). 119 Maybe() // don't require for all tests since some never call FVM 120 121 s.verifier = chunks.NewChunkVerifier(vmMock, vmCtx, zerolog.Nop()) 122 123 txBody, err := blueprints.SystemChunkTransaction(testChain.Chain()) 124 require.NoError(s.T(), err) 125 serviceTxBody = txBody 126 } 127 128 func (s *ChunkVerifierTestSuite) SetupTest() { 129 s.ledger = newLedger(s.T()) 130 131 s.snapshots = make(map[string]*snapshot.ExecutionSnapshot) 132 s.outputs = make(map[string]fvm.ProcedureOutput) 133 } 134 135 // TestChunkVerifier invokes all the tests in this test suite 136 func TestChunkVerifier(t *testing.T) { 137 suite.Run(t, new(ChunkVerifierTestSuite)) 138 } 139 140 // TestHappyPath tests verification of the baseline verifiable chunk 141 func (s *ChunkVerifierTestSuite) TestHappyPath() { 142 meta := s.GetTestSetup(s.T(), "", false) 143 vch := meta.RefreshChunkData(s.T()) 144 145 spockSecret, err := s.verifier.Verify(vch) 146 assert.NoError(s.T(), err) 147 assert.NotNil(s.T(), spockSecret) 148 } 149 150 // TestMissingRegisterTouchForUpdate tests verification given a chunkdatapack missing a register touch (update) 151 func (s *ChunkVerifierTestSuite) TestMissingRegisterTouchForUpdate() { 152 unittest.SkipUnless(s.T(), unittest.TEST_DEPRECATED, "Check new partial ledger for missing keys") 153 154 meta := s.GetTestSetup(s.T(), "", false) 155 vch := meta.RefreshChunkData(s.T()) 156 157 // remove the second register touch 158 // vch.ChunkDataPack.RegisterTouches = vch.ChunkDataPack.RegisterTouches[:1] 159 spockSecret, err := s.verifier.Verify(vch) 160 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 161 assert.IsType(s.T(), &chunksmodels.CFMissingRegisterTouch{}, err) 162 assert.Nil(s.T(), spockSecret) 163 } 164 165 // TestMissingRegisterTouchForRead tests verification given a chunkdatapack missing a register touch (read) 166 func (s *ChunkVerifierTestSuite) TestMissingRegisterTouchForRead() { 167 unittest.SkipUnless(s.T(), unittest.TEST_DEPRECATED, "Check new partial ledger for missing keys") 168 169 meta := s.GetTestSetup(s.T(), "", false) 170 vch := meta.RefreshChunkData(s.T()) 171 172 // remove the second register touch 173 // vch.ChunkDataPack.RegisterTouches = vch.ChunkDataPack.RegisterTouches[1:] 174 spockSecret, err := s.verifier.Verify(vch) 175 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 176 assert.IsType(s.T(), &chunksmodels.CFMissingRegisterTouch{}, err) 177 assert.Nil(s.T(), spockSecret) 178 } 179 180 // TestWrongEndState tests verification covering the case 181 // the state commitment computed after updating the partial trie 182 // doesn't match the one provided by the chunks 183 func (s *ChunkVerifierTestSuite) TestWrongEndState() { 184 meta := s.GetTestSetup(s.T(), "wrongEndState", false) 185 vch := meta.RefreshChunkData(s.T()) 186 187 // modify calculated end state, which is different from the one provided by the vch 188 s.snapshots["wrongEndState"] = &snapshot.ExecutionSnapshot{ 189 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 190 id0: []byte{'F'}, 191 }, 192 } 193 194 spockSecret, err := s.verifier.Verify(vch) 195 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 196 assert.IsType(s.T(), &chunksmodels.CFNonMatchingFinalState{}, err) 197 assert.Nil(s.T(), spockSecret) 198 } 199 200 // TestFailedTx tests verification behavior in case 201 // of failed transaction. if a transaction fails, it should 202 // still change the state commitment. 203 func (s *ChunkVerifierTestSuite) TestFailedTx() { 204 meta := s.GetTestSetup(s.T(), "failedTx", false) 205 vch := meta.RefreshChunkData(s.T()) 206 207 // modify the FVM output to include a failing tx. the input already has a failing tx, but we need to 208 s.snapshots["failedTx"] = &snapshot.ExecutionSnapshot{ 209 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 210 id5: []byte{'B'}, 211 }, 212 } 213 s.outputs["failedTx"] = fvm.ProcedureOutput{ 214 ComputationUsed: computationUsed, 215 Err: fvmErrors.NewCadenceRuntimeError(runtime.Error{}), // inside the runtime (e.g. div by zero, access account) 216 } 217 218 spockSecret, err := s.verifier.Verify(vch) 219 assert.NoError(s.T(), err) 220 assert.NotNil(s.T(), spockSecret) 221 } 222 223 // TestEventsMismatch tests verification behavior in case 224 // of emitted events not matching chunks 225 func (s *ChunkVerifierTestSuite) TestEventsMismatch() { 226 meta := s.GetTestSetup(s.T(), "eventsMismatch", false) 227 vch := meta.RefreshChunkData(s.T()) 228 229 // add an additional event to the list of events produced by FVM 230 output := generateDefaultOutput() 231 output.Events = append(eventsList, flow.Event{ 232 Type: "event.Extra", 233 TransactionID: flow.Identifier{2, 3}, 234 TransactionIndex: 0, 235 EventIndex: 0, 236 Payload: []byte{88}, 237 }) 238 s.outputs["eventsMismatch"] = output 239 240 _, err := s.verifier.Verify(vch) 241 assert.Error(s.T(), err) 242 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 243 assert.IsType(s.T(), &chunksmodels.CFInvalidEventsCollection{}, err) 244 } 245 246 // TestServiceEventsMismatch tests verification behavior in case 247 // of emitted service events not matching chunks' 248 func (s *ChunkVerifierTestSuite) TestServiceEventsMismatch() { 249 meta := s.GetTestSetup(s.T(), "doesn't matter", true) 250 vch := meta.RefreshChunkData(s.T()) 251 252 // modify the list of service events produced by FVM 253 // EpochSetup event is expected, but we emit EpochCommit here resulting in a chunk fault 254 epochCommitServiceEvent, err := convert.ServiceEvent(testChain, epochCommitEvent) 255 require.NoError(s.T(), err) 256 257 s.snapshots[string(serviceTxBody.Script)] = &snapshot.ExecutionSnapshot{} 258 s.outputs[string(serviceTxBody.Script)] = fvm.ProcedureOutput{ 259 ComputationUsed: computationUsed, 260 ConvertedServiceEvents: flow.ServiceEventList{*epochCommitServiceEvent}, 261 Events: meta.ChunkEvents, 262 } 263 264 _, err = s.verifier.Verify(vch) 265 assert.Error(s.T(), err) 266 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 267 assert.IsType(s.T(), &chunksmodels.CFInvalidServiceEventsEmitted{}, err) 268 } 269 270 // TestServiceEventsAreChecked ensures that service events are in fact checked 271 func (s *ChunkVerifierTestSuite) TestServiceEventsAreChecked() { 272 meta := s.GetTestSetup(s.T(), "doesn't matter", true) 273 vch := meta.RefreshChunkData(s.T()) 274 275 // setup the verifier output to include the correct data for the service events 276 output := generateDefaultOutput() 277 output.ConvertedServiceEvents = meta.ServiceEvents 278 output.Events = meta.ChunkEvents 279 s.outputs[string(serviceTxBody.Script)] = output 280 281 _, err := s.verifier.Verify(vch) 282 assert.NoError(s.T(), err) 283 } 284 285 // TestSystemChunkWithCollectionFails ensures verification fails for system chunks with collections 286 func (s *ChunkVerifierTestSuite) TestSystemChunkWithCollectionFails() { 287 meta := s.GetTestSetup(s.T(), "doesn't matter", true) 288 289 // add a collection to the system chunk 290 col := unittest.CollectionFixture(1) 291 meta.Collection = &col 292 293 vch := meta.RefreshChunkData(s.T()) 294 295 _, err := s.verifier.Verify(vch) 296 assert.Error(s.T(), err) 297 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 298 assert.IsType(s.T(), &chunksmodels.CFSystemChunkIncludedCollection{}, err) 299 } 300 301 // TestEmptyCollection tests verification behaviour if a 302 // collection doesn't have any transaction. 303 func (s *ChunkVerifierTestSuite) TestEmptyCollection() { 304 meta := s.GetTestSetup(s.T(), "", false) 305 306 // reset test to use an empty collection 307 collection := unittest.CollectionFixture(0) 308 meta.Collection = &collection 309 meta.ChunkEvents = nil 310 meta.TxResults = nil 311 312 // update the Update to not change the state 313 update, err := ledger.NewEmptyUpdate(meta.StartState) 314 require.NoError(s.T(), err) 315 316 meta.Update = update 317 318 vch := meta.RefreshChunkData(s.T()) 319 320 spockSecret, err := s.verifier.Verify(vch) 321 assert.NoError(s.T(), err) 322 assert.NotNil(s.T(), spockSecret) 323 } 324 325 func (s *ChunkVerifierTestSuite) TestExecutionDataBlockMismatch() { 326 meta := s.GetTestSetup(s.T(), "", false) 327 328 // modify Block in the ExecutionDataRoot 329 meta.ExecDataBlockID = unittest.IdentifierFixture() 330 331 vch := meta.RefreshChunkData(s.T()) 332 333 _, err := s.verifier.Verify(vch) 334 assert.Error(s.T(), err) 335 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 336 assert.IsType(s.T(), &chunksmodels.CFExecutionDataBlockIDMismatch{}, err) 337 } 338 339 func (s *ChunkVerifierTestSuite) TestExecutionDataChunkIdsLengthDiffers() { 340 meta := s.GetTestSetup(s.T(), "", false) 341 vch := meta.RefreshChunkData(s.T()) 342 343 // add an additional ChunkExecutionDataID into the ExecutionDataRoot passed into Verify 344 vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs = append(vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs, cid.Undef) 345 346 _, err := s.verifier.Verify(vch) 347 assert.Error(s.T(), err) 348 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 349 assert.IsType(s.T(), &chunksmodels.CFExecutionDataChunksLengthMismatch{}, err) 350 } 351 352 func (s *ChunkVerifierTestSuite) TestExecutionDataChunkIdMismatch() { 353 meta := s.GetTestSetup(s.T(), "", false) 354 vch := meta.RefreshChunkData(s.T()) 355 356 // modify one of the ChunkExecutionDataIDs passed into Verify 357 vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs[0] = cid.Undef // substitute invalid CID 358 359 _, err := s.verifier.Verify(vch) 360 assert.Error(s.T(), err) 361 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 362 assert.IsType(s.T(), &chunksmodels.CFExecutionDataInvalidChunkCID{}, err) 363 } 364 365 func (s *ChunkVerifierTestSuite) TestExecutionDataIdMismatch() { 366 meta := s.GetTestSetup(s.T(), "", false) 367 vch := meta.RefreshChunkData(s.T()) 368 369 // modify ExecutionDataID passed into Verify 370 vch.Result.ExecutionDataID[5]++ 371 372 _, err := s.verifier.Verify(vch) 373 assert.Error(s.T(), err) 374 assert.True(s.T(), chunksmodels.IsChunkFaultError(err)) 375 assert.IsType(s.T(), &chunksmodels.CFInvalidExecutionDataID{}, err) 376 } 377 378 func newLedger(t *testing.T) *completeLedger.Ledger { 379 f, err := completeLedger.NewLedger(&fixtures.NoopWAL{}, 1000, metrics.NewNoopCollector(), zerolog.Nop(), completeLedger.DefaultPathFinderVersion) 380 require.NoError(t, err) 381 382 compactor := fixtures.NewNoopCompactor(f) 383 <-compactor.Ready() 384 385 t.Cleanup(func() { 386 <-f.Done() 387 <-compactor.Done() 388 }) 389 390 return f 391 } 392 393 func blockFixture(collection *flow.Collection) *flow.Block { 394 guarantee := collection.Guarantee() 395 block := &flow.Block{ 396 Header: unittest.BlockHeaderFixture(), 397 Payload: &flow.Payload{ 398 Guarantees: []*flow.CollectionGuarantee{&guarantee}, 399 }, 400 } 401 block.Header.PayloadHash = block.Payload.Hash() 402 return block 403 } 404 405 func generateStateUpdates(t *testing.T, f *completeLedger.Ledger) (ledger.State, ledger.Proof, *ledger.Update) { 406 entries := flow.RegisterEntries{ 407 { 408 Key: id0, 409 Value: []byte{'a'}, 410 }, 411 { 412 Key: id5, 413 Value: []byte{'b'}, 414 }, 415 } 416 417 keys, values := executionState.RegisterEntriesToKeysValues(entries) 418 update, err := ledger.NewUpdate(f.InitialState(), keys, values) 419 require.NoError(t, err) 420 421 startState, _, err := f.Set(update) 422 require.NoError(t, err) 423 424 query, err := ledger.NewQuery(startState, keys) 425 require.NoError(t, err) 426 427 proof, err := f.Prove(query) 428 require.NoError(t, err) 429 430 entries = flow.RegisterEntries{ 431 { 432 Key: id5, 433 Value: []byte{'B'}, 434 }, 435 } 436 437 keys, values = executionState.RegisterEntriesToKeysValues(entries) 438 update, err = ledger.NewUpdate(startState, keys, values) 439 require.NoError(t, err) 440 441 return startState, proof, update 442 } 443 444 func generateExecutionData(t *testing.T, blockID flow.Identifier, ced *execution_data.ChunkExecutionData) (flow.Identifier, flow.BlockExecutionDataRoot) { 445 chunkCid, err := executionDataCIDProvider.CalculateChunkExecutionDataID(*ced) 446 require.NoError(t, err) 447 448 executionDataRoot := flow.BlockExecutionDataRoot{ 449 BlockID: blockID, 450 ChunkExecutionDataIDs: []cid.Cid{chunkCid}, 451 } 452 453 executionDataID, err := executionDataCIDProvider.CalculateExecutionDataRootID(executionDataRoot) 454 require.NoError(t, err) 455 456 return executionDataID, executionDataRoot 457 } 458 459 func generateEvents(t *testing.T, isSystemChunk bool, collection *flow.Collection) (flow.EventsList, []flow.ServiceEvent) { 460 var chunkEvents flow.EventsList 461 serviceEvents := make([]flow.ServiceEvent, 0) 462 463 // service events are also included as regular events 464 if isSystemChunk { 465 for _, e := range systemEventsList { 466 e := e 467 event, err := convert.ServiceEvent(testChain, e) 468 require.NoError(t, err) 469 470 serviceEvents = append(serviceEvents, *event) 471 chunkEvents = append(chunkEvents, e) 472 } 473 } 474 475 for _, coll := range collection.Transactions { 476 switch string(coll.Script) { 477 case "failedTx": 478 continue 479 } 480 chunkEvents = append(chunkEvents, eventsList...) 481 } 482 483 return chunkEvents, serviceEvents 484 } 485 486 func generateTransactionResults(t *testing.T, collection *flow.Collection) []flow.LightTransactionResult { 487 txResults := make([]flow.LightTransactionResult, len(collection.Transactions)) 488 for i, tx := range collection.Transactions { 489 txResults[i] = flow.LightTransactionResult{ 490 TransactionID: tx.ID(), 491 ComputationUsed: computationUsed, 492 Failed: false, 493 } 494 495 if string(tx.Script) == "failedTx" { 496 txResults[i].Failed = true 497 } 498 } 499 500 return txResults 501 } 502 503 func generateCollection(t *testing.T, isSystemChunk bool, script string) *flow.Collection { 504 if isSystemChunk { 505 // the system chunk's data pack does not include the collection, but the execution data does. 506 // we must include the correct collection in the execution data, otherwise verification will fail. 507 return &flow.Collection{ 508 Transactions: []*flow.TransactionBody{serviceTxBody}, 509 } 510 } 511 512 collectionSize := 5 513 magicTxIndex := 3 514 515 coll := unittest.CollectionFixture(collectionSize) 516 if script != "" { 517 coll.Transactions[magicTxIndex] = &flow.TransactionBody{Script: []byte(script)} 518 } 519 520 return &coll 521 } 522 523 func generateDefaultSnapshot() *snapshot.ExecutionSnapshot { 524 return &snapshot.ExecutionSnapshot{ 525 ReadSet: map[flow.RegisterID]struct{}{ 526 id0: {}, 527 id5: {}, 528 }, 529 WriteSet: map[flow.RegisterID]flow.RegisterValue{ 530 id5: []byte{'B'}, 531 }, 532 } 533 } 534 535 func generateDefaultOutput() fvm.ProcedureOutput { 536 return fvm.ProcedureOutput{ 537 ComputationUsed: computationUsed, 538 Logs: []string{"log1", "log2"}, 539 Events: eventsList, 540 } 541 } 542 543 func (s *ChunkVerifierTestSuite) GetTestSetup(t *testing.T, script string, system bool) *testMetadata { 544 collection := generateCollection(t, system, script) 545 block := blockFixture(collection) 546 547 // transaction results 548 txResults := generateTransactionResults(t, collection) 549 // make sure this includes results even for the service tx 550 if system { 551 require.Len(t, txResults, 1) 552 } else { 553 require.Len(t, txResults, len(collection.Transactions)) 554 } 555 556 // events 557 chunkEvents, serviceEvents := generateEvents(t, system, collection) 558 // make sure this includes events even for the service tx 559 require.NotEmpty(t, chunkEvents) 560 if system { 561 require.Len(t, serviceEvents, 1) 562 } else { 563 require.Empty(t, serviceEvents) 564 } 565 566 // registerTouch and State setup 567 startState, proof, update := generateStateUpdates(t, s.ledger) 568 569 if system { 570 collection = nil 571 } 572 573 meta := &testMetadata{ 574 IsSystemChunk: system, 575 Header: block.Header, 576 Collection: collection, 577 TxResults: txResults, 578 ChunkEvents: chunkEvents, 579 ServiceEvents: serviceEvents, 580 StartState: startState, 581 Update: update, 582 Proof: proof, 583 584 ExecDataBlockID: block.Header.ID(), 585 586 ledger: s.ledger, 587 } 588 589 return meta 590 } 591 592 type testMetadata struct { 593 IsSystemChunk bool 594 Header *flow.Header 595 Collection *flow.Collection 596 TxResults []flow.LightTransactionResult 597 ChunkEvents flow.EventsList 598 ServiceEvents []flow.ServiceEvent 599 StartState ledger.State 600 Update *ledger.Update 601 Proof ledger.Proof 602 603 // separated to allow overriding 604 ExecDataBlockID flow.Identifier 605 606 ledger *completeLedger.Ledger 607 } 608 609 func (m *testMetadata) RefreshChunkData(t *testing.T) *verification.VerifiableChunkData { 610 cedCollection := m.Collection 611 612 if m.IsSystemChunk { 613 // the system chunk's data pack does not include the collection, but the execution data does. 614 // we must include the correct collection in the execution data, otherwise verification will fail. 615 cedCollection = &flow.Collection{ 616 Transactions: []*flow.TransactionBody{serviceTxBody}, 617 } 618 } 619 620 endState, trieUpdate, err := m.ledger.Set(m.Update) 621 require.NoError(t, err) 622 623 eventsMerkleRootHash, err := flow.EventsMerkleRootHash(m.ChunkEvents) 624 require.NoError(t, err) 625 626 chunkExecutionData := &execution_data.ChunkExecutionData{ 627 Collection: cedCollection, 628 Events: m.ChunkEvents, 629 TrieUpdate: trieUpdate, 630 TransactionResults: m.TxResults, 631 } 632 633 executionDataID, executionDataRoot := generateExecutionData(t, m.ExecDataBlockID, chunkExecutionData) 634 635 // Chunk setup 636 chunk := &flow.Chunk{ 637 ChunkBody: flow.ChunkBody{ 638 CollectionIndex: 0, 639 StartState: flow.StateCommitment(m.StartState), 640 BlockID: m.Header.ID(), 641 EventCollection: eventsMerkleRootHash, 642 }, 643 Index: 0, 644 } 645 646 chunkDataPack := &flow.ChunkDataPack{ 647 ChunkID: chunk.ID(), 648 StartState: flow.StateCommitment(m.StartState), 649 Proof: m.Proof, 650 Collection: m.Collection, 651 ExecutionDataRoot: executionDataRoot, 652 } 653 654 // ExecutionResult setup 655 result := &flow.ExecutionResult{ 656 BlockID: m.Header.ID(), 657 Chunks: flow.ChunkList{chunk}, 658 ServiceEvents: m.ServiceEvents, 659 ExecutionDataID: executionDataID, 660 } 661 662 return &verification.VerifiableChunkData{ 663 IsSystemChunk: m.IsSystemChunk, 664 Header: m.Header, 665 Chunk: chunk, 666 Result: result, 667 ChunkDataPack: chunkDataPack, 668 EndState: flow.StateCommitment(endState), 669 } 670 }