From 10beb76585c7dc4df368f92ed4646fd77534bfa5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 27 Jun 2023 17:44:52 -0400 Subject: [PATCH] conform to core's gbt quirks --- backend/rust-gbt/src/audit_transaction.rs | 16 +++++++++---- backend/rust-gbt/src/gbt.rs | 29 ++++++++++++++++------- backend/src/api/mempool-blocks.ts | 2 +- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/backend/rust-gbt/src/audit_transaction.rs b/backend/rust-gbt/src/audit_transaction.rs index d4e9cf1c3..393594b2e 100644 --- a/backend/rust-gbt/src/audit_transaction.rs +++ b/backend/rust-gbt/src/audit_transaction.rs @@ -14,6 +14,7 @@ pub struct AuditTransaction { pub uid: u32, pub fee: u64, pub weight: u32, + pub vsize: u32, pub sigops: u32, pub fee_per_vsize: f64, pub effective_fee_per_vsize: f64, @@ -24,6 +25,7 @@ pub struct AuditTransaction { pub children: HashSet, ancestor_fee: u64, ancestor_weight: u32, + ancestor_vsize: u32, ancestor_sigops: u32, // Safety: Must be private to prevent NaN breaking Ord impl. score: f64, @@ -74,6 +76,7 @@ impl AuditTransaction { uid: tx.uid, fee: tx.fee, weight: tx.weight, + vsize: ((tx.weight + 3) / 4).max(tx.sigops * 5), // rounded up to the nearest integer sigops: tx.sigops, fee_per_vsize: tx.fee_per_vsize, effective_fee_per_vsize: tx.effective_fee_per_vsize, @@ -84,6 +87,7 @@ impl AuditTransaction { children: u32hashset_new(), ancestor_fee: tx.fee, ancestor_weight: tx.weight, + ancestor_vsize: ((tx.weight + 3) / 4).max(tx.sigops * 5), // rounded up to the nearest integer ancestor_sigops: tx.sigops, score: 0.0, used: false, @@ -98,8 +102,8 @@ impl AuditTransaction { } #[inline] - pub const fn ancestor_weight(&self) -> u32 { - self.ancestor_weight + pub const fn ancestor_vsize(&self) -> u32 { + self.ancestor_vsize } #[inline] @@ -128,10 +132,10 @@ impl AuditTransaction { #[inline] fn calc_new_score(&mut self) { self.score = (self.ancestor_fee as f64) - / (if self.ancestor_weight == 0 { + / (if self.ancestor_vsize == 0 { 1.0 } else { - f64::from(self.ancestor_weight) / 4.0 + f64::from(self.ancestor_vsize) }); } @@ -141,11 +145,13 @@ impl AuditTransaction { ancestors: HashSet, total_fee: u64, total_weight: u32, + total_vsize: u32, total_sigops: u32, ) { self.ancestors = ancestors; self.ancestor_fee = self.fee + total_fee; self.ancestor_weight = self.weight + total_weight; + self.ancestor_vsize = self.vsize + total_vsize; self.ancestor_sigops = self.sigops + total_sigops; self.calc_new_score(); self.relatives_set_flag = true; @@ -157,6 +163,7 @@ impl AuditTransaction { root_txid: u32, root_fee: u64, root_weight: u32, + root_vsize: u32, root_sigops: u32, cluster_rate: f64, ) -> f64 { @@ -165,6 +172,7 @@ impl AuditTransaction { if self.ancestors.remove(&root_txid) { self.ancestor_fee -= root_fee; self.ancestor_weight -= root_weight; + self.ancestor_vsize -= root_vsize; self.ancestor_sigops -= root_sigops; self.calc_new_score(); } diff --git a/backend/rust-gbt/src/gbt.rs b/backend/rust-gbt/src/gbt.rs index 797f7f981..cab684a9d 100644 --- a/backend/rust-gbt/src/gbt.rs +++ b/backend/rust-gbt/src/gbt.rs @@ -13,9 +13,10 @@ use crate::{ GbtResult, ThreadTransactionsMap, STARTING_CAPACITY, }; -const BLOCK_WEIGHT_UNITS: u32 = 4_000_000; +const MAX_BLOCK_WEIGHT_UNITS: u32 = 4_000_000 - 4_000; const BLOCK_SIGOPS: u32 = 80_000; const BLOCK_RESERVED_WEIGHT: u32 = 4_000; +const BLOCK_RESERVED_SIGOPS: u32 = 400; const MAX_BLOCKS: usize = 8; type AuditPool = HashMap; @@ -91,7 +92,7 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult { info!("(i.e. the package rooted in the transaction with the best ancestor score)"); let mut blocks: Vec> = Vec::new(); let mut block_weight: u32 = BLOCK_RESERVED_WEIGHT; - let mut block_sigops: u32 = 0; + let mut block_sigops: u32 = BLOCK_RESERVED_SIGOPS; let mut transactions: Vec = Vec::with_capacity(STARTING_CAPACITY); let mut modified: ModifiedQueue = u32priority_queue_with_capacity(STARTING_CAPACITY); let mut overflow: Vec = Vec::new(); @@ -135,7 +136,7 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult { } if blocks.len() < (MAX_BLOCKS - 1) - && ((block_weight + next_tx.ancestor_weight() >= BLOCK_WEIGHT_UNITS) + && ((block_weight + (4 * next_tx.ancestor_vsize()) >= MAX_BLOCK_WEIGHT_UNITS) || (block_sigops + next_tx.ancestor_sigops() > BLOCK_SIGOPS)) { // hold this package in an overflow list while we check for smaller options @@ -150,7 +151,13 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult { package.push((*ancestor_id, ancestor.ancestors.len())); } } - package.sort_unstable_by_key(|a| a.1); + package.sort_unstable_by(|a, b| -> Ordering { + if a.1 == b.1 { + b.0.cmp(&a.0) + } else { + a.1.cmp(&b.1) + } + }); package.push((next_tx.uid, next_tx.ancestors.len())); let cluster_rate = next_tx.cluster_rate(); @@ -176,7 +183,7 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult { // this block is full let exceeded_package_tries = - failures > 1000 && block_weight > (BLOCK_WEIGHT_UNITS - BLOCK_RESERVED_WEIGHT); + failures > 1000 && block_weight > (MAX_BLOCK_WEIGHT_UNITS - BLOCK_RESERVED_WEIGHT); let queue_is_empty = mempool_stack.is_empty() && modified.is_empty(); if (exceeded_package_tries || queue_is_empty) && blocks.len() < (MAX_BLOCKS - 1) { // finalize this block @@ -185,8 +192,8 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult { } // reset for the next block transactions = Vec::with_capacity(STARTING_CAPACITY); - block_weight = 4000; - block_sigops = 0; + block_weight = BLOCK_RESERVED_WEIGHT; + block_sigops = BLOCK_RESERVED_SIGOPS; failures = 0; // 'overflow' packages didn't fit in this block, but are valid candidates for the next overflow.reverse(); @@ -290,6 +297,7 @@ fn set_relatives(txid: u32, audit_pool: &mut AuditPool) { let mut total_fee: u64 = 0; let mut total_weight: u32 = 0; + let mut total_vsize: u32 = 0; let mut total_sigops: u32 = 0; for ancestor_id in &ancestors { @@ -298,11 +306,12 @@ fn set_relatives(txid: u32, audit_pool: &mut AuditPool) { .expect("audit_pool contains all ancestors"); total_fee += ancestor.fee; total_weight += ancestor.weight; + total_vsize += ancestor.vsize; total_sigops += ancestor.sigops; } if let Some(tx) = audit_pool.get_mut(&txid) { - tx.set_ancestors(ancestors, total_fee, total_weight, total_sigops); + tx.set_ancestors(ancestors, total_fee, total_weight, total_vsize, total_sigops); } } @@ -317,6 +326,7 @@ fn update_descendants( let mut descendant_stack: Vec = Vec::new(); let root_fee: u64; let root_weight: u32; + let root_vsize: u32; let root_sigops: u32; if let Some(root_tx) = audit_pool.get(&root_txid) { for descendant_id in &root_tx.children { @@ -327,6 +337,7 @@ fn update_descendants( } root_fee = root_tx.fee; root_weight = root_tx.weight; + root_vsize = root_tx.vsize; root_sigops = root_tx.sigops; } else { return; @@ -335,7 +346,7 @@ fn update_descendants( if let Some(descendant) = audit_pool.get_mut(&next_txid) { // remove root tx as ancestor let old_score = - descendant.remove_root(root_txid, root_fee, root_weight, root_sigops, cluster_rate); + descendant.remove_root(root_txid, root_fee, root_weight, root_vsize, root_sigops, cluster_rate); // add to priority queue or update priority if score has changed if descendant.score() < old_score { descendant.modified = true; diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index d9c61866d..7da8e0613 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -657,7 +657,7 @@ class MempoolBlocks { if (tx.uid !== null && tx.uid !== undefined) { view.setUint32(offset, tx.uid, false); view.setFloat64(offset + 4, tx.fee, false); - view.setUint32(offset + 12, (tx.adjustedVsize * 4), false); + view.setUint32(offset + 12, tx.weight, false); view.setUint32(offset + 16, tx.sigops, false); view.setFloat64(offset + 20, (tx.adjustedFeePerVsize || tx.feePerVsize), false); view.setFloat64(offset + 28, (tx.effectiveFeePerVsize || tx.adjustedFeePerVsize || tx.feePerVsize), false);