mirror of
https://github.com/mempool/mempool.git
synced 2025-02-24 06:47:52 +01:00
Use minfee node to limit gbt input size
This commit is contained in:
parent
5c430e57a4
commit
e2d3bb4cc5
6 changed files with 176 additions and 56 deletions
|
@ -1,6 +1,6 @@
|
|||
import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction, ThreadAcceleration as RustThreadAcceleration } from 'rust-gbt';
|
||||
import logger from '../logger';
|
||||
import { MempoolBlock, MempoolTransactionExtended, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag, TransactionClassified, TransactionCompressed, MempoolDeltaChange } from '../mempool.interfaces';
|
||||
import { MempoolBlock, MempoolTransactionExtended, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, TransactionClassified, TransactionCompressed, MempoolDeltaChange, GbtCandidates } from '../mempool.interfaces';
|
||||
import { Common, OnlineFeeStatsCalculator } from './common';
|
||||
import config from '../config';
|
||||
import { Worker } from 'worker_threads';
|
||||
|
@ -40,12 +40,9 @@ class MempoolBlocks {
|
|||
return this.mempoolBlockDeltas;
|
||||
}
|
||||
|
||||
public updateMempoolBlocks(memPool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false): MempoolBlockWithTransactions[] {
|
||||
public updateMempoolBlocks(transactions: string[], memPool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, saveResults: boolean = false): MempoolBlockWithTransactions[] {
|
||||
const latestMempool = memPool;
|
||||
const memPoolArray: MempoolTransactionExtended[] = [];
|
||||
for (const i in latestMempool) {
|
||||
memPoolArray.push(latestMempool[i]);
|
||||
}
|
||||
const memPoolArray: MempoolTransactionExtended[] = transactions.map(txid => memPool[txid]);
|
||||
const start = new Date().getTime();
|
||||
|
||||
// Clear bestDescendants & ancestors
|
||||
|
@ -207,7 +204,7 @@ class MempoolBlocks {
|
|||
return mempoolBlockDeltas;
|
||||
}
|
||||
|
||||
public async $makeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
public async $makeBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
const start = Date.now();
|
||||
|
||||
// reset mempool short ids
|
||||
|
@ -215,8 +212,9 @@ class MempoolBlocks {
|
|||
this.resetUids();
|
||||
}
|
||||
// set missing short ids
|
||||
for (const tx of Object.values(newMempool)) {
|
||||
this.setUid(tx, !saveResults);
|
||||
for (const txid of transactions) {
|
||||
const tx = newMempool[txid];
|
||||
this.setUid(tx, false);
|
||||
}
|
||||
|
||||
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
||||
|
@ -224,7 +222,8 @@ class MempoolBlocks {
|
|||
// 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
|
||||
const strippedMempool: Map<number, CompactThreadTransaction> = new Map();
|
||||
Object.values(newMempool).forEach(entry => {
|
||||
for (const txid of transactions) {
|
||||
const entry = newMempool[txid];
|
||||
if (entry.uid !== null && entry.uid !== undefined) {
|
||||
const stripped = {
|
||||
uid: entry.uid,
|
||||
|
@ -237,7 +236,7 @@ class MempoolBlocks {
|
|||
};
|
||||
strippedMempool.set(entry.uid, stripped);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// (re)initialize tx selection worker thread
|
||||
if (!this.txSelectionWorker) {
|
||||
|
@ -268,7 +267,7 @@ class MempoolBlocks {
|
|||
// clean up thread error listener
|
||||
this.txSelectionWorker?.removeListener('error', threadErrorListener);
|
||||
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, accelerationPool, saveResults);
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), candidates, accelerations, accelerationPool, saveResults);
|
||||
|
||||
logger.debug(`makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
||||
|
||||
|
@ -279,10 +278,10 @@ class MempoolBlocks {
|
|||
return this.mempoolBlocks;
|
||||
}
|
||||
|
||||
public async $updateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], accelerationDelta: string[] = [], saveResults: boolean = false, useAccelerations: boolean = false): Promise<void> {
|
||||
public async $updateBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], candidates: GbtCandidates | undefined, accelerationDelta: string[] = [], saveResults: boolean = false, useAccelerations: boolean = false): Promise<void> {
|
||||
if (!this.txSelectionWorker) {
|
||||
// need to reset the worker
|
||||
await this.$makeBlockTemplates(newMempool, saveResults, useAccelerations);
|
||||
await this.$makeBlockTemplates(transactions, newMempool, candidates, saveResults, useAccelerations);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,7 +291,7 @@ class MempoolBlocks {
|
|||
const addedAndChanged: MempoolTransactionExtended[] = useAccelerations ? accelerationDelta.map(txid => newMempool[txid]).filter(tx => tx != null).concat(added) : added;
|
||||
|
||||
for (const tx of addedAndChanged) {
|
||||
this.setUid(tx, true);
|
||||
this.setUid(tx, false);
|
||||
}
|
||||
const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => uid != null) as number[];
|
||||
|
||||
|
@ -328,7 +327,7 @@ class MempoolBlocks {
|
|||
// clean up thread error listener
|
||||
this.txSelectionWorker?.removeListener('error', threadErrorListener);
|
||||
|
||||
this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, null, saveResults);
|
||||
this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), candidates, accelerations, null, saveResults);
|
||||
logger.debug(`updateBlockTemplates completed in ${(Date.now() - start) / 1000} seconds`);
|
||||
} catch (e) {
|
||||
logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
|
||||
|
@ -336,23 +335,26 @@ class MempoolBlocks {
|
|||
}
|
||||
|
||||
private resetRustGbt(): void {
|
||||
console.log('reset rust gbt');
|
||||
this.rustInitialized = false;
|
||||
this.rustGbtGenerator = new GbtGenerator();
|
||||
}
|
||||
|
||||
public async $rustMakeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
public async $rustMakeBlockTemplates(txids: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
const start = Date.now();
|
||||
|
||||
// reset mempool short ids
|
||||
if (saveResults) {
|
||||
this.resetUids();
|
||||
}
|
||||
|
||||
const transactions = txids.map(txid => newMempool[txid]).filter(tx => tx != null);
|
||||
// set missing short ids
|
||||
for (const tx of Object.values(newMempool)) {
|
||||
this.setUid(tx, !saveResults);
|
||||
for (const tx of transactions) {
|
||||
this.setUid(tx, false);
|
||||
}
|
||||
// set short ids for transaction inputs
|
||||
for (const tx of Object.values(newMempool)) {
|
||||
for (const tx of transactions) {
|
||||
tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[];
|
||||
}
|
||||
|
||||
|
@ -369,15 +371,15 @@ class MempoolBlocks {
|
|||
const rustGbt = saveResults ? this.rustGbtGenerator : new GbtGenerator();
|
||||
try {
|
||||
const { blocks, blockWeights, rates, clusters, overflow } = this.convertNapiResultTxids(
|
||||
await rustGbt.make(Object.values(newMempool) as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid),
|
||||
await rustGbt.make(transactions as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid),
|
||||
);
|
||||
if (saveResults) {
|
||||
this.rustInitialized = true;
|
||||
}
|
||||
const mempoolSize = Object.keys(newMempool).length;
|
||||
const expectedSize = transactions.length;
|
||||
const resultMempoolSize = blocks.reduce((total, block) => total + block.length, 0) + overflow.length;
|
||||
logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${mempoolSize} in the mempool, ${overflow.length} were unmineable`);
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, saveResults);
|
||||
logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${expectedSize} in the mempool, ${overflow.length} were unmineable`);
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, candidates, accelerations, accelerationPool, saveResults);
|
||||
logger.debug(`RUST makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
||||
return processed;
|
||||
} catch (e) {
|
||||
|
@ -389,26 +391,26 @@ class MempoolBlocks {
|
|||
return this.mempoolBlocks;
|
||||
}
|
||||
|
||||
public async $oneOffRustBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
return this.$rustMakeBlockTemplates(newMempool, false, useAccelerations, accelerationPool);
|
||||
public async $oneOffRustBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, candidates: GbtCandidates | undefined, useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
return this.$rustMakeBlockTemplates(transactions, newMempool, candidates, false, useAccelerations, accelerationPool);
|
||||
}
|
||||
|
||||
public async $rustUpdateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
public async $rustUpdateBlockTemplates(transactions: string[], newMempool: { [txid: string]: MempoolTransactionExtended }, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], candidates: GbtCandidates | undefined, useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
|
||||
// GBT optimization requires that uids never get too sparse
|
||||
// as a sanity check, we should also explicitly prevent uint32 uid overflow
|
||||
if (this.nextUid + added.length >= Math.min(Math.max(262144, 2 * mempoolSize), MAX_UINT32)) {
|
||||
if (this.nextUid + added.length >= Math.min(Math.max(262144, 2 * transactions.length), MAX_UINT32)) {
|
||||
this.resetRustGbt();
|
||||
}
|
||||
|
||||
if (!this.rustInitialized) {
|
||||
// need to reset the worker
|
||||
return this.$rustMakeBlockTemplates(newMempool, true, useAccelerations, accelerationPool);
|
||||
return this.$rustMakeBlockTemplates(transactions, newMempool, candidates, true, useAccelerations, accelerationPool);
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
// set missing short ids
|
||||
for (const tx of added) {
|
||||
this.setUid(tx, true);
|
||||
this.setUid(tx, false);
|
||||
}
|
||||
// set short ids for transaction inputs
|
||||
for (const tx of added) {
|
||||
|
@ -416,6 +418,8 @@ class MempoolBlocks {
|
|||
}
|
||||
const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => (uid !== null && uid !== undefined)) as number[];
|
||||
|
||||
console.log(`removing ${removed.length} (${removedUids.length} with uids)`);
|
||||
|
||||
const accelerations = useAccelerations ? mempool.getAccelerations() : {};
|
||||
const acceleratedList = accelerationPool ? Object.values(accelerations).filter(acc => newMempool[acc.txid] && acc.pools.includes(accelerationPool)) : Object.values(accelerations).filter(acc => newMempool[acc.txid]);
|
||||
const convertedAccelerations = acceleratedList.map(acc => {
|
||||
|
@ -425,6 +429,8 @@ class MempoolBlocks {
|
|||
};
|
||||
});
|
||||
|
||||
console.log(`${acceleratedList.length} accelerations`);
|
||||
|
||||
// run the block construction algorithm in a separate thread, and wait for a result
|
||||
try {
|
||||
const { blocks, blockWeights, rates, clusters, overflow } = this.convertNapiResultTxids(
|
||||
|
@ -436,11 +442,11 @@ class MempoolBlocks {
|
|||
),
|
||||
);
|
||||
const resultMempoolSize = blocks.reduce((total, block) => total + block.length, 0) + overflow.length;
|
||||
logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${mempoolSize} in the mempool, ${overflow.length} were unmineable`);
|
||||
if (mempoolSize !== resultMempoolSize) {
|
||||
throw new Error('GBT returned wrong number of transactions , cache is probably out of sync');
|
||||
logger.debug(`RUST updateBlockTemplates returned ${resultMempoolSize} txs out of ${transactions.length} candidates, ${overflow.length} were unmineable`);
|
||||
if (transactions.length !== resultMempoolSize) {
|
||||
throw new Error(`GBT returned wrong number of transactions ${transactions.length} vs ${resultMempoolSize}, cache is probably out of sync`);
|
||||
} else {
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, true);
|
||||
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, candidates, accelerations, accelerationPool, true);
|
||||
this.removeUids(removedUids);
|
||||
logger.debug(`RUST updateBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
|
||||
return processed;
|
||||
|
@ -452,7 +458,7 @@ class MempoolBlocks {
|
|||
}
|
||||
}
|
||||
|
||||
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
|
||||
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], candidates: GbtCandidates | undefined, accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
|
||||
for (const [txid, rate] of rates) {
|
||||
if (txid in mempool) {
|
||||
mempool[txid].cpfpDirty = (rate !== mempool[txid].effectiveFeePerVsize);
|
||||
|
@ -486,6 +492,9 @@ class MempoolBlocks {
|
|||
if (txid === memberTxid) {
|
||||
matched = true;
|
||||
} else {
|
||||
if (!mempool[txid]) {
|
||||
console.log('txid missing from mempool! ', txid, candidates?.txs[txid]);
|
||||
}
|
||||
const relative = {
|
||||
txid: txid,
|
||||
fee: mempool[txid].fee,
|
||||
|
@ -518,6 +527,16 @@ class MempoolBlocks {
|
|||
let totalWeight = 0;
|
||||
let totalFees = 0;
|
||||
const transactions: MempoolTransactionExtended[] = [];
|
||||
|
||||
// backfill purged transactions
|
||||
if (candidates?.txs && blockIndex === blocks.length - 1) {
|
||||
for (const txid of Object.keys(mempool)) {
|
||||
if (!candidates.txs[txid]) {
|
||||
block.push(txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const txid of block) {
|
||||
if (txid) {
|
||||
mempoolTx = mempool[txid];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import config from '../config';
|
||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
||||
import { MempoolTransactionExtended, TransactionExtended, VbytesPerSecond } from '../mempool.interfaces';
|
||||
import { MempoolTransactionExtended, TransactionExtended, VbytesPerSecond, GbtCandidates } from '../mempool.interfaces';
|
||||
import logger from '../logger';
|
||||
import { Common } from './common';
|
||||
import transactionUtils from './transaction-utils';
|
||||
|
@ -11,18 +11,21 @@ import bitcoinSecondClient from './bitcoin/bitcoin-second-client';
|
|||
import rbfCache from './rbf-cache';
|
||||
import { Acceleration } from './services/acceleration';
|
||||
import redisCache from './redis-cache';
|
||||
import blocks from './blocks';
|
||||
|
||||
class Mempool {
|
||||
private inSync: boolean = false;
|
||||
private mempoolCacheDelta: number = -1;
|
||||
private mempoolCache: { [txId: string]: MempoolTransactionExtended } = {};
|
||||
private mempoolCandidates: { [txid: string ]: boolean } = {};
|
||||
private minFeeMempool: { [txId: string]: boolean } = {};
|
||||
private spendMap = new Map<string, MempoolTransactionExtended>();
|
||||
private mempoolInfo: IBitcoinApi.MempoolInfo = { loaded: false, size: 0, bytes: 0, usage: 0, total_fee: 0,
|
||||
maxmempool: 300000000, mempoolminfee: Common.isLiquid() ? 0.00000100 : 0.00001000, minrelaytxfee: Common.isLiquid() ? 0.00000100 : 0.00001000 };
|
||||
private mempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, newTransactions: MempoolTransactionExtended[],
|
||||
deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => void) | undefined;
|
||||
private $asyncMempoolChangedCallback: ((newMempool: {[txId: string]: MempoolTransactionExtended; }, mempoolSize: number, newTransactions: MempoolTransactionExtended[],
|
||||
deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise<void>) | undefined;
|
||||
deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[], candidates?: GbtCandidates) => Promise<void>) | undefined;
|
||||
|
||||
private accelerations: { [txId: string]: Acceleration } = {};
|
||||
|
||||
|
@ -40,6 +43,8 @@ class Mempool {
|
|||
private missingTxCount = 0;
|
||||
private mainLoopTimeout: number = 120000;
|
||||
|
||||
public limitGBT = config.MEMPOOL.USE_SECOND_NODE_FOR_MINFEE && config.MEMPOOL.LIMIT_GBT;
|
||||
|
||||
constructor() {
|
||||
setInterval(this.updateTxPerSecond.bind(this), 1000);
|
||||
}
|
||||
|
@ -74,7 +79,8 @@ class Mempool {
|
|||
}
|
||||
|
||||
public setAsyncMempoolChangedCallback(fn: (newMempool: { [txId: string]: MempoolTransactionExtended; }, mempoolSize: number,
|
||||
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise<void>): void {
|
||||
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[],
|
||||
candidates?: GbtCandidates) => Promise<void>): void {
|
||||
this.$asyncMempoolChangedCallback = fn;
|
||||
}
|
||||
|
||||
|
@ -117,7 +123,7 @@ class Mempool {
|
|||
this.mempoolChangedCallback(this.mempoolCache, [], [], []);
|
||||
}
|
||||
if (this.$asyncMempoolChangedCallback) {
|
||||
await this.$asyncMempoolChangedCallback(this.mempoolCache, count, [], [], []);
|
||||
await this.$asyncMempoolChangedCallback(this.mempoolCache, count, [], [], [], this.limitGBT ? { txs: {}, added: [], removed: [] } : undefined);
|
||||
}
|
||||
this.addToSpendMap(Object.values(this.mempoolCache));
|
||||
}
|
||||
|
@ -160,6 +166,10 @@ class Mempool {
|
|||
return newTransactions;
|
||||
}
|
||||
|
||||
public getMempoolCandidates(): { [txid: string]: boolean } {
|
||||
return this.mempoolCandidates;
|
||||
}
|
||||
|
||||
public async $updateMemPoolInfo() {
|
||||
this.mempoolInfo = await this.$getMempoolInfo();
|
||||
}
|
||||
|
@ -189,7 +199,7 @@ class Mempool {
|
|||
return txTimes;
|
||||
}
|
||||
|
||||
public async $updateMempool(transactions: string[], accelerations: Acceleration[] | null, pollRate: number): Promise<void> {
|
||||
public async $updateMempool(transactions: string[], accelerations: Acceleration[] | null, minFeeMempool: string[], minFeeTip: number, pollRate: number): Promise<void> {
|
||||
logger.debug(`Updating mempool...`);
|
||||
|
||||
// warn if this run stalls the main loop for more than 2 minutes
|
||||
|
@ -311,6 +321,8 @@ class Mempool {
|
|||
}, 1000 * 60 * config.MEMPOOL.CLEAR_PROTECTION_MINUTES);
|
||||
}
|
||||
|
||||
const candidates = await this.getNextCandidates(minFeeMempool, minFeeTip);
|
||||
|
||||
const deletedTransactions: MempoolTransactionExtended[] = [];
|
||||
|
||||
if (this.mempoolProtection !== 1) {
|
||||
|
@ -341,12 +353,14 @@ class Mempool {
|
|||
|
||||
this.mempoolCacheDelta = Math.abs(transactions.length - newMempoolSize);
|
||||
|
||||
const candidatesChanged = candidates?.added?.length || candidates?.removed?.length;
|
||||
|
||||
if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) {
|
||||
this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions, accelerationDelta);
|
||||
}
|
||||
if (this.$asyncMempoolChangedCallback && (hasChange || deletedTransactions.length)) {
|
||||
if (this.$asyncMempoolChangedCallback && (hasChange || deletedTransactions.length || candidatesChanged)) {
|
||||
this.updateTimerProgress(timer, 'running async mempool callback');
|
||||
await this.$asyncMempoolChangedCallback(this.mempoolCache, newMempoolSize, newTransactions, deletedTransactions, accelerationDelta);
|
||||
await this.$asyncMempoolChangedCallback(this.mempoolCache, newMempoolSize, newTransactions, deletedTransactions, accelerationDelta, candidates);
|
||||
this.updateTimerProgress(timer, 'completed async mempool callback');
|
||||
}
|
||||
|
||||
|
@ -432,6 +446,48 @@ class Mempool {
|
|||
}
|
||||
}
|
||||
|
||||
public async getNextCandidates(minFeeTransactions: string[], blockHeight: number): Promise<GbtCandidates | undefined> {
|
||||
if (this.limitGBT) {
|
||||
const newCandidateTxMap = {};
|
||||
this.minFeeMempool = {};
|
||||
for (const txid of minFeeTransactions) {
|
||||
if (this.mempoolCache[txid]) {
|
||||
newCandidateTxMap[txid] = true;
|
||||
}
|
||||
this.minFeeMempool[txid] = true;
|
||||
}
|
||||
const removed: MempoolTransactionExtended[] = [];
|
||||
const added: MempoolTransactionExtended[] = [];
|
||||
// don't prematurely remove txs included in a new block
|
||||
if (blockHeight > blocks.getCurrentBlockHeight()) {
|
||||
for (const txid of Object.keys(this.mempoolCandidates)) {
|
||||
newCandidateTxMap[txid] = true;
|
||||
}
|
||||
} else {
|
||||
for (const txid of Object.keys(this.mempoolCandidates)) {
|
||||
if (!newCandidateTxMap[txid]) {
|
||||
const tx = this.mempoolCache[txid];
|
||||
removed.push(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const txid of Object.keys(newCandidateTxMap)) {
|
||||
if (!this.mempoolCandidates[txid]) {
|
||||
const tx = this.mempoolCache[txid];
|
||||
added.push(tx);
|
||||
}
|
||||
}
|
||||
|
||||
this.mempoolCandidates = newCandidateTxMap;
|
||||
return {
|
||||
txs: this.mempoolCandidates,
|
||||
added,
|
||||
removed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private startTimer() {
|
||||
const state: any = {
|
||||
start: Date.now(),
|
||||
|
|
|
@ -2,7 +2,7 @@ import logger from '../logger';
|
|||
import * as WebSocket from 'ws';
|
||||
import {
|
||||
BlockExtended, TransactionExtended, MempoolTransactionExtended, WebsocketResponse,
|
||||
OptimizedStatistic, ILoadingIndicators
|
||||
OptimizedStatistic, ILoadingIndicators, GbtCandidates,
|
||||
} from '../mempool.interfaces';
|
||||
import blocks from './blocks';
|
||||
import memPool from './mempool';
|
||||
|
@ -32,6 +32,7 @@ interface AddressTransactions {
|
|||
confirmed: MempoolTransactionExtended[],
|
||||
removed: MempoolTransactionExtended[],
|
||||
}
|
||||
import bitcoinSecondClient from './bitcoin/bitcoin-second-client';
|
||||
|
||||
// valid 'want' subscriptions
|
||||
const wantable = [
|
||||
|
@ -436,21 +437,33 @@ class WebsocketHandler {
|
|||
}
|
||||
|
||||
async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number,
|
||||
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]): Promise<void> {
|
||||
newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[],
|
||||
candidates?: GbtCandidates): Promise<void> {
|
||||
if (!this.wss) {
|
||||
throw new Error('WebSocket.Server is not set');
|
||||
}
|
||||
|
||||
this.printLogs();
|
||||
|
||||
const transactionIds = (memPool.limitGBT && candidates) ? Object.keys(candidates?.txs || {}) : Object.keys(newMempool);
|
||||
let added = newTransactions;
|
||||
let removed = deletedTransactions;
|
||||
console.log(`handleMempoolChange: ${newTransactions.length} new txs, ${deletedTransactions.length} removed, ${candidates?.added.length} candidate added, ${candidates?.removed.length} candidate removed`);
|
||||
console.log(`mempool size ${Object.keys(newMempool).length}, candidates: ${transactionIds.length}`);
|
||||
if (memPool.limitGBT) {
|
||||
console.log('GBT on limited mempool...');
|
||||
added = candidates?.added || [];
|
||||
removed = candidates?.removed || [];
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) {
|
||||
if (config.MEMPOOL.RUST_GBT) {
|
||||
await mempoolBlocks.$rustUpdateBlockTemplates(newMempool, mempoolSize, newTransactions, deletedTransactions, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, newMempool, added, removed, candidates, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
} else {
|
||||
await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
await mempoolBlocks.$updateBlockTemplates(transactionIds, newMempool, added, removed, candidates, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
}
|
||||
} else {
|
||||
mempoolBlocks.updateMempoolBlocks(newMempool, true);
|
||||
mempoolBlocks.updateMempoolBlocks(transactionIds, newMempool, candidates, true);
|
||||
}
|
||||
|
||||
const mBlocks = mempoolBlocks.getMempoolBlocks();
|
||||
|
@ -739,6 +752,9 @@ class WebsocketHandler {
|
|||
await statistics.runStatistics();
|
||||
|
||||
const _memPool = memPool.getMempool();
|
||||
const candidateTxs = await memPool.getMempoolCandidates();
|
||||
let candidates: GbtCandidates | undefined = (memPool.limitGBT && candidateTxs) ? { txs: candidateTxs, added: [], removed: [] } : undefined;
|
||||
let transactionIds: string[] = (memPool.limitGBT && candidates) ? Object.keys(_memPool) : Object.keys(candidates?.txs || {});
|
||||
|
||||
const isAccelerated = config.MEMPOOL_SERVICES.ACCELERATIONS && accelerationApi.isAcceleratedBlock(block, Object.values(mempool.getAccelerations()));
|
||||
|
||||
|
@ -759,19 +775,23 @@ class WebsocketHandler {
|
|||
auditMempool = deepClone(_memPool);
|
||||
if (config.MEMPOOL.ADVANCED_GBT_AUDIT) {
|
||||
if (config.MEMPOOL.RUST_GBT) {
|
||||
projectedBlocks = await mempoolBlocks.$oneOffRustBlockTemplates(auditMempool, isAccelerated, block.extras.pool.id);
|
||||
projectedBlocks = await mempoolBlocks.$oneOffRustBlockTemplates(transactionIds, auditMempool, candidates, isAccelerated, block.extras.pool.id);
|
||||
} else {
|
||||
projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id);
|
||||
projectedBlocks = await mempoolBlocks.$makeBlockTemplates(transactionIds, auditMempool, candidates, false, isAccelerated, block.extras.pool.id);
|
||||
}
|
||||
} else {
|
||||
projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false);
|
||||
projectedBlocks = mempoolBlocks.updateMempoolBlocks(transactionIds, auditMempool, candidates, false);
|
||||
}
|
||||
} else {
|
||||
if ((config.MEMPOOL_SERVICES.ACCELERATIONS)) {
|
||||
if (config.MEMPOOL.RUST_GBT) {
|
||||
projectedBlocks = await mempoolBlocks.$rustUpdateBlockTemplates(auditMempool, Object.keys(auditMempool).length, [], [], isAccelerated, block.extras.pool.id);
|
||||
console.log(`handleNewBlock: ${transactions.length} mined txs, ${candidates?.added.length} candidate added, ${candidates?.removed.length} candidate removed`);
|
||||
console.log(`mempool size ${Object.keys(auditMempool).length}, candidates: ${transactionIds.length}`);
|
||||
let added = memPool.limitGBT ? (candidates?.added || []) : [];
|
||||
let removed = memPool.limitGBT ? (candidates?.removed || []) : [];
|
||||
projectedBlocks = await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, auditMempool, added, removed, candidates, isAccelerated, block.extras.pool.id);
|
||||
} else {
|
||||
projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id);
|
||||
projectedBlocks = await mempoolBlocks.$makeBlockTemplates(transactionIds, auditMempool, candidates, false, isAccelerated, block.extras.pool.id);
|
||||
}
|
||||
} else {
|
||||
projectedBlocks = mempoolBlocks.getMempoolBlocksWithTransactions();
|
||||
|
@ -838,14 +858,28 @@ class WebsocketHandler {
|
|||
confirmedTxids[txId] = true;
|
||||
}
|
||||
|
||||
if (memPool.limitGBT) {
|
||||
const minFeeMempool = memPool.limitGBT ? await bitcoinSecondClient.getRawMemPool() : null;
|
||||
const minFeeTip = memPool.limitGBT ? await bitcoinSecondClient.getBlockCount() : -1;
|
||||
candidates = await memPool.getNextCandidates(minFeeMempool, minFeeTip);
|
||||
transactionIds = Object.keys(candidates?.txs || {});
|
||||
} else {
|
||||
candidates = undefined;
|
||||
transactionIds = Object.keys(memPool.getMempool());
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) {
|
||||
if (config.MEMPOOL.RUST_GBT) {
|
||||
await mempoolBlocks.$rustUpdateBlockTemplates(_memPool, Object.keys(_memPool).length, [], transactions, true);
|
||||
console.log(`handleNewBlock (after): ${transactions.length} mined txs, ${candidates?.added.length} candidate added, ${candidates?.removed.length} candidate removed`);
|
||||
console.log(`mempool size ${Object.keys(_memPool).length}, candidates: ${transactionIds.length}`);
|
||||
let added = memPool.limitGBT ? (candidates?.added || []) : [];
|
||||
let removed = memPool.limitGBT ? (candidates?.removed || []) : transactions;
|
||||
await mempoolBlocks.$rustUpdateBlockTemplates(transactionIds, _memPool, added, removed, candidates, true);
|
||||
} else {
|
||||
await mempoolBlocks.$makeBlockTemplates(_memPool, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
await mempoolBlocks.$makeBlockTemplates(transactionIds, _memPool, candidates, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
|
||||
}
|
||||
} else {
|
||||
mempoolBlocks.updateMempoolBlocks(_memPool, true);
|
||||
mempoolBlocks.updateMempoolBlocks(transactionIds, _memPool, candidates, true);
|
||||
}
|
||||
const mBlocks = mempoolBlocks.getMempoolBlocks();
|
||||
const mBlockDeltas = mempoolBlocks.getMempoolBlockDeltas();
|
||||
|
|
|
@ -35,6 +35,7 @@ interface IConfig {
|
|||
ADVANCED_GBT_AUDIT: boolean;
|
||||
ADVANCED_GBT_MEMPOOL: boolean;
|
||||
RUST_GBT: boolean;
|
||||
LIMIT_GBT: boolean;
|
||||
CPFP_INDEXING: boolean;
|
||||
MAX_BLOCKS_BULK_QUERY: number;
|
||||
DISK_CACHE_BLOCK_INTERVAL: number;
|
||||
|
@ -197,6 +198,7 @@ const defaults: IConfig = {
|
|||
'ADVANCED_GBT_AUDIT': false,
|
||||
'ADVANCED_GBT_MEMPOOL': false,
|
||||
'RUST_GBT': false,
|
||||
'LIMIT_GBT': false,
|
||||
'CPFP_INDEXING': false,
|
||||
'MAX_BLOCKS_BULK_QUERY': 0,
|
||||
'DISK_CACHE_BLOCK_INTERVAL': 6,
|
||||
|
|
|
@ -45,6 +45,7 @@ import { formatBytes, getBytesUnit } from './utils/format';
|
|||
import redisCache from './api/redis-cache';
|
||||
import accelerationApi from './api/services/acceleration';
|
||||
import bitcoinCoreRoutes from './api/bitcoin/bitcoin-core.routes';
|
||||
import bitcoinSecondClient from './api/bitcoin/bitcoin-second-client';
|
||||
|
||||
class Server {
|
||||
private wss: WebSocket.Server | undefined;
|
||||
|
@ -215,11 +216,13 @@ class Server {
|
|||
}
|
||||
}
|
||||
const newMempool = await bitcoinApi.$getRawMempool();
|
||||
const minFeeMempool = memPool.limitGBT ? await bitcoinSecondClient.getRawMemPool() : null;
|
||||
const minFeeTip = memPool.limitGBT ? await bitcoinSecondClient.getBlockCount() : -1;
|
||||
const newAccelerations = await accelerationApi.$fetchAccelerations();
|
||||
const numHandledBlocks = await blocks.$updateBlocks();
|
||||
const pollRate = config.MEMPOOL.POLL_RATE_MS * (indexer.indexerIsRunning() ? 10 : 1);
|
||||
if (numHandledBlocks === 0) {
|
||||
await memPool.$updateMempool(newMempool, newAccelerations, pollRate);
|
||||
await memPool.$updateMempool(newMempool, newAccelerations, minFeeMempool, minFeeTip, pollRate);
|
||||
}
|
||||
indexer.$run();
|
||||
if (config.FIAT_PRICE.ENABLED) {
|
||||
|
|
|
@ -143,6 +143,12 @@ export interface CompactThreadTransaction {
|
|||
dirty?: boolean;
|
||||
}
|
||||
|
||||
export interface GbtCandidates {
|
||||
txs: { [txid: string ]: boolean },
|
||||
added: MempoolTransactionExtended[];
|
||||
removed: MempoolTransactionExtended[];
|
||||
}
|
||||
|
||||
export interface ThreadTransaction {
|
||||
txid: string;
|
||||
fee: number;
|
||||
|
|
Loading…
Add table
Reference in a new issue