1515// limitations under the License.
1616
1717// The confirmation reconciler manages transaction confirmation queues by:
18- // - Copying blocks from the canonical chain and the existing confirmation queue
18+ // - Copying blocks from the in-memory partial chain and the existing confirmation queue
1919// - Detecting blockchain forks and rebuilding confirmation queues when necessary
2020// - Filling gaps in confirmation queues by fetching missing blocks
2121// - Determining when transactions have reached the target confirmation count
@@ -32,9 +32,9 @@ import (
3232)
3333
3434// reconcileConfirmationsForTransaction reconciles the confirmation queue for a transaction
35- func (bl * blockListener ) reconcileConfirmationsForTransaction (ctx context.Context , txHash string , existingConfirmations []* ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationMapUpdateResult , error ) {
35+ func (bl * blockListener ) reconcileConfirmationsForTransaction (ctx context.Context , txHash string , existingConfirmations []* ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationUpdateResult , error ) {
3636 // Initialize the result with existing confirmations
37- reconcileResult := & ffcapi.ConfirmationMapUpdateResult {
37+ reconcileResult := & ffcapi.ConfirmationUpdateResult {
3838 Confirmations : existingConfirmations ,
3939 NewFork : false ,
4040 Confirmed : false ,
@@ -50,31 +50,52 @@ func (bl *blockListener) reconcileConfirmationsForTransaction(ctx context.Contex
5050
5151 if txBlockInfo == nil {
5252 log .L (ctx ).Debugf ("Transaction %s not found in any block" , txHash )
53- return reconcileResult , nil
53+ return nil , i18n . NewError ( ctx , msgs . MsgTransactionNotFound , txHash )
5454 }
5555
5656 return bl .compareAndUpdateConfirmationQueue (ctx , reconcileResult , txBlockInfo , targetConfirmationCount )
5757}
5858
5959// compareAndUpdateConfirmationQueue orchestrates the confirmation reconciliation process.
60- // It builds new confirmations from the canonical chain and fills gaps in the confirmation queue.
61- func (bl * blockListener ) compareAndUpdateConfirmationQueue (ctx context.Context , reconcileResult * ffcapi.ConfirmationMapUpdateResult , txBlockInfo * ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationMapUpdateResult , error ) {
60+ // It builds new confirmations from the in-memory partial chain and fills gaps in the confirmation queue.
61+ func (bl * blockListener ) compareAndUpdateConfirmationQueue (ctx context.Context , reconcileResult * ffcapi.ConfirmationUpdateResult , txBlockInfo * ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationUpdateResult , error ) {
6262 var err error
63- // Build new confirmations from the canonical chain and get existing confirmations
64- newConfirmationsWithoutTxBlock , lastValidatedBlock , returnResult := bl .buildConfirmationQueueUsingCanonicalChain (ctx , reconcileResult , txBlockInfo , targetConfirmationCount )
65- if returnResult {
66- return reconcileResult , nil
63+ // Build new confirmations from the in-memory partial chain and get existing confirmations
64+ newConfirmationsWithoutTxBlock , lastValidatedBlock , err := bl .buildConfirmationQueueUsingInMemoryPartialChain (ctx , txBlockInfo , targetConfirmationCount )
65+ if err != nil {
66+ return nil , err
6767 }
6868
6969 // Initialize confirmation map and get existing confirmations
70- // the init must happen after the canonical chain check to avoid
71- // confirming blocks that are not yet validated in the canonical chain
72- existingConfirmations := bl .initializeConfirmationMap (reconcileResult , txBlockInfo )
70+ // the init must happen after the in-memory partial chain check to avoid
71+ // confirming blocks that are not yet validated in the in-memory partial chain
72+ var existingConfirmations []* ffcapi.MinimalBlockInfo
73+ // If no existing confirmations, initialize with the transaction block
74+ if len (reconcileResult .Confirmations ) == 0 {
75+ reconcileResult .Confirmations = []* ffcapi.MinimalBlockInfo {txBlockInfo }
76+ existingConfirmations = nil
77+ } else {
78+ // Validate existing confirmations against the current transaction block
79+ existingQueue := reconcileResult .Confirmations
80+ if len (existingQueue ) > 0 {
81+ existingTxBlock := existingQueue [0 ]
82+ if ! existingTxBlock .Equal (txBlockInfo ) {
83+ // Transaction block mismatch indicates a fork - rebuild confirmation queue
84+ reconcileResult .NewFork = true
85+ reconcileResult .Confirmations = []* ffcapi.MinimalBlockInfo {txBlockInfo }
86+ existingConfirmations = nil
87+ } else {
88+ existingConfirmations = existingQueue
89+ }
90+ } else {
91+ existingConfirmations = existingQueue
92+ }
93+ }
7394
7495 // Special case: if targetConfirmationCount is 0, transaction is immediately confirmed
7596 if targetConfirmationCount == 0 {
76- reconcileResult .Confirmed = true
7797 reconcileResult .Confirmations = []* ffcapi.MinimalBlockInfo {txBlockInfo }
98+ reconcileResult .Confirmed = true
7899 return reconcileResult , nil
79100 }
80101
@@ -83,88 +104,63 @@ func (bl *blockListener) compareAndUpdateConfirmationQueue(ctx context.Context,
83104 var newFork bool
84105 confirmations , newFork , err = bl .checkAndFillInGap (ctx , newConfirmationsWithoutTxBlock , existingConfirmations , txBlockInfo , targetConfirmationCount , lastValidatedBlock )
85106 if err != nil {
86- return reconcileResult , err
107+ return nil , err
87108 }
88109 reconcileResult .NewFork = newFork
89110 reconcileResult .Confirmations = confirmations
111+ reconcileResult .Confirmed = uint64 (len (confirmations )) >= targetConfirmationCount + 1
90112 return reconcileResult , err
91113}
92114
93- // buildConfirmationQueueUsingCanonicalChain builds the confirmation queue using the in-memory canonical chain.
94- // It does not modify the canonical chain itself, only reads from it.
95- // This function holds a read lock on the canonical chain, so it should not make long-running queries.
96- func (bl * blockListener ) buildConfirmationQueueUsingCanonicalChain (ctx context.Context , reconcileResult * ffcapi. ConfirmationMapUpdateResult , txBlockInfo * ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (newConfirmationsWithoutTxBlock []* ffcapi.MinimalBlockInfo , lastValidatedBlock * ffcapi.MinimalBlockInfo , returnResult bool ) {
115+ // buildConfirmationQueueUsingInMemoryPartialChain builds the confirmation queue using the in-memory partial chain.
116+ // It does not modify the in-memory partial chain itself, only reads from it.
117+ // This function holds a read lock on the in-memory partial chain, so it should not make long-running queries.
118+ func (bl * blockListener ) buildConfirmationQueueUsingInMemoryPartialChain (ctx context.Context , txBlockInfo * ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (newConfirmationsWithoutTxBlock []* ffcapi.MinimalBlockInfo , lastValidatedBlock * ffcapi.MinimalBlockInfo , err error ) {
97119 bl .mux .RLock ()
98120 defer bl .mux .RUnlock ()
99121 txBlockNumber := txBlockInfo .BlockNumber .Uint64 ()
100122 targetBlockNumber := txBlockInfo .BlockNumber .Uint64 () + targetConfirmationCount
101123
102- // Check if the canonical chain has caught up to the transaction block
124+ // Check if the in-memory partial chain has caught up to the transaction block
103125 chainTail := bl .canonicalChain .Back ().Value .(* ffcapi.MinimalBlockInfo )
104126 if chainTail == nil || chainTail .BlockNumber .Uint64 () < txBlockNumber {
105- log .L (ctx ).Debugf ("Canonical chain is waiting for the transaction block %d to be indexed" , txBlockNumber )
106- return nil , nil , true
127+ log .L (ctx ).Debugf ("in-memory partial chain is waiting for the transaction block %d to be indexed" , txBlockNumber )
128+ return nil , nil , i18n . NewError ( ctx , msgs . MsgInMemoryPartialChainNotCaughtUp , txBlockNumber , txBlockInfo . BlockHash )
107129 }
108130
109131 // Build new confirmations from blocks after the transaction block
110132
111133 newConfirmationsWithoutTxBlock = []* ffcapi.MinimalBlockInfo {}
112- currentBlock := bl .canonicalChain .Front ()
113- for currentBlock != nil {
114- currentBlockInfo := currentBlock .Value .(* ffcapi.MinimalBlockInfo )
134+ nextInMemoryBlock := bl .canonicalChain .Front ()
135+ for nextInMemoryBlock != nil {
136+ nextInMemoryBlockInfo := nextInMemoryBlock .Value .(* ffcapi.MinimalBlockInfo )
115137
116138 // If we've reached the target confirmation count, mark as confirmed
117- if currentBlockInfo .BlockNumber .Uint64 () > targetBlockNumber {
118- reconcileResult .Confirmed = true
119- // if the canonical chain contains the next block after the target block number,
139+ if nextInMemoryBlockInfo .BlockNumber .Uint64 () > targetBlockNumber {
140+ // if the in-memory partial chain contains the next block after the target block number,
120141 // and the new confirmations queue is empty,
121142 // we set the last validated block to the next block, so the downstream function can use it validate blocks before it
122- if len (newConfirmationsWithoutTxBlock ) == 0 && currentBlockInfo .BlockNumber .Uint64 () == targetBlockNumber + 1 {
123- lastValidatedBlock = currentBlockInfo
143+ if len (newConfirmationsWithoutTxBlock ) == 0 && nextInMemoryBlockInfo .BlockNumber .Uint64 () == targetBlockNumber + 1 {
144+ lastValidatedBlock = nextInMemoryBlockInfo
124145 }
125146 break
126147 }
127148
128149 // Skip blocks at or before the transaction block
129- if currentBlockInfo .BlockNumber .Uint64 () <= txBlockNumber {
130- currentBlock = currentBlock .Next ()
150+ if nextInMemoryBlockInfo .BlockNumber .Uint64 () <= txBlockNumber {
151+ nextInMemoryBlock = nextInMemoryBlock .Next ()
131152 continue
132153 }
133154
134155 // Add blocks after the transaction block to confirmations
135156 newConfirmationsWithoutTxBlock = append (newConfirmationsWithoutTxBlock , & ffcapi.MinimalBlockInfo {
136- BlockHash : currentBlockInfo .BlockHash ,
137- BlockNumber : fftypes .FFuint64 (currentBlockInfo .BlockNumber .Uint64 ()),
138- ParentHash : currentBlockInfo .ParentHash ,
157+ BlockHash : nextInMemoryBlockInfo .BlockHash ,
158+ BlockNumber : fftypes .FFuint64 (nextInMemoryBlockInfo .BlockNumber .Uint64 ()),
159+ ParentHash : nextInMemoryBlockInfo .ParentHash ,
139160 })
140- currentBlock = currentBlock .Next ()
161+ nextInMemoryBlock = nextInMemoryBlock .Next ()
141162 }
142- return newConfirmationsWithoutTxBlock , lastValidatedBlock , false
143- }
144-
145- // initializeConfirmationMap initializes the confirmation map with the transaction block
146- // and validates existing confirmations against the current transaction block.
147- // Returns existing confirmations if valid, or nil if a fork is detected.
148- func (bl * blockListener ) initializeConfirmationMap (reconcileResult * ffcapi.ConfirmationMapUpdateResult , txBlockInfo * ffcapi.MinimalBlockInfo ) []* ffcapi.MinimalBlockInfo {
149- // If no existing confirmations, initialize with the transaction block
150- if len (reconcileResult .Confirmations ) == 0 {
151- reconcileResult .Confirmations = []* ffcapi.MinimalBlockInfo {txBlockInfo }
152- return nil
153- }
154-
155- // Validate existing confirmations against the current transaction block
156- existingQueue := reconcileResult .Confirmations
157- if len (existingQueue ) > 0 {
158- existingTxBlock := existingQueue [0 ]
159- if ! existingTxBlock .Equal (txBlockInfo ) {
160- // Transaction block mismatch indicates a fork - rebuild confirmation queue
161- reconcileResult .NewFork = true
162- reconcileResult .Confirmations = []* ffcapi.MinimalBlockInfo {txBlockInfo }
163- return nil
164- }
165- }
166-
167- return existingQueue
163+ return newConfirmationsWithoutTxBlock , lastValidatedBlock , nil
168164}
169165
170166// checkAndFillInGap validates existing confirmations, detects forks, and fills gaps
@@ -254,6 +250,6 @@ func (bl *blockListener) checkAndFillInGap(ctx context.Context, newConfirmations
254250
255251// ReconcileConfirmationsForTransaction is the public API for reconciling transaction confirmations.
256252// It delegates to the blockListener's internal reconciliation logic.
257- func (c * ethConnector ) ReconcileConfirmationsForTransaction (ctx context.Context , txHash string , existingConfirmations []* ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationMapUpdateResult , error ) {
253+ func (c * ethConnector ) ReconcileConfirmationsForTransaction (ctx context.Context , txHash string , existingConfirmations []* ffcapi.MinimalBlockInfo , targetConfirmationCount uint64 ) (* ffcapi.ConfirmationUpdateResult , error ) {
258254 return c .blockListener .reconcileConfirmationsForTransaction (ctx , txHash , existingConfirmations , targetConfirmationCount )
259255}
0 commit comments