Mini-miner based block cpfp calculations
This commit is contained in:
		
							parent
							
								
									27374bd131
								
							
						
					
					
						commit
						41c373c39d
					
				@ -31,6 +31,7 @@ import rbfCache from './rbf-cache';
 | 
			
		||||
import { calcBitsDifference } from './difficulty-adjustment';
 | 
			
		||||
import AccelerationRepository from '../repositories/AccelerationRepository';
 | 
			
		||||
import { calculateFastBlockCpfp, calculateGoodBlockCpfp } from './cpfp';
 | 
			
		||||
import mempool from './mempool';
 | 
			
		||||
 | 
			
		||||
class Blocks {
 | 
			
		||||
  private blocks: BlockExtended[] = [];
 | 
			
		||||
@ -602,13 +603,13 @@ class Blocks {
 | 
			
		||||
 | 
			
		||||
    for (let height = currentBlockHeight; height >= 0; height--) {
 | 
			
		||||
      try {
 | 
			
		||||
        let txs: TransactionExtended[] | null = null;
 | 
			
		||||
        let txs: MempoolTransactionExtended[] | null = null;
 | 
			
		||||
        if (unclassifiedBlocks[height]) {
 | 
			
		||||
          const blockHash = unclassifiedBlocks[height];
 | 
			
		||||
          // fetch transactions
 | 
			
		||||
          txs = (await bitcoinApi.$getTxsForBlock(blockHash)).map(tx => transactionUtils.extendTransaction(tx)) || [];
 | 
			
		||||
          txs = (await bitcoinApi.$getTxsForBlock(blockHash)).map(tx => transactionUtils.extendMempoolTransaction(tx)) || [];
 | 
			
		||||
          // add CPFP
 | 
			
		||||
          const cpfpSummary = calculateFastBlockCpfp(height, txs, true);
 | 
			
		||||
          const cpfpSummary = calculateGoodBlockCpfp(height, txs, []);
 | 
			
		||||
          // classify
 | 
			
		||||
          const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, cpfpSummary.transactions);
 | 
			
		||||
          await BlocksSummariesRepository.$saveTransactions(height, blockHash, classifiedTxs, 1);
 | 
			
		||||
@ -637,7 +638,7 @@ class Blocks {
 | 
			
		||||
              }
 | 
			
		||||
              templateTxs.push(tx || templateTx);
 | 
			
		||||
            }
 | 
			
		||||
            const cpfpSummary = calculateFastBlockCpfp(height, templateTxs?.filter(tx => tx['effectiveFeePerVsize'] != null) as TransactionExtended[], true);
 | 
			
		||||
            const cpfpSummary = calculateGoodBlockCpfp(height, templateTxs?.filter(tx => tx['effectiveFeePerVsize'] != null) as MempoolTransactionExtended[], []);
 | 
			
		||||
            // classify
 | 
			
		||||
            const { transactions: classifiedTxs } = this.summarizeBlockTransactions(blockHash, cpfpSummary.transactions);
 | 
			
		||||
            const classifiedTxMap: { [txid: string]: TransactionClassified } = {};
 | 
			
		||||
@ -891,7 +892,7 @@ class Blocks {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const cpfpSummary: CpfpSummary = calculateGoodBlockCpfp(block.height, transactions);
 | 
			
		||||
      const cpfpSummary: CpfpSummary = calculateGoodBlockCpfp(block.height, transactions, Object.values(mempool.getAccelerations()).map(a => ({ txid: a.txid, max_bid: a.feeDelta })));
 | 
			
		||||
      const blockExtended: BlockExtended = await this.$getBlockExtended(block, cpfpSummary.transactions);
 | 
			
		||||
      const blockSummary: BlockSummary = this.summarizeBlockTransactions(block.id, cpfpSummary.transactions);
 | 
			
		||||
      this.updateTimerProgress(timer, `got block data for ${this.currentBlockHeight}`);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import { CpfpCluster, CpfpInfo, CpfpSummary, MempoolTransactionExtended, TransactionExtended } from '../mempool.interfaces';
 | 
			
		||||
import { GraphTx, convertToGraphTx, expandRelativesGraph, initializeRelatives, mempoolComparator, removeAncestors, setAncestorScores } from './mini-miner';
 | 
			
		||||
import { Ancestor, CpfpCluster, CpfpInfo, CpfpSummary, MempoolTransactionExtended, TransactionExtended } from '../mempool.interfaces';
 | 
			
		||||
import { GraphTx, convertToGraphTx, expandRelativesGraph, initializeRelatives, makeBlockTemplate, mempoolComparator, removeAncestors, setAncestorScores } from './mini-miner';
 | 
			
		||||
import memPool from './mempool';
 | 
			
		||||
import { Acceleration } from './acceleration';
 | 
			
		||||
 | 
			
		||||
const CPFP_UPDATE_INTERVAL = 60_000; // update CPFP info at most once per 60s per transaction
 | 
			
		||||
const MAX_CLUSTER_ITERATIONS = 100;
 | 
			
		||||
@ -95,8 +96,70 @@ export function calculateFastBlockCpfp(height: number, transactions: Transaction
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function calculateGoodBlockCpfp(height: number, transactions: TransactionExtended[]): CpfpSummary {
 | 
			
		||||
  return calculateFastBlockCpfp(height, transactions, true);
 | 
			
		||||
export function calculateGoodBlockCpfp(height: number, transactions: MempoolTransactionExtended[], accelerations: Acceleration[]): CpfpSummary {
 | 
			
		||||
  const txMap: { [txid: string]: MempoolTransactionExtended } = {};
 | 
			
		||||
  for (const tx of transactions) {
 | 
			
		||||
    txMap[tx.txid] = tx;
 | 
			
		||||
  }
 | 
			
		||||
  const template = makeBlockTemplate(transactions, accelerations, 1, Infinity, Infinity);
 | 
			
		||||
  const clusters = new Map<string, string[]>();
 | 
			
		||||
  for (const tx of template) {
 | 
			
		||||
    const cluster = tx.cluster || [];
 | 
			
		||||
    const root = cluster.length ? cluster[cluster.length - 1] : null;
 | 
			
		||||
    if (cluster.length > 1 && root && !clusters.has(root)) {
 | 
			
		||||
      clusters.set(root, cluster);
 | 
			
		||||
    }
 | 
			
		||||
    txMap[tx.txid].effectiveFeePerVsize = tx.effectiveFeePerVsize;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const clusterArray: CpfpCluster[] = [];
 | 
			
		||||
 | 
			
		||||
  for (const cluster of clusters.values()) {
 | 
			
		||||
    for (const txid of cluster) {
 | 
			
		||||
      const mempoolTx = txMap[txid];
 | 
			
		||||
      if (mempoolTx) {
 | 
			
		||||
        const ancestors: Ancestor[] = [];
 | 
			
		||||
        const descendants: Ancestor[] = [];
 | 
			
		||||
        let matched = false;
 | 
			
		||||
        cluster.forEach(relativeTxid => {
 | 
			
		||||
          if (relativeTxid === txid) {
 | 
			
		||||
            matched = true;
 | 
			
		||||
          } else {
 | 
			
		||||
            const relative = {
 | 
			
		||||
              txid: relativeTxid,
 | 
			
		||||
              fee: txMap[relativeTxid].fee,
 | 
			
		||||
              weight: (txMap[relativeTxid].adjustedVsize * 4) || txMap[relativeTxid].weight,
 | 
			
		||||
            };
 | 
			
		||||
            if (matched) {
 | 
			
		||||
              descendants.push(relative);
 | 
			
		||||
            } else {
 | 
			
		||||
              ancestors.push(relative);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        if (mempoolTx.ancestors?.length !== ancestors.length || mempoolTx.descendants?.length !== descendants.length) {
 | 
			
		||||
          mempoolTx.cpfpDirty = true;
 | 
			
		||||
        }
 | 
			
		||||
        Object.assign(mempoolTx, { ancestors, descendants, bestDescendant: null, cpfpChecked: true });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const root = cluster[cluster.length - 1];
 | 
			
		||||
    clusterArray.push({
 | 
			
		||||
      root: root,
 | 
			
		||||
      height,
 | 
			
		||||
      txs: cluster.reverse().map(txid => ({
 | 
			
		||||
        txid,
 | 
			
		||||
        fee: txMap[txid].fee,
 | 
			
		||||
        weight: (txMap[txid].adjustedVsize * 4) || txMap[txid].weight,
 | 
			
		||||
      })),
 | 
			
		||||
      effectiveFeePerVsize: txMap[root].effectiveFeePerVsize,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    transactions: transactions.map(tx => txMap[tx.txid]),
 | 
			
		||||
    clusters: clusterArray,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user