Add acceleration support to rust gbt

This commit is contained in:
Mononaut 2023-07-18 15:05:44 +09:00
parent 6494f890fe
commit ffc2b6c53c
No known key found for this signature in database
GPG key ID: A3F058E41374C04E
14 changed files with 96 additions and 45 deletions

View file

@ -12,6 +12,10 @@ export interface ThreadTransaction {
effectiveFeePerVsize: number effectiveFeePerVsize: number
inputs: Array<number> inputs: Array<number>
} }
export interface ThreadAcceleration {
uid: number
delta: number
}
export class GbtGenerator { export class GbtGenerator {
constructor() constructor()
/** /**
@ -19,13 +23,13 @@ export class GbtGenerator {
* *
* Rejects if the thread panics or if the Mutex is poisoned. * Rejects if the thread panics or if the Mutex is poisoned.
*/ */
make(mempool: Array<ThreadTransaction>, maxUid: number): Promise<GbtResult> make(mempool: Array<ThreadTransaction>, accelerations: Array<ThreadAcceleration>, maxUid: number): Promise<GbtResult>
/** /**
* # Errors * # Errors
* *
* Rejects if the thread panics or if the Mutex is poisoned. * Rejects if the thread panics or if the Mutex is poisoned.
*/ */
update(newTxs: Array<ThreadTransaction>, removeTxs: Array<number>, maxUid: number): Promise<GbtResult> update(newTxs: Array<ThreadTransaction>, removeTxs: Array<number>, accelerations: Array<ThreadAcceleration>, maxUid: number): Promise<GbtResult>
} }
/** /**
* The result from calling the gbt function. * The result from calling the gbt function.

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
u32_hasher_types::{u32hashset_new, U32HasherState}, u32_hasher_types::{u32hashset_new, U32HasherState},
ThreadTransaction, ThreadTransaction, thread_acceleration::ThreadAcceleration,
}; };
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
@ -88,37 +88,42 @@ impl Ord for AuditTransaction {
} }
#[inline] #[inline]
fn calc_fee_rate(fee: f64, vsize: f64) -> f64 { fn calc_fee_rate(fee: u64, vsize: f64) -> f64 {
fee / (if vsize == 0.0 { 1.0 } else { vsize }) (fee as f64) / (if vsize == 0.0 { 1.0 } else { vsize })
} }
impl AuditTransaction { impl AuditTransaction {
pub fn from_thread_transaction(tx: &ThreadTransaction) -> Self { pub fn from_thread_transaction(tx: &ThreadTransaction, maybe_acceleration: Option<Option<&ThreadAcceleration>>) -> Self {
let fee_delta = match maybe_acceleration {
Some(Some(acceleration)) => acceleration.delta,
_ => 0.0
};
let fee = (tx.fee as u64) + (fee_delta as u64);
// rounded up to the nearest integer // rounded up to the nearest integer
let is_adjusted = tx.weight < (tx.sigops * 20); let is_adjusted = tx.weight < (tx.sigops * 20);
let sigop_adjusted_vsize = ((tx.weight + 3) / 4).max(tx.sigops * 5); let sigop_adjusted_vsize = ((tx.weight + 3) / 4).max(tx.sigops * 5);
let sigop_adjusted_weight = tx.weight.max(tx.sigops * 20); let sigop_adjusted_weight = tx.weight.max(tx.sigops * 20);
let effective_fee_per_vsize = if is_adjusted { let effective_fee_per_vsize = if is_adjusted || fee_delta > 0.0 {
calc_fee_rate(tx.fee, f64::from(sigop_adjusted_weight) / 4.0) calc_fee_rate(fee, f64::from(sigop_adjusted_weight) / 4.0)
} else { } else {
tx.effective_fee_per_vsize tx.effective_fee_per_vsize
}; };
Self { Self {
uid: tx.uid, uid: tx.uid,
order: tx.order, order: tx.order,
fee: tx.fee as u64, fee,
weight: tx.weight, weight: tx.weight,
sigop_adjusted_weight, sigop_adjusted_weight,
sigop_adjusted_vsize, sigop_adjusted_vsize,
sigops: tx.sigops, sigops: tx.sigops,
adjusted_fee_per_vsize: calc_fee_rate(tx.fee, f64::from(sigop_adjusted_vsize)), adjusted_fee_per_vsize: calc_fee_rate(fee, f64::from(sigop_adjusted_vsize)),
effective_fee_per_vsize, effective_fee_per_vsize,
dependency_rate: f64::INFINITY, dependency_rate: f64::INFINITY,
inputs: tx.inputs.clone(), inputs: tx.inputs.clone(),
relatives_set_flag: false, relatives_set_flag: false,
ancestors: u32hashset_new(), ancestors: u32hashset_new(),
children: u32hashset_new(), children: u32hashset_new(),
ancestor_fee: tx.fee as u64, ancestor_fee: fee,
ancestor_sigop_adjusted_weight: sigop_adjusted_weight, ancestor_sigop_adjusted_weight: sigop_adjusted_weight,
ancestor_sigop_adjusted_vsize: sigop_adjusted_vsize, ancestor_sigop_adjusted_vsize: sigop_adjusted_vsize,
ancestor_sigops: tx.sigops, ancestor_sigops: tx.sigops,
@ -156,7 +161,7 @@ impl AuditTransaction {
// grows, so if we think of 0 as "grew infinitely" then dependency_rate would be // grows, so if we think of 0 as "grew infinitely" then dependency_rate would be
// the smaller of the two. If either side is NaN, the other side is returned. // the smaller of the two. If either side is NaN, the other side is returned.
self.dependency_rate.min(calc_fee_rate( self.dependency_rate.min(calc_fee_rate(
self.ancestor_fee as f64, self.ancestor_fee,
f64::from(self.ancestor_sigop_adjusted_weight) / 4.0, f64::from(self.ancestor_sigop_adjusted_weight) / 4.0,
)) ))
} }
@ -172,7 +177,7 @@ impl AuditTransaction {
#[inline] #[inline]
fn calc_new_score(&mut self) { fn calc_new_score(&mut self) {
self.score = self.adjusted_fee_per_vsize.min(calc_fee_rate( self.score = self.adjusted_fee_per_vsize.min(calc_fee_rate(
self.ancestor_fee as f64, self.ancestor_fee,
f64::from(self.ancestor_sigop_adjusted_vsize), f64::from(self.ancestor_sigop_adjusted_vsize),
)); ));
} }

View file

@ -5,7 +5,7 @@ use tracing::{info, trace};
use crate::{ use crate::{
audit_transaction::{partial_cmp_uid_score, AuditTransaction}, audit_transaction::{partial_cmp_uid_score, AuditTransaction},
u32_hasher_types::{u32hashset_new, u32priority_queue_with_capacity, U32HasherState}, u32_hasher_types::{u32hashset_new, u32priority_queue_with_capacity, U32HasherState},
GbtResult, ThreadTransactionsMap, GbtResult, ThreadTransactionsMap, thread_acceleration::ThreadAcceleration,
}; };
const MAX_BLOCK_WEIGHT_UNITS: u32 = 4_000_000 - 4_000; const MAX_BLOCK_WEIGHT_UNITS: u32 = 4_000_000 - 4_000;
@ -53,7 +53,13 @@ impl Ord for TxPriority {
// TODO: Make gbt smaller to fix these lints. // TODO: Make gbt smaller to fix these lints.
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn gbt(mempool: &mut ThreadTransactionsMap, max_uid: usize) -> GbtResult { pub fn gbt(mempool: &mut ThreadTransactionsMap, accelerations: &[ThreadAcceleration], max_uid: usize) -> GbtResult {
let mut indexed_accelerations = Vec::with_capacity(max_uid + 1);
indexed_accelerations.resize(max_uid + 1, None);
for acceleration in accelerations {
indexed_accelerations[acceleration.uid as usize] = Some(acceleration);
}
let mempool_len = mempool.len(); let mempool_len = mempool.len();
let mut audit_pool: AuditPool = Vec::with_capacity(max_uid + 1); let mut audit_pool: AuditPool = Vec::with_capacity(max_uid + 1);
audit_pool.resize(max_uid + 1, None); audit_pool.resize(max_uid + 1, None);
@ -63,7 +69,8 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap, max_uid: usize) -> GbtResult {
info!("Initializing working structs"); info!("Initializing working structs");
for (uid, tx) in &mut *mempool { for (uid, tx) in &mut *mempool {
let audit_tx = AuditTransaction::from_thread_transaction(tx); let acceleration = indexed_accelerations.get(*uid as usize);
let audit_tx = AuditTransaction::from_thread_transaction(tx, acceleration.copied());
// Safety: audit_pool and mempool_stack must always contain the same transactions // Safety: audit_pool and mempool_stack must always contain the same transactions
audit_pool[*uid as usize] = Some(ManuallyDrop::new(audit_tx)); audit_pool[*uid as usize] = Some(ManuallyDrop::new(audit_tx));
mempool_stack.push(*uid); mempool_stack.push(*uid);

View file

@ -9,6 +9,7 @@
use napi::bindgen_prelude::Result; use napi::bindgen_prelude::Result;
use napi_derive::napi; use napi_derive::napi;
use thread_transaction::ThreadTransaction; use thread_transaction::ThreadTransaction;
use thread_acceleration::ThreadAcceleration;
use tracing::{debug, info, trace}; use tracing::{debug, info, trace};
use tracing_log::LogTracer; use tracing_log::LogTracer;
use tracing_subscriber::{EnvFilter, FmtSubscriber}; use tracing_subscriber::{EnvFilter, FmtSubscriber};
@ -19,6 +20,7 @@ use std::sync::{Arc, Mutex};
mod audit_transaction; mod audit_transaction;
mod gbt; mod gbt;
mod thread_transaction; mod thread_transaction;
mod thread_acceleration;
mod u32_hasher_types; mod u32_hasher_types;
use u32_hasher_types::{u32hashmap_with_capacity, U32HasherState}; use u32_hasher_types::{u32hashmap_with_capacity, U32HasherState};
@ -74,10 +76,11 @@ impl GbtGenerator {
/// ///
/// Rejects if the thread panics or if the Mutex is poisoned. /// Rejects if the thread panics or if the Mutex is poisoned.
#[napi] #[napi]
pub async fn make(&self, mempool: Vec<ThreadTransaction>, max_uid: u32) -> Result<GbtResult> { pub async fn make(&self, mempool: Vec<ThreadTransaction>, accelerations: Vec<ThreadAcceleration>, max_uid: u32) -> Result<GbtResult> {
trace!("make: Current State {:#?}", self.thread_transactions); trace!("make: Current State {:#?}", self.thread_transactions);
run_task( run_task(
Arc::clone(&self.thread_transactions), Arc::clone(&self.thread_transactions),
accelerations,
max_uid as usize, max_uid as usize,
move |map| { move |map| {
for tx in mempool { for tx in mempool {
@ -96,11 +99,13 @@ impl GbtGenerator {
&self, &self,
new_txs: Vec<ThreadTransaction>, new_txs: Vec<ThreadTransaction>,
remove_txs: Vec<u32>, remove_txs: Vec<u32>,
accelerations: Vec<ThreadAcceleration>,
max_uid: u32, max_uid: u32,
) -> Result<GbtResult> { ) -> Result<GbtResult> {
trace!("update: Current State {:#?}", self.thread_transactions); trace!("update: Current State {:#?}", self.thread_transactions);
run_task( run_task(
Arc::clone(&self.thread_transactions), Arc::clone(&self.thread_transactions),
accelerations,
max_uid as usize, max_uid as usize,
move |map| { move |map| {
for tx in new_txs { for tx in new_txs {
@ -141,6 +146,7 @@ pub struct GbtResult {
/// to the `HashMap` as the only argument. (A move closure is recommended to meet the bounds) /// to the `HashMap` as the only argument. (A move closure is recommended to meet the bounds)
async fn run_task<F>( async fn run_task<F>(
thread_transactions: Arc<Mutex<ThreadTransactionsMap>>, thread_transactions: Arc<Mutex<ThreadTransactionsMap>>,
accelerations: Vec<ThreadAcceleration>,
max_uid: usize, max_uid: usize,
callback: F, callback: F,
) -> Result<GbtResult> ) -> Result<GbtResult>
@ -159,7 +165,7 @@ where
callback(&mut map); callback(&mut map);
info!("Starting gbt algorithm for {} elements...", map.len()); info!("Starting gbt algorithm for {} elements...", map.len());
let result = gbt::gbt(&mut map, max_uid); let result = gbt::gbt(&mut map, &accelerations, max_uid);
info!("Finished gbt algorithm for {} elements...", map.len()); info!("Finished gbt algorithm for {} elements...", map.len());
debug!( debug!(

View file

@ -1,4 +1,4 @@
import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction } from '../../rust-gbt'; import { GbtGenerator, GbtResult, ThreadTransaction as RustThreadTransaction, ThreadAcceleration as RustThreadAcceleration } from '../../rust-gbt';
import logger from '../logger'; import logger from '../logger';
import { MempoolBlock, MempoolTransactionExtended, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag } from '../mempool.interfaces'; import { MempoolBlock, MempoolTransactionExtended, TransactionStripped, MempoolBlockWithTransactions, MempoolBlockDelta, Ancestor, CompactThreadTransaction, EffectiveFeeStats, PoolTag } from '../mempool.interfaces';
import { Common, OnlineFeeStatsCalculator } from './common'; import { Common, OnlineFeeStatsCalculator } from './common';
@ -171,7 +171,7 @@ class MempoolBlocks {
for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) { for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) {
let added: TransactionStripped[] = []; let added: TransactionStripped[] = [];
let removed: string[] = []; let removed: string[] = [];
const changed: { txid: string, rate: number | undefined, acc: number | undefined }[] = []; const changed: { txid: string, rate: number | undefined, acc: boolean | undefined }[] = [];
if (mempoolBlocks[i] && !prevBlocks[i]) { if (mempoolBlocks[i] && !prevBlocks[i]) {
added = mempoolBlocks[i].transactions; added = mempoolBlocks[i].transactions;
} else if (!mempoolBlocks[i] && prevBlocks[i]) { } else if (!mempoolBlocks[i] && prevBlocks[i]) {
@ -265,7 +265,7 @@ class MempoolBlocks {
// clean up thread error listener // clean up thread error listener
this.txSelectionWorker?.removeListener('error', threadErrorListener); this.txSelectionWorker?.removeListener('error', threadErrorListener);
const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, saveResults); const processed = this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, accelerationPool, saveResults);
logger.debug(`makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); logger.debug(`makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
@ -325,7 +325,7 @@ class MempoolBlocks {
// clean up thread error listener // clean up thread error listener
this.txSelectionWorker?.removeListener('error', threadErrorListener); this.txSelectionWorker?.removeListener('error', threadErrorListener);
this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, saveResults); this.processBlockTemplates(newMempool, blocks, null, Object.entries(rates), Object.values(clusters), accelerations, null, saveResults);
logger.debug(`updateBlockTemplates completed in ${(Date.now() - start) / 1000} seconds`); logger.debug(`updateBlockTemplates completed in ${(Date.now() - start) / 1000} seconds`);
} catch (e) { } catch (e) {
logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e)); logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e));
@ -337,7 +337,7 @@ class MempoolBlocks {
this.rustGbtGenerator = new GbtGenerator(); this.rustGbtGenerator = new GbtGenerator();
} }
private async $rustMakeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false): Promise<MempoolBlockWithTransactions[]> { private async $rustMakeBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, saveResults: boolean = false, useAccelerations: boolean = false, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
const start = Date.now(); const start = Date.now();
// reset mempool short ids // reset mempool short ids
@ -353,16 +353,25 @@ class MempoolBlocks {
tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[]; tx.inputs = tx.vin.map(v => this.getUid(newMempool[v.txid])).filter(uid => (uid !== null && uid !== undefined)) as number[];
} }
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 => {
return {
uid: this.getUid(newMempool[acc.txid]),
delta: acc.feeDelta,
};
});
// 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
const rustGbt = saveResults ? this.rustGbtGenerator : new GbtGenerator(); const rustGbt = saveResults ? this.rustGbtGenerator : new GbtGenerator();
try { try {
const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids( const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids(
await rustGbt.make(Object.values(newMempool) as RustThreadTransaction[], this.nextUid), await rustGbt.make(Object.values(newMempool) as RustThreadTransaction[], convertedAccelerations as RustThreadAcceleration[], this.nextUid),
); );
if (saveResults) { if (saveResults) {
this.rustInitialized = true; this.rustInitialized = true;
} }
const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, {}, saveResults); const processed = this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, saveResults);
logger.debug(`RUST makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); logger.debug(`RUST makeBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
return processed; return processed;
} catch (e) { } catch (e) {
@ -374,19 +383,20 @@ class MempoolBlocks {
return this.mempoolBlocks; return this.mempoolBlocks;
} }
public async $oneOffRustBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }): Promise<MempoolBlockWithTransactions[]> { public async $oneOffRustBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, useAccelerations: boolean, accelerationPool?: number): Promise<MempoolBlockWithTransactions[]> {
return this.$rustMakeBlockTemplates(newMempool, false); return this.$rustMakeBlockTemplates(newMempool, false, useAccelerations, accelerationPool);
} }
public async $rustUpdateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[]): Promise<void> { public async $rustUpdateBlockTemplates(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, added: MempoolTransactionExtended[], removed: MempoolTransactionExtended[], useAccelerations: boolean, accelerationPool?: number): Promise<void> {
// GBT optimization requires that uids never get too sparse // GBT optimization requires that uids never get too sparse
// as a sanity check, we should also explicitly prevent uint32 uid overflow // 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 * mempoolSize), MAX_UINT32)) {
this.resetRustGbt(); this.resetRustGbt();
} }
if (!this.rustInitialized) { if (!this.rustInitialized) {
// need to reset the worker // need to reset the worker
await this.$rustMakeBlockTemplates(newMempool, true); await this.$rustMakeBlockTemplates(newMempool, true, useAccelerations, accelerationPool);
return; return;
} }
@ -401,12 +411,22 @@ class MempoolBlocks {
} }
const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => (uid !== null && uid !== undefined)) as number[]; const removedUids = removed.map(tx => this.getUid(tx)).filter(uid => (uid !== null && uid !== undefined)) as number[];
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 => {
return {
uid: this.getUid(newMempool[acc.txid]),
delta: acc.feeDelta,
};
});
// 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
try { try {
const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids( const { blocks, blockWeights, rates, clusters } = this.convertNapiResultTxids(
await this.rustGbtGenerator.update( await this.rustGbtGenerator.update(
added as RustThreadTransaction[], added as RustThreadTransaction[],
removedUids, removedUids,
convertedAccelerations as RustThreadAcceleration[],
this.nextUid, this.nextUid,
), ),
); );
@ -414,7 +434,7 @@ class MempoolBlocks {
if (mempoolSize !== resultMempoolSize) { if (mempoolSize !== resultMempoolSize) {
throw new Error('GBT returned wrong number of transactions, cache is probably out of sync'); throw new Error('GBT returned wrong number of transactions, cache is probably out of sync');
} else { } else {
this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, {}, true); this.processBlockTemplates(newMempool, blocks, blockWeights, rates, clusters, accelerations, accelerationPool, true);
} }
this.removeUids(removedUids); this.removeUids(removedUids);
logger.debug(`RUST updateBlockTemplates completed in ${(Date.now() - start)/1000} seconds`); logger.debug(`RUST updateBlockTemplates completed in ${(Date.now() - start)/1000} seconds`);
@ -424,7 +444,7 @@ class MempoolBlocks {
} }
} }
private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, saveResults): MempoolBlockWithTransactions[] { private processBlockTemplates(mempool: { [txid: string]: MempoolTransactionExtended }, blocks: string[][], blockWeights: number[] | null, rates: [string, number][], clusters: string[][], accelerations, accelerationPool, saveResults): MempoolBlockWithTransactions[] {
for (const [txid, rate] of rates) { for (const [txid, rate] of rates) {
if (txid in mempool) { if (txid in mempool) {
mempool[txid].effectiveFeePerVsize = rate; mempool[txid].effectiveFeePerVsize = rate;
@ -503,7 +523,15 @@ class MempoolBlocks {
mempoolTx.cpfpChecked = true; mempoolTx.cpfpChecked = true;
} }
mempoolTx.acceleration = accelerations[txid]; const acceleration = accelerations[txid];
if (acceleration && (!accelerationPool || acceleration.pools.includes(accelerationPool))) {
mempoolTx.acceleration = true;
for (const ancestor of mempoolTx.ancestors || []) {
mempool[ancestor.txid].acceleration = true;
}
} else {
delete mempoolTx.acceleration;
}
// online calculation of stack-of-blocks fee stats // online calculation of stack-of-blocks fee stats
if (hasBlockStack && blockIndex === lastBlockIndex && feeStatsCalculator) { if (hasBlockStack && blockIndex === lastBlockIndex && feeStatsCalculator) {

View file

@ -9,7 +9,7 @@ import loadingIndicators from './loading-indicators';
import bitcoinClient from './bitcoin/bitcoin-client'; import bitcoinClient from './bitcoin/bitcoin-client';
import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; import bitcoinSecondClient from './bitcoin/bitcoin-second-client';
import rbfCache from './rbf-cache'; import rbfCache from './rbf-cache';
import accelerationApi from './services/acceleration'; import accelerationApi, { Acceleration } from './services/acceleration';
class Mempool { class Mempool {
private inSync: boolean = false; private inSync: boolean = false;

View file

@ -174,7 +174,7 @@ class WebsocketHandler {
} }
const tx = memPool.getMempool()[trackTxid]; const tx = memPool.getMempool()[trackTxid];
if (tx && tx.position) { if (tx && tx.position) {
const position: { block: number, vsize: number, accelerated?: number } = { const position: { block: number, vsize: number, accelerated?: boolean } = {
...tx.position ...tx.position
}; };
if (tx.acceleration) { if (tx.acceleration) {
@ -397,7 +397,7 @@ class WebsocketHandler {
if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) {
if (config.MEMPOOL.RUST_GBT) { if (config.MEMPOOL.RUST_GBT) {
await mempoolBlocks.$rustUpdateBlockTemplates(newMempool, mempoolSize, newTransactions, deletedTransactions); await mempoolBlocks.$rustUpdateBlockTemplates(newMempool, mempoolSize, newTransactions, deletedTransactions, true,);
} else { } else {
await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS); await mempoolBlocks.$updateBlockTemplates(newMempool, newTransactions, deletedTransactions, accelerationDelta, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
} }
@ -666,7 +666,7 @@ class WebsocketHandler {
auditMempool = deepClone(_memPool); auditMempool = deepClone(_memPool);
if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { if (config.MEMPOOL.ADVANCED_GBT_AUDIT) {
if (config.MEMPOOL.RUST_GBT) { if (config.MEMPOOL.RUST_GBT) {
projectedBlocks = await mempoolBlocks.$oneOffRustBlockTemplates(auditMempool); projectedBlocks = await mempoolBlocks.$oneOffRustBlockTemplates(auditMempool, isAccelerated, block.extras.pool.id);
} else { } else {
projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id); projectedBlocks = await mempoolBlocks.$makeBlockTemplates(auditMempool, false, isAccelerated, block.extras.pool.id);
} }
@ -739,7 +739,7 @@ class WebsocketHandler {
if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) {
if (config.MEMPOOL.RUST_GBT) { if (config.MEMPOOL.RUST_GBT) {
await mempoolBlocks.$rustUpdateBlockTemplates(_memPool, Object.keys(_memPool).length, [], transactions); await mempoolBlocks.$rustUpdateBlockTemplates(_memPool, Object.keys(_memPool).length, [], transactions, true);
} else { } else {
await mempoolBlocks.$makeBlockTemplates(_memPool, true, config.MEMPOOL_SERVICES.ACCELERATIONS); await mempoolBlocks.$makeBlockTemplates(_memPool, true, config.MEMPOOL_SERVICES.ACCELERATIONS);
} }

View file

@ -92,7 +92,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
block: number, block: number,
vsize: number, vsize: number,
}; };
acceleration?: number; acceleration?: boolean;
uid?: number; uid?: number;
} }
@ -184,7 +184,7 @@ export interface TransactionStripped {
fee: number; fee: number;
vsize: number; vsize: number;
value: number; value: number;
acc?: number; acc?: boolean;
rate?: number; // effective fee rate rate?: number; // effective fee rate
} }

View file

@ -116,6 +116,7 @@ class AuditReplication {
freshTxs: auditSummary.freshTxs || [], freshTxs: auditSummary.freshTxs || [],
sigopTxs: auditSummary.sigopTxs || [], sigopTxs: auditSummary.sigopTxs || [],
fullrbfTxs: auditSummary.fullrbfTxs || [], fullrbfTxs: auditSummary.fullrbfTxs || [],
acceleratedTxs: auditSummary.acceleratedTxs || [],
matchRate: auditSummary.matchRate, matchRate: auditSummary.matchRate,
expectedFees: auditSummary.expectedFees, expectedFees: auditSummary.expectedFees,
expectedWeight: auditSummary.expectedWeight, expectedWeight: auditSummary.expectedWeight,

View file

@ -147,7 +147,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
} }
} }
update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined, acc: number | undefined }[], direction: string = 'left', resetLayout: boolean = false): void { update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined, acc: boolean | undefined }[], direction: string = 'left', resetLayout: boolean = false): void {
if (this.scene) { if (this.scene) {
this.scene.update(add, remove, change, direction, resetLayout); this.scene.update(add, remove, change, direction, resetLayout);
this.start(); this.start();

View file

@ -150,7 +150,7 @@ export default class BlockScene {
this.updateAll(startTime, 200, direction); this.updateAll(startTime, 200, direction);
} }
update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined, acc: number | undefined }[], direction: string = 'left', resetLayout: boolean = false): void { update(add: TransactionStripped[], remove: string[], change: { txid: string, rate: number | undefined, acc: boolean | undefined }[], direction: string = 'left', resetLayout: boolean = false): void {
const startTime = performance.now(); const startTime = performance.now();
const removed = this.removeBatch(remove, startTime, direction); const removed = this.removeBatch(remove, startTime, direction);

View file

@ -38,7 +38,7 @@ export default class TxView implements TransactionStripped {
vsize: number; vsize: number;
value: number; value: number;
feerate: number; feerate: number;
acc?: number; acc?: boolean;
rate?: number; rate?: number;
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';

View file

@ -64,7 +64,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr
return; return;
} }
const samples = []; const samples = [];
const txs = this.transactions.map(tx => { return { vsize: tx.vsize, rate: tx.rate || (tx.fee / tx.vsize) }; }).sort((a, b) => { return b.rate - a.rate; }); const txs = this.transactions.filter(tx => !tx.acc).map(tx => { return { vsize: tx.vsize, rate: tx.rate || (tx.fee / tx.vsize) }; }).sort((a, b) => { return b.rate - a.rate; });
const maxBlockVSize = this.stateService.env.BLOCK_WEIGHT_UNITS / 4; const maxBlockVSize = this.stateService.env.BLOCK_WEIGHT_UNITS / 4;
const sampleInterval = maxBlockVSize / this.numSamples; const sampleInterval = maxBlockVSize / this.numSamples;
let cumVSize = 0; let cumVSize = 0;

View file

@ -70,7 +70,7 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
export interface MempoolBlockDelta { export interface MempoolBlockDelta {
added: TransactionStripped[], added: TransactionStripped[],
removed: string[], removed: string[],
changed?: { txid: string, rate: number | undefined, acc: number | undefined }[]; changed?: { txid: string, rate: number | undefined, acc: boolean | undefined }[];
} }
export interface MempoolInfo { export interface MempoolInfo {
@ -88,7 +88,7 @@ export interface TransactionStripped {
fee: number; fee: number;
vsize: number; vsize: number;
value: number; value: number;
acc?: number; // acceleration delta acc?: boolean; // is accelerated?
rate?: number; // effective fee rate rate?: number; // effective fee rate
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated'; status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'rbf' | 'accelerated';
context?: 'projected' | 'actual'; context?: 'projected' | 'actual';