@@ -123,6 +123,30 @@ const getLastTransactionsAtBlock = (
123123 return txsFromSameBlock ;
124124} ;
125125
126+ export const revertLastBlock = (
127+ localTransactions : Cardano . HydratedTx [ ] ,
128+ blockNo : Cardano . BlockNo ,
129+ rollback$ : Subject < Cardano . HydratedTx > ,
130+ logger : Logger
131+ ) => {
132+ const result = [ ...localTransactions ] ;
133+
134+ while ( result . length > 0 ) {
135+ const lastKnownTx = result [ result . length - 1 ] ;
136+
137+ if ( lastKnownTx . blockHeader . blockNo === blockNo ) {
138+ logger . debug ( `Transaction ${ lastKnownTx . id } was rolled back` ) ;
139+
140+ rollback$ . next ( lastKnownTx ) ;
141+ result . pop ( ) ;
142+ } else {
143+ break ;
144+ }
145+ }
146+
147+ return result ;
148+ } ;
149+
126150const findIntersectionAndUpdateTxStore = ( {
127151 chainHistoryProvider,
128152 logger,
@@ -148,12 +172,9 @@ const findIntersectionAndUpdateTxStore = ({
148172 combinator : exhaustMap ,
149173 equals : transactionsEquals ,
150174 onFatalError,
151- // eslint-disable-next-line complexity
152175 provider : async ( ) => {
153176 // eslint-disable-next-line no-constant-condition
154177 while ( true ) {
155- let rollbackOccured = false ;
156-
157178 const lastStoredTransaction : Cardano . HydratedTx | undefined = localTransactions [ localTransactions . length - 1 ] ;
158179
159180 lastStoredTransaction &&
@@ -172,65 +193,46 @@ const findIntersectionAndUpdateTxStore = ({
172193 lowerBound !== undefined && `since block ${ lowerBound } `
173194 ) ;
174195
196+ // Fetching transactions from scratch, nothing else to do here.
175197 if ( lowerBound === undefined ) {
176- localTransactions = newTransactions ;
177- return localTransactions ;
198+ return newTransactions ;
178199 }
179200
201+ // If no transactions found from that block range, it means the last known block has been rolled back.
180202 if ( newTransactions . length === 0 ) {
181- // If no transactions found from that block range, it means the last known block has been rolled back.
182- while ( localTransactions . length > 0 ) {
183- const lastKnownTx = localTransactions [ localTransactions . length - 1 ] ;
184-
185- if ( lastKnownTx . blockHeader . blockNo === lowerBound ) {
186- rollbackOccured = true ;
187- logger . debug ( `Transaction ${ lastKnownTx . id } was rolled back` ) ;
188- rollback$ . next ( lastKnownTx ) ;
189- localTransactions . pop ( ) ;
190- } else {
191- break ;
192- }
193- }
194-
203+ localTransactions = revertLastBlock ( localTransactions , lowerBound , rollback$ , logger ) ;
195204 continue ;
196205 }
197206
198207 const localTxsFromSameBlock = getLastTransactionsAtBlock ( localTransactions , lowerBound ) ;
208+ const firstSegmentOfNewTransactions = newTransactions . slice ( 0 , localTxsFromSameBlock . length ) ;
199209
200- // Roll back transactions from this block not found in the result
201- for ( const localTx of localTxsFromSameBlock ) {
202- const txFound = newTransactions . find ( ( tx ) => tx . id === localTx . id ) ;
203-
204- if ( ! txFound ) {
205- rollbackOccured = true ;
206- logger . debug ( `Transaction ${ localTx . id } was rolled back` ) ;
207- rollback$ . next ( localTx ) ;
210+ // The first segment of new transaction should match exactly (same txs and same order) our last know TXs. Otherwise
211+ // roll them back and re-apply in new order.
212+ const sameLength = localTxsFromSameBlock . length === firstSegmentOfNewTransactions . length ;
213+ const sameOrder =
214+ sameLength && localTxsFromSameBlock . every ( ( tx , index ) => tx . id === firstSegmentOfNewTransactions [ index ] . id ) ;
208215
209- const index = localTransactions . findLastIndex ( ( tx ) => tx . id === localTx . id ) ;
216+ if ( ! sameLength || ! sameOrder ) {
217+ localTransactions = revertLastBlock ( localTransactions , lowerBound , rollback$ , logger ) ;
218+ localTransactions = [ ...localTransactions , ...newTransactions ] ;
219+ store . setAll ( localTransactions ) ;
210220
211- if ( index !== - 1 ) {
212- localTransactions . splice ( index , 1 ) ;
213- }
214- }
221+ continue ;
215222 }
216223
217- if ( ! rollbackOccured ) {
218- const lastLocalTxs = localTransactions . slice ( - newTransactions . length ) ;
219-
220- const areTransactionsSame =
221- lastLocalTxs . length === newTransactions . length &&
222- lastLocalTxs . every ( ( tx , index ) => tx . id === newTransactions [ index ] . id ) ;
224+ // No rollbacks, if they overlap 100% do nothing, otherwise add the difference.
225+ const areTransactionsSame =
226+ newTransactions . length === localTxsFromSameBlock . length &&
227+ localTxsFromSameBlock . every ( ( tx , index ) => tx . id === newTransactions [ index ] . id ) ;
223228
224- if ( ! areTransactionsSame ) {
225- // Remove overlapping transactions
226- localTransactions = localTransactions . slice ( 0 , - localTxsFromSameBlock . length ) ;
227- localTransactions = [ ...localTransactions , ...newTransactions ] ;
228-
229- store . setAll ( localTransactions ) ;
230- }
231-
232- return localTransactions ;
229+ if ( ! areTransactionsSame ) {
230+ // Skip overlapping transactions to avoid duplicates
231+ localTransactions = [ ...localTransactions , ...newTransactions . slice ( localTxsFromSameBlock . length ) ] ;
232+ store . setAll ( localTransactions ) ;
233233 }
234+
235+ return localTransactions ;
234236 }
235237 } ,
236238 retryBackoffConfig,
0 commit comments