Merge pull request #3785 from mempool/mononaut/mined-block-rbf
Detect RBF by mined transactions
This commit is contained in:
		
						commit
						bdfa67e670
					
				@ -77,6 +77,24 @@ export class Common {
 | 
			
		||||
    return matches;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static findMinedRbfTransactions(minedTransactions: TransactionExtended[], spendMap: Map<string, TransactionExtended>): { [txid: string]: { replaced: TransactionExtended[], replacedBy: TransactionExtended }} {
 | 
			
		||||
    const matches: { [txid: string]: { replaced: TransactionExtended[], replacedBy: TransactionExtended }} = {};
 | 
			
		||||
    for (const tx of minedTransactions) {
 | 
			
		||||
      const replaced: Set<TransactionExtended> = new Set();
 | 
			
		||||
      for (let i = 0; i < tx.vin.length; i++) {
 | 
			
		||||
        const vin = tx.vin[i];
 | 
			
		||||
        const match = spendMap.get(`${vin.txid}:${vin.vout}`);
 | 
			
		||||
        if (match && match.txid !== tx.txid) {
 | 
			
		||||
          replaced.add(match);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (replaced.size) {
 | 
			
		||||
        matches[tx.txid] = { replaced: Array.from(replaced), replacedBy: tx };
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return matches;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static stripTransaction(tx: TransactionExtended): TransactionStripped {
 | 
			
		||||
    return {
 | 
			
		||||
      txid: tx.txid,
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ class Mempool {
 | 
			
		||||
  private inSync: boolean = false;
 | 
			
		||||
  private mempoolCacheDelta: number = -1;
 | 
			
		||||
  private mempoolCache: { [txId: string]: TransactionExtended } = {};
 | 
			
		||||
  private spendMap = new Map<string, TransactionExtended>();
 | 
			
		||||
  private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0,
 | 
			
		||||
                                                    maxmempool: 300000000, mempoolminfee: 0.00001000, minrelaytxfee: 0.00001000 };
 | 
			
		||||
  private mempoolChangedCallback: ((newMempool: {[txId: string]: TransactionExtended; }, newTransactions: TransactionExtended[],
 | 
			
		||||
@ -77,6 +78,10 @@ class Mempool {
 | 
			
		||||
    return this.mempoolCache;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getSpendMap(): Map<string, TransactionExtended> {
 | 
			
		||||
    return this.spendMap;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $setMempool(mempoolData: { [txId: string]: TransactionExtended }) {
 | 
			
		||||
    this.mempoolCache = mempoolData;
 | 
			
		||||
    if (this.mempoolChangedCallback) {
 | 
			
		||||
@ -85,6 +90,7 @@ class Mempool {
 | 
			
		||||
    if (this.$asyncMempoolChangedCallback) {
 | 
			
		||||
      await this.$asyncMempoolChangedCallback(this.mempoolCache, [], []);
 | 
			
		||||
    }
 | 
			
		||||
    this.addToSpendMap(Object.values(this.mempoolCache));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $updateMemPoolInfo() {
 | 
			
		||||
@ -276,6 +282,34 @@ class Mempool {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public handleMinedRbfTransactions(rbfTransactions: { [txid: string]: { replaced: TransactionExtended[], replacedBy: TransactionExtended }}): void {
 | 
			
		||||
    for (const rbfTransaction in rbfTransactions) {
 | 
			
		||||
      if (rbfTransactions[rbfTransaction].replacedBy && rbfTransactions[rbfTransaction]?.replaced?.length) {
 | 
			
		||||
        // Store replaced transactions
 | 
			
		||||
        rbfCache.add(rbfTransactions[rbfTransaction].replaced, rbfTransactions[rbfTransaction].replacedBy);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public addToSpendMap(transactions: TransactionExtended[]): void {
 | 
			
		||||
    for (const tx of transactions) {
 | 
			
		||||
      for (const vin of tx.vin) {
 | 
			
		||||
        this.spendMap.set(`${vin.txid}:${vin.vout}`, tx);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public removeFromSpendMap(transactions: TransactionExtended[]): void {
 | 
			
		||||
    for (const tx of transactions) {
 | 
			
		||||
      for (const vin of tx.vin) {
 | 
			
		||||
        const key = `${vin.txid}:${vin.vout}`;
 | 
			
		||||
        if (this.spendMap.get(key)?.txid === tx.txid) {
 | 
			
		||||
          this.spendMap.delete(key);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private updateTxPerSecond() {
 | 
			
		||||
    const nowMinusTimeSpan = new Date().getTime() - (1000 * config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD);
 | 
			
		||||
    this.txPerSecondArray = this.txPerSecondArray.filter((unixTime) => unixTime > nowMinusTimeSpan);
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class RbfCache {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public add(replaced: TransactionExtended[], newTxExtended: TransactionExtended): void {
 | 
			
		||||
    if (!newTxExtended || !replaced?.length) {
 | 
			
		||||
    if (!newTxExtended || !replaced?.length || this.txs.has(newTxExtended.txid)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -332,6 +332,8 @@ class WebsocketHandler {
 | 
			
		||||
    for (const deletedTx of deletedTransactions) {
 | 
			
		||||
      rbfCache.evict(deletedTx.txid);
 | 
			
		||||
    }
 | 
			
		||||
    memPool.removeFromSpendMap(deletedTransactions);
 | 
			
		||||
    memPool.addToSpendMap(newTransactions);
 | 
			
		||||
    const recommendedFees = feeApi.getRecommendedFee();
 | 
			
		||||
 | 
			
		||||
    // update init data
 | 
			
		||||
@ -600,6 +602,10 @@ class WebsocketHandler {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const rbfTransactions = Common.findMinedRbfTransactions(transactions, memPool.getSpendMap());
 | 
			
		||||
    memPool.handleMinedRbfTransactions(rbfTransactions);
 | 
			
		||||
    memPool.removeFromSpendMap(transactions);
 | 
			
		||||
 | 
			
		||||
    // Update mempool to remove transactions included in the new block
 | 
			
		||||
    for (const txId of txIds) {
 | 
			
		||||
      delete _memPool[txId];
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user