keep cached RBF info for 24 hours after tx leaves the mempool
This commit is contained in:
		
							parent
							
								
									0481f57304
								
							
						
					
					
						commit
						d778530620
					
				@ -595,7 +595,7 @@ class BitcoinRoutes {
 | 
			
		||||
  private async getRbfHistory(req: Request, res: Response) {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = rbfCache.getReplaces(req.params.txId);
 | 
			
		||||
      res.json(result?.txids || []);
 | 
			
		||||
      res.json(result || []);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -236,6 +236,7 @@ class Mempool {
 | 
			
		||||
      const lazyDeleteAt = this.mempoolCache[tx].deleteAfter;
 | 
			
		||||
      if (lazyDeleteAt && lazyDeleteAt < now) {
 | 
			
		||||
        delete this.mempoolCache[tx];
 | 
			
		||||
        rbfCache.evict(tx);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,46 +1,29 @@
 | 
			
		||||
import { TransactionExtended } from "../mempool.interfaces";
 | 
			
		||||
 | 
			
		||||
export interface CachedRbf {
 | 
			
		||||
  txid: string;
 | 
			
		||||
  expires: Date;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CachedRbfs {
 | 
			
		||||
  txids: string[];
 | 
			
		||||
  expires: Date;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class RbfCache {
 | 
			
		||||
  private replacedby: { [txid: string]: CachedRbf; } = {};
 | 
			
		||||
  private replaces: { [txid: string]: CachedRbfs } = {};
 | 
			
		||||
  private replacedBy: { [txid: string]: string; } = {};
 | 
			
		||||
  private replaces: { [txid: string]: string[] } = {};
 | 
			
		||||
  private txs: { [txid: string]: TransactionExtended } = {};
 | 
			
		||||
  private expiring: { [txid: string]: Date } = {};
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    setInterval(this.cleanup.bind(this), 1000 * 60 * 60);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public add(replacedTx: TransactionExtended, newTxId: string): void {
 | 
			
		||||
    const expiry = new Date(Date.now() + 1000 * 604800); // 1 week
 | 
			
		||||
    this.replacedby[replacedTx.txid] = {
 | 
			
		||||
      expires: expiry,
 | 
			
		||||
      txid: newTxId,
 | 
			
		||||
    };
 | 
			
		||||
    this.replacedBy[replacedTx.txid] = newTxId;
 | 
			
		||||
    this.txs[replacedTx.txid] = replacedTx;
 | 
			
		||||
    if (!this.replaces[newTxId]) {
 | 
			
		||||
      this.replaces[newTxId] = {
 | 
			
		||||
        txids: [],
 | 
			
		||||
        expires: expiry,
 | 
			
		||||
      };
 | 
			
		||||
      this.replaces[newTxId] = [];
 | 
			
		||||
    }
 | 
			
		||||
    this.replaces[newTxId].txids.push(replacedTx.txid);
 | 
			
		||||
    this.replaces[newTxId].expires = expiry;
 | 
			
		||||
    this.replaces[newTxId].push(replacedTx.txid);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getReplacedBy(txId: string): CachedRbf | undefined {
 | 
			
		||||
    return this.replacedby[txId];
 | 
			
		||||
  public getReplacedBy(txId: string): string | undefined {
 | 
			
		||||
    return this.replacedBy[txId];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getReplaces(txId: string): CachedRbfs | undefined {
 | 
			
		||||
  public getReplaces(txId: string): string[] | undefined {
 | 
			
		||||
    return this.replaces[txId];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -48,17 +31,32 @@ class RbfCache {
 | 
			
		||||
    return this.txs[txId];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // flag a transaction as removed from the mempool
 | 
			
		||||
  public evict(txid): void {
 | 
			
		||||
    this.expiring[txid] = new Date(Date.now() + 1000 * 86400); // 24 hours
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private cleanup(): void {
 | 
			
		||||
    const currentDate = new Date();
 | 
			
		||||
    for (const c in this.replacedby) {
 | 
			
		||||
      if (this.replacedby[c].expires < currentDate) {
 | 
			
		||||
        delete this.replacedby[c];
 | 
			
		||||
        delete this.txs[c];
 | 
			
		||||
    for (const txid in this.expiring) {
 | 
			
		||||
      if (this.expiring[txid] < currentDate) {
 | 
			
		||||
        delete this.expiring[txid];
 | 
			
		||||
        this.remove(txid);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    for (const c in this.replaces) {
 | 
			
		||||
      if (this.replaces[c].expires < currentDate) {
 | 
			
		||||
        delete this.replaces[c];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // remove a transaction & all previous versions from the cache
 | 
			
		||||
  private remove(txid): void {
 | 
			
		||||
    // don't remove a transaction while a newer version remains in the mempool
 | 
			
		||||
    if (this.replaces[txid] && !this.replacedBy[txid]) {
 | 
			
		||||
      const replaces = this.replaces[txid];
 | 
			
		||||
      delete this.replaces[txid];
 | 
			
		||||
      for (const tx of replaces) {
 | 
			
		||||
        // recursively remove prior versions from the cache
 | 
			
		||||
        delete this.replacedBy[tx];
 | 
			
		||||
        delete this.txs[tx];
 | 
			
		||||
        this.remove(tx);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -58,10 +58,10 @@ class WebsocketHandler {
 | 
			
		||||
              client['track-tx'] = parsedMessage['track-tx'];
 | 
			
		||||
              // Client is telling the transaction wasn't found
 | 
			
		||||
              if (parsedMessage['watch-mempool']) {
 | 
			
		||||
                const rbfCacheTx = rbfCache.getReplacedBy(client['track-tx']);
 | 
			
		||||
                if (rbfCacheTx) {
 | 
			
		||||
                const rbfCacheTxid = rbfCache.getReplacedBy(client['track-tx']);
 | 
			
		||||
                if (rbfCacheTxid) {
 | 
			
		||||
                  response['txReplaced'] = {
 | 
			
		||||
                    txid: rbfCacheTx.txid,
 | 
			
		||||
                    txid: rbfCacheTxid,
 | 
			
		||||
                  };
 | 
			
		||||
                  client['track-tx'] = null;
 | 
			
		||||
                } else {
 | 
			
		||||
@ -467,6 +467,7 @@ class WebsocketHandler {
 | 
			
		||||
    for (const txId of txIds) {
 | 
			
		||||
      delete _memPool[txId];
 | 
			
		||||
      removed.push(txId);
 | 
			
		||||
      rbfCache.evict(txId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user