@@ -314,10 +314,7 @@ func (e *blockComputer) queueSystemTransactions(
314314 systemLogger zerolog.Logger ,
315315) chan TransactionRequest {
316316 allTxs := append (executeCallbackTxs , systemTxn )
317- // add execute callback transactions to the system collection info along to existing process transaction
318- // TODO(7749): fix illegal mutation
319317 systemTxs := systemColection .CompleteCollection .Collection .Transactions
320- systemColection .CompleteCollection .Collection .Transactions = append (systemTxs , allTxs ... ) //nolint:structwrite
321318 systemLogger = systemLogger .With ().Uint32 ("num_txs" , uint32 (len (systemTxs ))).Logger ()
322319
323320 txQueue := make (chan TransactionRequest , len (allTxs ))
@@ -497,19 +494,24 @@ func (e *blockComputer) executeSystemTransactions(
497494 Logger ()
498495
499496 systemCollectionInfo := collectionInfo {
500- blockId : block .BlockID (),
501- blockIdStr : block .BlockID ().String (),
502- blockHeight : block .Block .Height ,
503- collectionIndex : userCollectionCount ,
504- CompleteCollection : & entity.CompleteCollection {
505- Collection : flow .NewEmptyCollection (), // TODO(7749)
506- },
497+ blockId : block .BlockID (),
498+ blockIdStr : block .BlockID ().String (),
499+ blockHeight : block .Block .Height ,
500+ collectionIndex : userCollectionCount ,
501+ CompleteCollection : nil , // We do not yet know all the scheduled callbacks, so postpone construction of the collection.
507502 isSystemTransaction : true ,
508503 }
509504
510505 var callbackTxs []* flow.TransactionBody
511506
512507 if e .vmCtx .ScheduleCallbacksEnabled {
508+ // We pass in the `systemCollectionInfo` here. However, note that at this point, the composition of the system chunk
509+ // is not yet known. Specifically, the `entity.CompleteCollection` represents the *final* output of a process and is
510+ // immutable by protocol mandate. If we had a bug in our software that accidentally illegally mutated such structs,
511+ // likely the node encountering that bug would misbehave and get slashed, or in the worst case the flow protocol might
512+ // be compromised. Therefore, we have the rigorous convention in our code base that the `CompleteCollection` is only
513+ // constructed once the final composition of the system chunk has been determined.
514+ // To that end, the CompleteCollection is nil here, such that any attempt to access the Collection will panic.
513515 callbacks , updatedTxnIndex , err := e .executeProcessCallback (
514516 callbackCtx ,
515517 systemCollectionInfo ,
@@ -524,6 +526,26 @@ func (e *blockComputer) executeSystemTransactions(
524526
525527 callbackTxs = callbacks
526528 txIndex = updatedTxnIndex
529+
530+ finalCollection , err := flow .NewCollection (flow.UntrustedCollection {
531+ Transactions : append (append ([]* flow.TransactionBody {e .processCallbackTxn }, callbackTxs ... ), e .systemTxn ),
532+ })
533+ if err != nil {
534+ return err
535+ }
536+ systemCollectionInfo .CompleteCollection = & entity.CompleteCollection {
537+ Collection : finalCollection ,
538+ }
539+ } else {
540+ finalCollection , err := flow .NewCollection (flow.UntrustedCollection {
541+ Transactions : []* flow.TransactionBody {e .systemTxn },
542+ })
543+ if err != nil {
544+ return err
545+ }
546+ systemCollectionInfo .CompleteCollection = & entity.CompleteCollection {
547+ Collection : finalCollection ,
548+ }
527549 }
528550
529551 // Update logger with number of transactions once they've become known
@@ -589,10 +611,6 @@ func (e *blockComputer) executeProcessCallback(
589611 txnIndex uint32 ,
590612 systemLogger zerolog.Logger ,
591613) ([]* flow.TransactionBody , uint32 , error ) {
592- // add process callback transaction to the system collection info
593- // TODO(7749): fix illegal mutation
594- systemCollectionInfo .CompleteCollection .Collection .Transactions = append (systemCollectionInfo .CompleteCollection .Collection .Transactions , e .processCallbackTxn ) //nolint:structwrite
595-
596614 request := newTransactionRequest (
597615 systemCollectionInfo ,
598616 systemCtx ,
0 commit comments