reduce number of GBT candidates
This commit is contained in:
parent
8bbe8a976b
commit
a62bc22549
@ -1,6 +1,7 @@
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import { MempoolBlock, TransactionExtended, ThreadTransaction, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor } from '../mempool.interfaces';
|
import { MempoolBlock, TransactionExtended, ThreadTransaction, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor } from '../mempool.interfaces';
|
||||||
import { Common } from './common';
|
import { Common } from './common';
|
||||||
|
import Mempool from './mempool';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import { Worker } from 'worker_threads';
|
import { Worker } from 'worker_threads';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -10,6 +11,9 @@ class MempoolBlocks {
|
|||||||
private mempoolBlockDeltas: MempoolBlockDelta[] = [];
|
private mempoolBlockDeltas: MempoolBlockDelta[] = [];
|
||||||
private txSelectionWorker: Worker | null = null;
|
private txSelectionWorker: Worker | null = null;
|
||||||
|
|
||||||
|
private filteredTxs: Map<string, TransactionExtended> = new Map();
|
||||||
|
private minFee: number = 0;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
public getMempoolBlocks(): MempoolBlock[] {
|
public getMempoolBlocks(): MempoolBlock[] {
|
||||||
@ -94,6 +98,7 @@ class MempoolBlocks {
|
|||||||
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks);
|
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks);
|
||||||
this.mempoolBlocks = blocks;
|
this.mempoolBlocks = blocks;
|
||||||
this.mempoolBlockDeltas = deltas;
|
this.mempoolBlockDeltas = deltas;
|
||||||
|
this.updateMinFee(this.mempoolBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks;
|
return blocks;
|
||||||
@ -175,18 +180,29 @@ class MempoolBlocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async $makeBlockTemplates(newMempool: { [txid: string]: TransactionExtended }, saveResults: boolean = false): Promise<MempoolBlockWithTransactions[]> {
|
public async $makeBlockTemplates(newMempool: { [txid: string]: TransactionExtended }, saveResults: boolean = false): Promise<MempoolBlockWithTransactions[]> {
|
||||||
|
// Identify txs that can't be CPFP'd
|
||||||
|
this.markRelatives(newMempool);
|
||||||
|
|
||||||
|
// Track filtered transactions
|
||||||
|
this.filteredTxs.clear();
|
||||||
|
const filterFee = this.getFilterFee();
|
||||||
|
|
||||||
// prepare a stripped down version of the mempool with only the minimum necessary data
|
// prepare a stripped down version of the mempool with only the minimum necessary data
|
||||||
// to reduce the overhead of passing this data to the worker thread
|
// to reduce the overhead of passing this data to the worker thread
|
||||||
const strippedMempool: { [txid: string]: ThreadTransaction } = {};
|
const strippedMempool: { [txid: string]: ThreadTransaction } = {};
|
||||||
Object.values(newMempool).forEach(entry => {
|
Object.values(newMempool).forEach(entry => {
|
||||||
strippedMempool[entry.txid] = {
|
if (entry.hasRelatives || entry.feePerVsize >= filterFee) {
|
||||||
txid: entry.txid,
|
strippedMempool[entry.txid] = {
|
||||||
fee: entry.fee,
|
txid: entry.txid,
|
||||||
weight: entry.weight,
|
fee: entry.fee,
|
||||||
feePerVsize: entry.fee / (entry.weight / 4),
|
weight: entry.weight,
|
||||||
effectiveFeePerVsize: entry.fee / (entry.weight / 4),
|
feePerVsize: entry.fee / (entry.weight / 4),
|
||||||
vin: entry.vin.map(v => v.txid),
|
effectiveFeePerVsize: entry.fee / (entry.weight / 4),
|
||||||
};
|
vin: entry.vin.map(v => v.txid),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.filteredTxs.set(entry.txid, entry);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// (re)initialize tx selection worker thread
|
// (re)initialize tx selection worker thread
|
||||||
@ -238,9 +254,14 @@ class MempoolBlocks {
|
|||||||
await this.$makeBlockTemplates(newMempool, saveResults);
|
await this.$makeBlockTemplates(newMempool, saveResults);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.updateMarkedRelatives(newMempool, added);
|
||||||
|
const filterDiff = this.updateFilteredTxs(newMempool, this.getFilterFee());
|
||||||
|
|
||||||
// prepare a stripped down version of the mempool with only the minimum necessary data
|
// prepare a stripped down version of the mempool with only the minimum necessary data
|
||||||
// to reduce the overhead of passing this data to the worker thread
|
// to reduce the overhead of passing this data to the worker thread
|
||||||
const addedStripped: ThreadTransaction[] = added.map(entry => {
|
const addedStripped: ThreadTransaction[] = added.concat(filterDiff.included).map(entry => {
|
||||||
return {
|
return {
|
||||||
txid: entry.txid,
|
txid: entry.txid,
|
||||||
fee: entry.fee,
|
fee: entry.fee,
|
||||||
@ -250,6 +271,7 @@ class MempoolBlocks {
|
|||||||
vin: entry.vin.map(v => v.txid),
|
vin: entry.vin.map(v => v.txid),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const removedIds = removed.concat(filterDiff.filtered);
|
||||||
|
|
||||||
// run the block construction algorithm in a separate thread, and wait for a result
|
// run the block construction algorithm in a separate thread, and wait for a result
|
||||||
let threadErrorListener;
|
let threadErrorListener;
|
||||||
@ -261,7 +283,7 @@ class MempoolBlocks {
|
|||||||
});
|
});
|
||||||
this.txSelectionWorker?.once('error', reject);
|
this.txSelectionWorker?.once('error', reject);
|
||||||
});
|
});
|
||||||
this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed });
|
this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed: removedIds });
|
||||||
let { blocks, clusters } = await workerResultPromise;
|
let { blocks, clusters } = await workerResultPromise;
|
||||||
// filter out stale transactions
|
// filter out stale transactions
|
||||||
const unfilteredCount = blocks.reduce((total, block) => { return total + block.length; }, 0);
|
const unfilteredCount = blocks.reduce((total, block) => { return total + block.length; }, 0);
|
||||||
@ -332,6 +354,11 @@ class MempoolBlocks {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (blocks.length) {
|
||||||
|
const sortedFiltered = Array.from(this.filteredTxs.values()).sort((a, b) => b.feePerVsize - a.feePerVsize);
|
||||||
|
blocks[blocks.length - 1] = blocks[blocks.length - 1].concat(sortedFiltered);
|
||||||
|
}
|
||||||
|
|
||||||
// unpack the condensed blocks into proper mempool blocks
|
// unpack the condensed blocks into proper mempool blocks
|
||||||
const mempoolBlocks = blocks.map((transactions) => {
|
const mempoolBlocks = blocks.map((transactions) => {
|
||||||
return this.dataToMempoolBlocks(transactions.map(tx => {
|
return this.dataToMempoolBlocks(transactions.map(tx => {
|
||||||
@ -343,6 +370,7 @@ class MempoolBlocks {
|
|||||||
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks);
|
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks);
|
||||||
this.mempoolBlocks = mempoolBlocks;
|
this.mempoolBlocks = mempoolBlocks;
|
||||||
this.mempoolBlockDeltas = deltas;
|
this.mempoolBlockDeltas = deltas;
|
||||||
|
this.updateMinFee(this.mempoolBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mempoolBlocks;
|
return mempoolBlocks;
|
||||||
@ -371,6 +399,84 @@ class MempoolBlocks {
|
|||||||
transactions: fitTransactions.map((tx) => Common.stripTransaction(tx)),
|
transactions: fitTransactions.map((tx) => Common.stripTransaction(tx)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark all transactions with in-mempool relatives
|
||||||
|
private markRelatives(mempool: { [txid: string]: TransactionExtended }): void {
|
||||||
|
for (const tx of Object.values(mempool)) {
|
||||||
|
if (!tx.hasRelatives) {
|
||||||
|
let hasRelatives = false;
|
||||||
|
tx.vin.forEach(parent => {
|
||||||
|
if (mempool[parent.txid] != null) {
|
||||||
|
hasRelatives = true;
|
||||||
|
mempool[parent.txid].hasRelatives = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tx.hasRelatives = hasRelatives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateMarkedRelatives(mempool: { [txid: string]: TransactionExtended }, added: TransactionExtended[]): void {
|
||||||
|
const newFiltered: TransactionExtended[] = [];
|
||||||
|
for (const tx of added) {
|
||||||
|
if (!tx.hasRelatives) {
|
||||||
|
let hasRelatives = false;
|
||||||
|
tx.vin.forEach(parent => {
|
||||||
|
if (mempool[parent.txid] != null) {
|
||||||
|
hasRelatives = true;
|
||||||
|
mempool[parent.txid].hasRelatives = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tx.hasRelatives = hasRelatives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateFilteredTxs(mempool: { [txid: string]: TransactionExtended }, filterFee: number): { filtered: string[], included: TransactionExtended[] } {
|
||||||
|
const filtered: string[] = [];
|
||||||
|
const included: TransactionExtended[] = [];
|
||||||
|
for (const tx of Object.values(mempool)) {
|
||||||
|
if (!tx.hasRelatives) {
|
||||||
|
if (tx.feePerVsize < filterFee) {
|
||||||
|
// filter out tx
|
||||||
|
if (!this.filteredTxs.has(tx.txid)) {
|
||||||
|
this.filteredTxs.set(tx.txid, tx);
|
||||||
|
filtered.push(tx.txid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// include tx
|
||||||
|
if (this.filteredTxs.has(tx.txid)) {
|
||||||
|
this.filteredTxs.delete(tx.txid);
|
||||||
|
included.push(tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { filtered, included };
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateMinFee(mempoolBlocks: MempoolBlockWithTransactions[]): void {
|
||||||
|
let totalFeeRate = 0;
|
||||||
|
let totalSize = 0;
|
||||||
|
let count = 0;
|
||||||
|
if (mempoolBlocks.length === 8) {
|
||||||
|
const lastBlock = mempoolBlocks[mempoolBlocks.length - 1];
|
||||||
|
for (let i = 0; i < lastBlock.transactions.length && totalSize < 16_000_000; i++) {
|
||||||
|
totalFeeRate += lastBlock.transactions[i].rate || (lastBlock.transactions[i].fee / lastBlock.transactions[i].vsize);
|
||||||
|
totalSize += lastBlock.transactions[i].vsize;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
this.minFee = count ? (totalFeeRate / count) : 1;
|
||||||
|
} else {
|
||||||
|
this.minFee = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilterFee(): number {
|
||||||
|
const purgingBelow = Mempool.getMempoolInfo().mempoolminfee * 100000;
|
||||||
|
const filterFee = Math.max(purgingBelow, this.minFee);
|
||||||
|
return filterFee;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new MempoolBlocks();
|
export default new MempoolBlocks();
|
||||||
|
@ -80,6 +80,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
|
|||||||
descendants?: Ancestor[];
|
descendants?: Ancestor[];
|
||||||
bestDescendant?: BestDescendant | null;
|
bestDescendant?: BestDescendant | null;
|
||||||
cpfpChecked?: boolean;
|
cpfpChecked?: boolean;
|
||||||
|
hasRelatives?: boolean;
|
||||||
position?: {
|
position?: {
|
||||||
block: number,
|
block: number,
|
||||||
vsize: number,
|
vsize: number,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user