Add "recently cpfpd" exception to audits
This commit is contained in:
		
							parent
							
								
									b03f2185ce
								
							
						
					
					
						commit
						b33ea4679d
					
				@ -1,12 +1,12 @@
 | 
				
			|||||||
import config from '../config';
 | 
					import config from '../config';
 | 
				
			||||||
import logger from '../logger';
 | 
					import logger from '../logger';
 | 
				
			||||||
import { TransactionExtended, MempoolBlockWithTransactions } from '../mempool.interfaces';
 | 
					import { MempoolTransactionExtended, MempoolBlockWithTransactions } from '../mempool.interfaces';
 | 
				
			||||||
import rbfCache from './rbf-cache';
 | 
					import rbfCache from './rbf-cache';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first seen after which it is assumed to have propagated to all miners
 | 
					const PROPAGATION_MARGIN = 180; // in seconds, time since a transaction is first seen after which it is assumed to have propagated to all miners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Audit {
 | 
					class Audit {
 | 
				
			||||||
  auditBlock(transactions: TransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: TransactionExtended })
 | 
					  auditBlock(transactions: MempoolTransactionExtended[], projectedBlocks: MempoolBlockWithTransactions[], mempool: { [txId: string]: MempoolTransactionExtended })
 | 
				
			||||||
   : { censored: string[], added: string[], fresh: string[], sigop: string[], fullrbf: string[], score: number, similarity: number } {
 | 
					   : { censored: string[], added: string[], fresh: string[], sigop: string[], fullrbf: string[], score: number, similarity: number } {
 | 
				
			||||||
    if (!projectedBlocks?.[0]?.transactionIds || !mempool) {
 | 
					    if (!projectedBlocks?.[0]?.transactionIds || !mempool) {
 | 
				
			||||||
      return { censored: [], added: [], fresh: [], sigop: [], fullrbf: [], score: 0, similarity: 1 };
 | 
					      return { censored: [], added: [], fresh: [], sigop: [], fullrbf: [], score: 0, similarity: 1 };
 | 
				
			||||||
@ -14,7 +14,7 @@ class Audit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const matches: string[] = []; // present in both mined block and template
 | 
					    const matches: string[] = []; // present in both mined block and template
 | 
				
			||||||
    const added: string[] = []; // present in mined block, not in template
 | 
					    const added: string[] = []; // present in mined block, not in template
 | 
				
			||||||
    const fresh: string[] = []; // missing, but firstSeen within PROPAGATION_MARGIN
 | 
					    const fresh: string[] = []; // missing, but firstSeen or lastBoosted within PROPAGATION_MARGIN
 | 
				
			||||||
    const fullrbf: string[] = []; // either missing or present, and part of a fullrbf replacement
 | 
					    const fullrbf: string[] = []; // either missing or present, and part of a fullrbf replacement
 | 
				
			||||||
    const isCensored = {}; // missing, without excuse
 | 
					    const isCensored = {}; // missing, without excuse
 | 
				
			||||||
    const isDisplaced = {};
 | 
					    const isDisplaced = {};
 | 
				
			||||||
@ -36,10 +36,13 @@ class Audit {
 | 
				
			|||||||
    // look for transactions that were expected in the template, but missing from the mined block
 | 
					    // look for transactions that were expected in the template, but missing from the mined block
 | 
				
			||||||
    for (const txid of projectedBlocks[0].transactionIds) {
 | 
					    for (const txid of projectedBlocks[0].transactionIds) {
 | 
				
			||||||
      if (!inBlock[txid]) {
 | 
					      if (!inBlock[txid]) {
 | 
				
			||||||
        // tx is recent, may have reached the miner too late for inclusion
 | 
					 | 
				
			||||||
        if (rbfCache.isFullRbf(txid)) {
 | 
					        if (rbfCache.isFullRbf(txid)) {
 | 
				
			||||||
          fullrbf.push(txid);
 | 
					          fullrbf.push(txid);
 | 
				
			||||||
        } else if (mempool[txid]?.firstSeen != null && (now - (mempool[txid]?.firstSeen || 0)) <= PROPAGATION_MARGIN) {
 | 
					        } else if (mempool[txid]?.firstSeen != null && (now - (mempool[txid]?.firstSeen || 0)) <= PROPAGATION_MARGIN) {
 | 
				
			||||||
 | 
					          // tx is recent, may have reached the miner too late for inclusion
 | 
				
			||||||
 | 
					          fresh.push(txid);
 | 
				
			||||||
 | 
					        } else if (mempool[txid]?.lastBoosted != null && (now - (mempool[txid]?.lastBoosted || 0)) <= PROPAGATION_MARGIN) {
 | 
				
			||||||
 | 
					          // tx was recently cpfp'd, miner may not have the latest effective rate
 | 
				
			||||||
          fresh.push(txid);
 | 
					          fresh.push(txid);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          isCensored[txid] = true;
 | 
					          isCensored[txid] = true;
 | 
				
			||||||
 | 
				
			|||||||
@ -457,6 +457,7 @@ class MempoolBlocks {
 | 
				
			|||||||
              };
 | 
					              };
 | 
				
			||||||
              if (matched) {
 | 
					              if (matched) {
 | 
				
			||||||
                descendants.push(relative);
 | 
					                descendants.push(relative);
 | 
				
			||||||
 | 
					                mempoolTx.lastBoosted = Math.max(mempoolTx.lastBoosted || 0, mempool[txid].firstSeen || 0);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                ancestors.push(relative);
 | 
					                ancestors.push(relative);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
				
			|||||||
@ -100,6 +100,7 @@ export interface MempoolTransactionExtended extends TransactionExtended {
 | 
				
			|||||||
  adjustedVsize: number;
 | 
					  adjustedVsize: number;
 | 
				
			||||||
  adjustedFeePerVsize: number;
 | 
					  adjustedFeePerVsize: number;
 | 
				
			||||||
  inputs?: number[];
 | 
					  inputs?: number[];
 | 
				
			||||||
 | 
					  lastBoosted?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface AuditTransaction {
 | 
					export interface AuditTransaction {
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ export default class TxView implements TransactionStripped {
 | 
				
			|||||||
  value: number;
 | 
					  value: number;
 | 
				
			||||||
  feerate: number;
 | 
					  feerate: number;
 | 
				
			||||||
  rate?: number;
 | 
					  rate?: number;
 | 
				
			||||||
  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
					  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
				
			||||||
  context?: 'projected' | 'actual';
 | 
					  context?: 'projected' | 'actual';
 | 
				
			||||||
  scene?: BlockScene;
 | 
					  scene?: BlockScene;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -210,6 +210,7 @@ export default class TxView implements TransactionStripped {
 | 
				
			|||||||
      case 'fullrbf':
 | 
					      case 'fullrbf':
 | 
				
			||||||
        return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
 | 
					        return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
 | 
				
			||||||
      case 'fresh':
 | 
					      case 'fresh':
 | 
				
			||||||
 | 
					      case 'freshcpfp':
 | 
				
			||||||
        return auditColors.missing;
 | 
					        return auditColors.missing;
 | 
				
			||||||
      case 'added':
 | 
					      case 'added':
 | 
				
			||||||
        return auditColors.added;
 | 
					        return auditColors.added;
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@
 | 
				
			|||||||
          <td *ngSwitchCase="'missing'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
 | 
					          <td *ngSwitchCase="'missing'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
 | 
				
			||||||
          <td *ngSwitchCase="'sigop'"><span class="badge badge-warning" i18n="transaction.audit.sigop">High sigop count</span></td>
 | 
					          <td *ngSwitchCase="'sigop'"><span class="badge badge-warning" i18n="transaction.audit.sigop">High sigop count</span></td>
 | 
				
			||||||
          <td *ngSwitchCase="'fresh'"><span class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span></td>
 | 
					          <td *ngSwitchCase="'fresh'"><span class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span></td>
 | 
				
			||||||
 | 
					          <td *ngSwitchCase="'freshcpfp'"><span class="badge badge-warning" i18n="transaction.audit.recently-cpfped">Recently CPFP'd</span></td>
 | 
				
			||||||
          <td *ngSwitchCase="'added'"><span class="badge badge-warning" i18n="transaction.audit.added">Added</span></td>
 | 
					          <td *ngSwitchCase="'added'"><span class="badge badge-warning" i18n="transaction.audit.added">Added</span></td>
 | 
				
			||||||
          <td *ngSwitchCase="'selected'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
 | 
					          <td *ngSwitchCase="'selected'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
 | 
				
			||||||
          <td *ngSwitchCase="'fullrbf'"><span class="badge badge-warning" i18n="transaction.audit.fullrbf">Full RBF</span></td>
 | 
					          <td *ngSwitchCase="'fullrbf'"><span class="badge badge-warning" i18n="transaction.audit.fullrbf">Full RBF</span></td>
 | 
				
			||||||
 | 
				
			|||||||
@ -370,7 +370,11 @@ export class BlockComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
              tx.status = 'found';
 | 
					              tx.status = 'found';
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              if (isFresh[tx.txid]) {
 | 
					              if (isFresh[tx.txid]) {
 | 
				
			||||||
                tx.status = 'fresh';
 | 
					                if (tx.rate - (tx.fee / tx.vsize) >= 0.1) {
 | 
				
			||||||
 | 
					                  tx.status = 'freshcpfp';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                  tx.status = 'fresh';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
              } else if (isSigop[tx.txid]) {
 | 
					              } else if (isSigop[tx.txid]) {
 | 
				
			||||||
                tx.status = 'sigop';
 | 
					                tx.status = 'sigop';
 | 
				
			||||||
              } else if (isFullRbf[tx.txid]) {
 | 
					              } else if (isFullRbf[tx.txid]) {
 | 
				
			||||||
 | 
				
			|||||||
@ -173,7 +173,8 @@ export interface TransactionStripped {
 | 
				
			|||||||
  fee: number;
 | 
					  fee: number;
 | 
				
			||||||
  vsize: number;
 | 
					  vsize: number;
 | 
				
			||||||
  value: number;
 | 
					  value: number;
 | 
				
			||||||
  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
					  rate?: number; // effective fee rate
 | 
				
			||||||
 | 
					  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
				
			||||||
  context?: 'projected' | 'actual';
 | 
					  context?: 'projected' | 'actual';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -89,7 +89,7 @@ export interface TransactionStripped {
 | 
				
			|||||||
  vsize: number;
 | 
					  vsize: number;
 | 
				
			||||||
  value: number;
 | 
					  value: number;
 | 
				
			||||||
  rate?: number; // effective fee rate
 | 
					  rate?: number; // effective fee rate
 | 
				
			||||||
  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
					  status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'fullrbf';
 | 
				
			||||||
  context?: 'projected' | 'actual';
 | 
					  context?: 'projected' | 'actual';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user