Estimate accelerated positions in partner mempools

This commit is contained in:
Mononaut 2024-02-02 02:25:49 +00:00
parent e4d92c8fe0
commit ccc127c84a
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
4 changed files with 115 additions and 6 deletions

View File

@ -6,6 +6,7 @@ import config from '../config';
import { Worker } from 'worker_threads';
import path from 'path';
import mempool from './mempool';
import { Acceleration } from './services/acceleration';
const MAX_UINT32 = Math.pow(2, 32) - 1;
@ -452,7 +453,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[][], accelerations: { [txid: string]: Acceleration }, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
for (const [txid, rate] of rates) {
if (txid in mempool) {
mempool[txid].cpfpDirty = (rate !== mempool[txid].effectiveFeePerVsize);
@ -586,7 +587,7 @@ class MempoolBlocks {
const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks);
this.mempoolBlocks = mempoolBlocks;
this.mempoolBlockDeltas = deltas;
this.updateAccelerationPositions(mempool, accelerations, mempoolBlocks);
}
return mempoolBlocks;
@ -691,6 +692,94 @@ class MempoolBlocks {
});
return { blocks: convertedBlocks, blockWeights, rates: convertedRates, clusters: convertedClusters, overflow: convertedOverflow };
}
// estimates and saves positions of accelerations in mining partner mempools
private updateAccelerationPositions(mempoolCache: { [txid: string]: MempoolTransactionExtended }, accelerations: { [txid: string]: Acceleration }, mempoolBlocks: MempoolBlockWithTransactions[]): void {
const accelerationPositions: { [txid: string]: { [pool: string]: { block: number, vbytes: number } } } = {};
// keep track of simulated mempool blocks for each active pool
const pools: {
[pool: string]: { block: number, vsize: number, accelerations: string[] };
} = {};
// prepare a list of accelerations in ascending order (we'll pop items off the end of the list)
const accQueue: { acceleration: Acceleration, rate: number, vsize: number }[] = Object.values(accelerations).map(acc => {
let vsize = mempoolCache[acc.txid].vsize;
for (const ancestor of mempoolCache[acc.txid].ancestors || []) {
vsize += (ancestor.weight / 4);
}
return {
acceleration: acc,
rate: mempoolCache[acc.txid].effectiveFeePerVsize,
vsize
};
}).sort((a, b) => a.rate - b.rate);
// initialize the pool tracker
for (const { acceleration } of accQueue) {
accelerationPositions[acceleration.txid] = {};
for (const pool of acceleration.pools) {
if (!pools[pool]) {
pools[pool] = {
block: 0,
vsize: 0,
accelerations: [],
};
}
pools[pool].accelerations.push(acceleration.txid);
}
for (const ancestor of mempoolCache[acceleration.txid].ancestors || []) {
accelerationPositions[ancestor.txid] = {};
}
}
let block = 0;
let index = 0;
let next = accQueue.pop();
// build simulated blocks for each pool by taking the best option from
// either the mempool or the list of accelerations.
while (next && block < mempoolBlocks.length) {
while (next && index < mempoolBlocks[block].transactions.length) {
const nextTx = mempoolBlocks[block].transactions[index];
if (next.rate >= (nextTx.rate || (nextTx.fee / nextTx.vsize))) {
for (const pool of next.acceleration.pools) {
if (pools[pool].vsize + next.vsize <= 999_000) {
pools[pool].vsize += next.vsize;
} else {
pools[pool].block++;
pools[pool].vsize = next.vsize;
}
// insert the acceleration into matching pool's blocks
accelerationPositions[next.acceleration.txid][pool] = {
block: pools[pool].block,
vbytes: pools[pool].vsize - (next.vsize / 2),
};
// and any accelerated ancestors
for (const ancestor of mempoolCache[next.acceleration.txid].ancestors || []) {
accelerationPositions[ancestor.txid][pool] = {
block: pools[pool].block,
vbytes: pools[pool].vsize - (next.vsize / 2),
};
}
}
next = accQueue.pop();
} else {
// skip accelerated transactions and their CPFP ancestors
if (accelerationPositions[nextTx.txid] == null) {
// insert into all pools' blocks
for (const pool of Object.keys(pools)) {
if (pools[pool].vsize + nextTx.vsize <= 999_000) {
pools[pool].vsize += nextTx.vsize;
} else {
pools[pool].block++;
pools[pool].vsize = nextTx.vsize;
}
}
}
index++;
}
}
block++;
index = 0;
}
mempool.setAccelerationPositions(accelerationPositions);
}
}
export default new MempoolBlocks();

View File

@ -25,6 +25,7 @@ class Mempool {
deletedTransactions: MempoolTransactionExtended[], accelerationDelta: string[]) => Promise<void>) | undefined;
private accelerations: { [txId: string]: Acceleration } = {};
private accelerationPositions: { [txid: string]: { [pool: number]: { block: number, vbytes: number } } } = {};
private txPerSecondArray: number[] = [];
private txPerSecond: number = 0;
@ -431,6 +432,14 @@ class Mempool {
}
}
setAccelerationPositions(positions: { [txid: string]: { [pool: number]: { block: number, vbytes: number } } }): void {
this.accelerationPositions = positions;
}
getAccelerationPositions(txid: string): { [pool: number]: { block: number, vbytes: number } } | undefined {
return this.accelerationPositions[txid];
}
private startTimer() {
const state: any = {
start: Date.now(),

View File

@ -7,6 +7,14 @@ export interface Acceleration {
txid: string,
feeDelta: number,
pools: number[],
effectiveFee: number;
effectiveVsize: number;
positions?: {
[pool: number]: {
block: number,
vbytes: number,
},
},
}
class AccelerationApi {

View File

@ -192,7 +192,8 @@ class WebsocketHandler {
}
response['txPosition'] = JSON.stringify({
txid: trackTxid,
position
position,
accelerationPositions: memPool.getAccelerationPositions(tx.txid),
});
}
} else {
@ -674,7 +675,8 @@ class WebsocketHandler {
position: {
...mempoolTx.position,
accelerated: mempoolTx.acceleration || undefined,
}
},
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
};
if (mempoolTx.cpfpDirty) {
positionData['cpfp'] = {
@ -684,7 +686,7 @@ class WebsocketHandler {
effectiveFeePerVsize: mempoolTx.effectiveFeePerVsize || null,
sigops: mempoolTx.sigops,
adjustedVsize: mempoolTx.adjustedVsize,
acceleration: mempoolTx.acceleration
acceleration: mempoolTx.acceleration,
};
}
response['txPosition'] = JSON.stringify(positionData);
@ -896,7 +898,8 @@ class WebsocketHandler {
position: {
...mempoolTx.position,
accelerated: mempoolTx.acceleration || undefined,
}
},
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
});
}
}