use bulk /txs endpoint to check cached rbf tx status
This commit is contained in:
		
							parent
							
								
									2339a0771e
								
							
						
					
					
						commit
						38909cfc42
					
				@ -3,6 +3,7 @@ import { IEsploraApi } from './esplora-api.interface';
 | 
				
			|||||||
export interface AbstractBitcoinApi {
 | 
					export interface AbstractBitcoinApi {
 | 
				
			||||||
  $getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
 | 
					  $getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
 | 
				
			||||||
  $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
 | 
					  $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
 | 
				
			||||||
 | 
					  $getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
 | 
				
			||||||
  $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
 | 
					  $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]>;
 | 
				
			||||||
  $getAllMempoolTransactions(lastTxid: string);
 | 
					  $getAllMempoolTransactions(lastTxid: string);
 | 
				
			||||||
  $getTransactionHex(txId: string): Promise<string>;
 | 
					  $getTransactionHex(txId: string): Promise<string>;
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,10 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
				
			||||||
 | 
					    throw new Error('Method getRawTransactions not supported by the Bitcoin RPC API.');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
					  $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
				
			||||||
    throw new Error('Method getMempoolTransactions not supported by the Bitcoin RPC API.');
 | 
					    throw new Error('Method getMempoolTransactions not supported by the Bitcoin RPC API.');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -213,6 +213,10 @@ class ElectrsApi implements AbstractBitcoinApi {
 | 
				
			|||||||
    return this.failoverRouter.$get<IEsploraApi.Transaction>('/tx/' + txId);
 | 
					    return this.failoverRouter.$get<IEsploraApi.Transaction>('/tx/' + txId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async $getRawTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
				
			||||||
 | 
					    return this.$postWrapper<IEsploraApi.Transaction[]>(config.ESPLORA.REST_API_URL + '/txs', txids, 'json');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
					  async $getMempoolTransactions(txids: string[]): Promise<IEsploraApi.Transaction[]> {
 | 
				
			||||||
    return this.failoverRouter.$post<IEsploraApi.Transaction[]>('/internal/mempool/txs', txids, 'json');
 | 
					    return this.failoverRouter.$post<IEsploraApi.Transaction[]>('/internal/mempool/txs', txids, 'json');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ import config from "../config";
 | 
				
			|||||||
import logger from "../logger";
 | 
					import logger from "../logger";
 | 
				
			||||||
import { MempoolTransactionExtended, TransactionStripped } from "../mempool.interfaces";
 | 
					import { MempoolTransactionExtended, TransactionStripped } from "../mempool.interfaces";
 | 
				
			||||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
 | 
					import bitcoinApi from './bitcoin/bitcoin-api-factory';
 | 
				
			||||||
 | 
					import { IEsploraApi } from "./bitcoin/esplora-api.interface";
 | 
				
			||||||
import { Common } from "./common";
 | 
					import { Common } from "./common";
 | 
				
			||||||
import redisCache from "./redis-cache";
 | 
					import redisCache from "./redis-cache";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -383,6 +384,7 @@ class RbfCache {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`);
 | 
					      logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`);
 | 
				
			||||||
      this.staleCount = 0;
 | 
					      this.staleCount = 0;
 | 
				
			||||||
 | 
					      await this.checkTrees();
 | 
				
			||||||
      this.cleanup();
 | 
					      this.cleanup();
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
@ -481,6 +483,57 @@ class RbfCache {
 | 
				
			|||||||
    return tree;
 | 
					    return tree;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async checkTrees(): Promise<void> {
 | 
				
			||||||
 | 
					    const found: { [txid: string]: boolean } = {};
 | 
				
			||||||
 | 
					    const txids = Array.from(this.txs.values()).map(tx => tx.txid).filter(txid => {
 | 
				
			||||||
 | 
					      return !this.expiring.has(txid) && !this.getRbfTree(txid)?.mined;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const processTxs = (txs: IEsploraApi.Transaction[]): void => {
 | 
				
			||||||
 | 
					      for (const tx of txs) {
 | 
				
			||||||
 | 
					        found[tx.txid] = true;
 | 
				
			||||||
 | 
					        if (tx.status?.confirmed) {
 | 
				
			||||||
 | 
					          const tree = this.getRbfTree(tx.txid);
 | 
				
			||||||
 | 
					          if (tree) {
 | 
				
			||||||
 | 
					            this.setTreeMined(tree, tx.txid);
 | 
				
			||||||
 | 
					            tree.mined = true;
 | 
				
			||||||
 | 
					            this.evict(tx.txid, false);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (config.MEMPOOL.BACKEND === 'esplora') {
 | 
				
			||||||
 | 
					      const sliceLength = 10000;
 | 
				
			||||||
 | 
					      for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) {
 | 
				
			||||||
 | 
					        const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          const txs = await bitcoinApi.$getRawTransactions(slice);
 | 
				
			||||||
 | 
					          processTxs(txs);
 | 
				
			||||||
 | 
					        } catch (err) {
 | 
				
			||||||
 | 
					          logger.err('failed to fetch some cached rbf transactions');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const txs: IEsploraApi.Transaction[] = [];
 | 
				
			||||||
 | 
					      for (const txid of txids) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          const tx = await bitcoinApi.$getRawTransaction(txid, true, false);
 | 
				
			||||||
 | 
					          txs.push(tx);
 | 
				
			||||||
 | 
					        } catch (err) {
 | 
				
			||||||
 | 
					          // some 404s are expected, so continue quietly
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      processTxs(txs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const txid of txids) {
 | 
				
			||||||
 | 
					      if (!found[txid]) {
 | 
				
			||||||
 | 
					        this.evict(txid, false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public getLatestRbfSummary(): ReplacementInfo[] {
 | 
					  public getLatestRbfSummary(): ReplacementInfo[] {
 | 
				
			||||||
    const rbfList = this.getRbfTrees(false);
 | 
					    const rbfList = this.getRbfTrees(false);
 | 
				
			||||||
    return rbfList.slice(0, 6).map(rbfTree => {
 | 
					    return rbfList.slice(0, 6).map(rbfTree => {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user