@@ -34,7 +34,6 @@ import (
3434 "encoding/hex"
3535 "errors"
3636 "fmt"
37- "maps"
3837 "math"
3938 "strings"
4039 "sync"
@@ -756,9 +755,12 @@ func configureNewLedger(
756755 nil ,
757756 nil ,
758757 nil ,
758+ nil , // systemTransactions (ordered slice)
759+ nil , // systemTransactionBodies
760+ nil , // systemTransactionResults
759761 genesisExecutionSnapshot ,
760762 output .Events ,
761- nil ,
763+ nil , // scheduledTransactionIDs
762764 )
763765 if err != nil {
764766 return nil , nil , err
@@ -1102,6 +1104,35 @@ func (b *Blockchain) getTransactionResult(txID flowgo.Identifier) (*accessmodel.
11021104 return & result , nil
11031105}
11041106
1107+ func (b * Blockchain ) getSystemTransactionResult (blockID flowgo.Identifier , txID flowgo.Identifier ) (* accessmodel.TransactionResult , error ) {
1108+ storedResult , err := b .storage .SystemTransactionResultByID (context .Background (), blockID , txID )
1109+ if err != nil {
1110+ if errors .Is (err , storage .ErrNotFound ) {
1111+ return & accessmodel.TransactionResult {
1112+ Status : flowgo .TransactionStatusUnknown ,
1113+ }, nil
1114+ }
1115+ return nil , err
1116+ }
1117+
1118+ statusCode := 0
1119+ if storedResult .ErrorCode > 0 {
1120+ statusCode = 1
1121+ }
1122+ result := accessmodel.TransactionResult {
1123+ Status : flowgo .TransactionStatusSealed ,
1124+ StatusCode : uint (statusCode ),
1125+ ErrorMessage : storedResult .ErrorMessage ,
1126+ Events : storedResult .Events ,
1127+ TransactionID : txID ,
1128+ BlockHeight : storedResult .BlockHeight ,
1129+ BlockID : storedResult .BlockID ,
1130+ CollectionID : storedResult .CollectionID ,
1131+ }
1132+
1133+ return & result , nil
1134+ }
1135+
11051136// GetAccountByIndex returns the account for the given address.
11061137func (b * Blockchain ) GetAccountByIndex (index uint ) (* flowgo.Account , error ) {
11071138 generator := flowsdk .NewAddressGenerator (flowsdk .ChainID (b .vmCtx .Chain .ChainID ()))
@@ -1425,24 +1456,28 @@ func (b *Blockchain) commitBlock() (*flowgo.Block, error) {
14251456 if len (collections ) > 0 {
14261457 collectionID = collections [0 ].ID ()
14271458 }
1459+
1460+ // Regular user transactions and results
14281461 transactions := b .pendingBlock .Transactions ()
14291462 transactionResults , err := convertToSealedResults (b .pendingBlock .TransactionResults (), b .pendingBlock .ID (), b .pendingBlock .height , collectionID )
14301463 if err != nil {
14311464 return nil , err
14321465 }
14331466
1467+ // System transactions and results (stored separately, order preserved)
1468+ systemTransactionIDs := []flowgo.Identifier {} // Ordered list of system tx IDs
1469+ systemTransactionBodies := make (map [flowgo.Identifier ]* flowgo.TransactionBody )
1470+ systemTransactionResults := make (map [flowgo.Identifier ]* types.StorableTransactionResult )
1471+ scheduledTransactionIDs := make (map [uint64 ]flowgo.Identifier )
1472+
14341473 // execute scheduled transactions out-of-band before the system chunk
14351474 // (does not change collections or payload)
14361475 blockContext := fvm .NewContextFromParent (
14371476 b .vmCtx ,
14381477 fvm .WithBlockHeader (block .ToHeader ()),
14391478 )
1440- systemTransactions := storage.SystemTransactions {
1441- BlockID : b .pendingBlock .ID (),
1442- Transactions : []flowgo.Identifier {},
1443- }
14441479 if b .conf .ScheduledTransactionsEnabled {
1445- tx , results , scheduledTxIDs , err := b .executeScheduledTransactions (blockContext )
1480+ tx , results , scheduledTxIDs , orderedIDs , err := b .executeScheduledTransactions (blockContext )
14461481 if err != nil {
14471482 return nil , err
14481483 }
@@ -1451,18 +1486,23 @@ func (b *Blockchain) commitBlock() (*flowgo.Block, error) {
14511486 if err != nil {
14521487 return nil , err
14531488 }
1454- maps .Copy (transactionResults , convertedResults )
1489+ for id , result := range convertedResults {
1490+ systemTransactionResults [id ] = result
1491+ }
14551492 for id , t := range tx {
1456- transactions [id ] = t
1457- systemTransactions .Transactions = append (systemTransactions .Transactions , id )
1493+ systemTransactionBodies [id ] = t
14581494 }
14591495
1496+ // Append scheduled tx IDs in execution order
1497+ systemTransactionIDs = append (systemTransactionIDs , orderedIDs ... )
1498+
14601499 // Store scheduled transaction ID mappings
1461- systemTransactions . ScheduledTransactionIDs = scheduledTxIDs
1500+ scheduledTransactionIDs = scheduledTxIDs
14621501 }
14631502
1464- // lastly we execute the system chunk transaction
1465- chunkBody , itr , err := b .executeSystemChunkTransaction ()
1503+ // Calculate index for system chunk transaction which executes after all user and scheduled transactions
1504+ systemChunkIndex := uint32 (len (transactions )) + uint32 (len (systemTransactionIDs ))
1505+ chunkBody , itr , err := b .executeSystemChunkTransaction (systemChunkIndex )
14661506 if err != nil {
14671507 return nil , err
14681508 }
@@ -1472,9 +1512,9 @@ func (b *Blockchain) commitBlock() (*flowgo.Block, error) {
14721512 if err != nil {
14731513 return nil , err
14741514 }
1475- transactions [systemTxID ] = chunkBody
1476- transactionResults [systemTxID ] = & systemTxStorableResult
1477- systemTransactions . Transactions = append (systemTransactions . Transactions , systemTxID )
1515+ systemTransactionBodies [systemTxID ] = chunkBody
1516+ systemTransactionResults [systemTxID ] = & systemTxStorableResult
1517+ systemTransactionIDs = append (systemTransactionIDs , systemTxID )
14781518
14791519 executionSnapshot := b .pendingBlock .Finalize ()
14801520 events := b .pendingBlock .Events ()
@@ -1486,15 +1526,18 @@ func (b *Blockchain) commitBlock() (*flowgo.Block, error) {
14861526 collections ,
14871527 transactions ,
14881528 transactionResults ,
1529+ systemTransactionIDs ,
1530+ systemTransactionBodies ,
1531+ systemTransactionResults ,
14891532 executionSnapshot ,
14901533 events ,
1491- & systemTransactions )
1534+ scheduledTransactionIDs )
14921535 if err != nil {
14931536 return nil , err
14941537 }
14951538
14961539 // Index scheduled transactions globally (scheduledTxID → blockID)
1497- for scheduledTxID := range systemTransactions . ScheduledTransactionIDs {
1540+ for scheduledTxID := range scheduledTransactionIDs {
14981541 err = b .storage .IndexScheduledTransactionID (context .Background (), scheduledTxID , block .ID ())
14991542 if err != nil {
15001543 return nil , fmt .Errorf ("failed to index scheduled transaction %d: %w" , scheduledTxID , err )
@@ -1866,9 +1909,9 @@ func (b *Blockchain) GetTransactionResultsByBlockID(blockID flowgo.Identifier) (
18661909 return nil , fmt .Errorf ("failed to get system transactions %w" , err )
18671910 }
18681911 for j , txID := range st .Transactions {
1869- result , err := b .getTransactionResult ( txID )
1912+ result , err := b .getSystemTransactionResult ( blockID , txID )
18701913 if err != nil {
1871- return nil , fmt .Errorf ("failed to get transaction result [%d] %s: %w" , j , txID , err )
1914+ return nil , fmt .Errorf ("failed to get system transaction result [%d] %s: %w" , j , txID , err )
18721915 }
18731916 results = append (results , result )
18741917 }
@@ -1952,8 +1995,8 @@ func (b *Blockchain) GetSystemTransactionResult(txID flowgo.Identifier, blockID
19521995 return nil , & types.TransactionNotFoundError {ID : txID }
19531996 }
19541997
1955- // Retrieve the transaction result
1956- return b .getTransactionResult ( txID )
1998+ // Retrieve the system transaction result using composite key (blockID, txID)
1999+ return b .getSystemTransactionResult ( blockID , txID )
19572000}
19582001
19592002// GetScheduledTransactionByBlockID returns a scheduled transaction by its scheduled transaction ID and block ID.
@@ -2036,8 +2079,8 @@ func (b *Blockchain) GetScheduledTransactionResultByBlockID(scheduledTxID uint64
20362079 return nil , & types.TransactionNotFoundError {ID : flowgo .ZeroID }
20372080 }
20382081
2039- // Retrieve the transaction result
2040- return b .getTransactionResult ( txID )
2082+ // Retrieve the system transaction result using composite key
2083+ return b .getSystemTransactionResult ( blockID , txID )
20412084}
20422085
20432086// GetScheduledTransactionResult returns the result of a scheduled transaction by its scheduled transaction ID.
@@ -2070,8 +2113,8 @@ func (b *Blockchain) GetScheduledTransactionResult(scheduledTxID uint64) (*acces
20702113 return nil , & types.TransactionNotFoundError {ID : flowgo .ZeroID }
20712114 }
20722115
2073- // Retrieve the transaction result
2074- return b .getTransactionResult ( txID )
2116+ // Retrieve the system transaction result using composite key
2117+ return b .getSystemTransactionResult ( blockID , txID )
20752118}
20762119
20772120func (b * Blockchain ) GetLogs (identifier flowgo.Identifier ) ([]string , error ) {
@@ -2162,7 +2205,7 @@ func (b *Blockchain) systemChunkTransaction() (*flowgo.TransactionBody, error) {
21622205 return tx , nil
21632206}
21642207
2165- func (b * Blockchain ) executeSystemChunkTransaction () (* flowgo.TransactionBody , * IndexedTransactionResult , error ) {
2208+ func (b * Blockchain ) executeSystemChunkTransaction (txIndex uint32 ) (* flowgo.TransactionBody , * IndexedTransactionResult , error ) {
21662209 txn , err := b .systemChunkTransaction ()
21672210 if err != nil {
21682211 return nil , nil , err
@@ -2178,7 +2221,7 @@ func (b *Blockchain) executeSystemChunkTransaction() (*flowgo.TransactionBody, *
21782221
21792222 executionSnapshot , output , err := b .vm .Run (
21802223 ctx ,
2181- fvm .Transaction (txn , uint32 ( len ( b . pendingBlock . Transactions ())) ),
2224+ fvm .Transaction (txn , txIndex ),
21822225 b .pendingBlock .ledgerState ,
21832226 )
21842227 if err != nil {
@@ -2198,15 +2241,16 @@ func (b *Blockchain) executeSystemChunkTransaction() (*flowgo.TransactionBody, *
21982241
21992242 itr := & IndexedTransactionResult {
22002243 ProcedureOutput : output ,
2201- Index : 0 ,
2244+ Index : txIndex ,
22022245 }
22032246 return txn , itr , nil
22042247}
22052248
2206- func (b * Blockchain ) executeScheduledTransactions (blockContext fvm.Context ) (map [flowgo.Identifier ]* flowgo.TransactionBody , map [flowgo.Identifier ]IndexedTransactionResult , map [uint64 ]flowgo.Identifier , error ) {
2249+ func (b * Blockchain ) executeScheduledTransactions (blockContext fvm.Context ) (map [flowgo.Identifier ]* flowgo.TransactionBody , map [flowgo.Identifier ]IndexedTransactionResult , map [uint64 ]flowgo.Identifier , []flowgo. Identifier , error ) {
22072250 systemTransactions := map [flowgo.Identifier ]* flowgo.TransactionBody {}
22082251 systemTransactionResults := map [flowgo.Identifier ]IndexedTransactionResult {}
22092252 scheduledTxIDMap := map [uint64 ]flowgo.Identifier {} // maps scheduled tx ID to transaction ID
2253+ orderedTxIDs := []flowgo.Identifier {} // execution order of system transactions
22102254
22112255 // disable checks for signatures and keys since we are executing a system transaction
22122256 ctx := fvm .NewContextFromParent (
@@ -2216,62 +2260,71 @@ func (b *Blockchain) executeScheduledTransactions(blockContext fvm.Context) (map
22162260 fvm .WithTransactionFeesEnabled (false ),
22172261 )
22182262
2263+ // Base index for system transactions (equal to count of user transactions)
2264+ systemTxBaseIndex := uint32 (len (b .pendingBlock .Transactions ()))
2265+
22192266 // process scheduled transactions out-of-band (do not alter collections)
22202267 processTx , err := blueprints .ProcessCallbacksTransaction (b .GetChain ())
22212268 if err != nil {
2222- return nil , nil , nil , err
2269+ return nil , nil , nil , nil , err
22232270 }
22242271
22252272 // Use the real transaction ID from the process transaction
22262273 processID := processTx .ID ()
22272274 systemTransactions [processID ] = processTx
2275+ orderedTxIDs = append (orderedTxIDs , processID ) // First in order
22282276
2277+ processTxIndex := systemTxBaseIndex
22292278 executionSnapshot , output , err := b .vm .Run (
22302279 ctx ,
2231- fvm .Transaction (processTx , uint32 ( len ( b . pendingBlock . Transactions ())) ),
2280+ fvm .Transaction (processTx , processTxIndex ),
22322281 b .pendingBlock .ledgerState ,
22332282 )
22342283 if err != nil {
2235- return nil , nil , nil , err
2284+ return nil , nil , nil , nil , err
22362285 }
22372286
22382287 systemTransactionResults [processID ] = IndexedTransactionResult {
22392288 ProcedureOutput : output ,
2240- Index : 0 ,
2289+ Index : processTxIndex ,
22412290 }
22422291
22432292 // record events and state changes
22442293 b .pendingBlock .events = append (b .pendingBlock .events , output .Events ... )
22452294 if err := b .pendingBlock .ledgerState .Merge (executionSnapshot ); err != nil {
2246- return nil , nil , nil , err
2295+ return nil , nil , nil , nil , err
22472296 }
22482297
22492298 executeTxs , err := blueprints .ExecuteCallbacksTransactions (b .GetChain (), output .Events )
22502299 if err != nil {
2251- return nil , nil , nil , err
2300+ return nil , nil , nil , nil , err
22522301 }
22532302
22542303 env := systemcontracts .SystemContractsForChain (b .GetChain ().ChainID ()).AsTemplateEnv ()
22552304 scheduledIDs , err := parseScheduledIDs (env , output .Events )
22562305 if err != nil {
2257- return nil , nil , nil , err
2306+ return nil , nil , nil , nil , err
22582307 }
22592308
22602309 // execute scheduled transactions out-of-band
22612310 for idx , tx := range executeTxs {
22622311 id := tx .ID ()
2312+ orderedTxIDs = append (orderedTxIDs , id ) // Add in execution order
2313+
2314+ // Each execute callback gets its own index after the process transaction
2315+ executeTxIndex := systemTxBaseIndex + 1 + uint32 (idx )
22632316 execSnapshot , execOutput , err := b .vm .Run (
22642317 ctx ,
2265- fvm .Transaction (tx , uint32 ( len ( b . pendingBlock . Transactions ())) ),
2318+ fvm .Transaction (tx , executeTxIndex ),
22662319 b .pendingBlock .ledgerState ,
22672320 )
22682321 if err != nil {
2269- return nil , nil , nil , err
2322+ return nil , nil , nil , nil , err
22702323 }
22712324
22722325 systemTransactionResults [id ] = IndexedTransactionResult {
22732326 ProcedureOutput : execOutput ,
2274- Index : 0 ,
2327+ Index : executeTxIndex ,
22752328 }
22762329 systemTransactions [id ] = tx
22772330
@@ -2288,7 +2341,7 @@ func (b *Blockchain) executeScheduledTransactions(blockContext fvm.Context) (map
22882341 // Print scheduled transaction result (labeled), including app-level scheduled tx id
22892342 schedResult , err := convert .VMTransactionResultToEmulator (tx .ID (), execOutput )
22902343 if err != nil {
2291- return nil , nil , nil , err
2344+ return nil , nil , nil , nil , err
22922345 }
22932346 appScheduledID := ""
22942347 if idx < len (scheduledIDs ) {
@@ -2298,11 +2351,11 @@ func (b *Blockchain) executeScheduledTransactions(blockContext fvm.Context) (map
22982351
22992352 b .pendingBlock .events = append (b .pendingBlock .events , execOutput .Events ... )
23002353 if err := b .pendingBlock .ledgerState .Merge (execSnapshot ); err != nil {
2301- return nil , nil , nil , err
2354+ return nil , nil , nil , nil , err
23022355 }
23032356 }
23042357
2305- return systemTransactions , systemTransactionResults , scheduledTxIDMap , nil
2358+ return systemTransactions , systemTransactionResults , scheduledTxIDMap , orderedTxIDs , nil
23062359}
23072360
23082361func (b * Blockchain ) GetRegisterValues (registerIDs flowgo.RegisterIDs , height uint64 ) (values []flowgo.RegisterValue , err error ) {
0 commit comments