Refactor rust code for style

This commit is contained in:
Mononaut 2023-06-23 20:41:39 -04:00
parent 1d51b01bd1
commit 0e00881826
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
5 changed files with 480 additions and 425 deletions

View File

@ -1,53 +1,57 @@
use std::{collections::{HashSet}, hash::{Hash, Hasher}, cmp::Ordering}; use std::{
cmp::Ordering,
collections::HashSet,
hash::{Hash, Hasher},
};
#[derive(Clone)] #[derive(Clone)]
pub struct AuditTransaction { pub struct AuditTransaction {
pub uid: u32, pub uid: u32,
pub fee: u64, pub fee: u64,
pub weight: u32, pub weight: u32,
pub sigops: u32, pub sigops: u32,
pub fee_per_vsize: f64, pub fee_per_vsize: f64,
pub effective_fee_per_vsize: f64, pub effective_fee_per_vsize: f64,
pub dependency_rate: f64, pub dependency_rate: f64,
pub inputs: Vec<u32>, pub inputs: Vec<u32>,
pub is_relatives_set: bool, pub relatives_set_flag: bool,
pub ancestors: HashSet<u32>, pub ancestors: HashSet<u32>,
pub children: HashSet<u32>, pub children: HashSet<u32>,
pub ancestor_fee: u64, pub ancestor_fee: u64,
pub ancestor_weight: u32, pub ancestor_weight: u32,
pub ancestor_sigops: u32, pub ancestor_sigops: u32,
pub score: f64, pub score: f64,
pub used: bool, pub used: bool,
pub modified: bool, pub modified: bool,
pub dirty: bool, pub dirty: bool,
} }
impl Hash for AuditTransaction { impl Hash for AuditTransaction {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.uid.hash(state); self.uid.hash(state);
} }
} }
impl PartialEq for AuditTransaction { impl PartialEq for AuditTransaction {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.uid == other.uid self.uid == other.uid
} }
} }
impl Eq for AuditTransaction {} impl Eq for AuditTransaction {}
impl PartialOrd for AuditTransaction { impl PartialOrd for AuditTransaction {
fn partial_cmp(&self, other: &AuditTransaction) -> Option<Ordering> { fn partial_cmp(&self, other: &AuditTransaction) -> Option<Ordering> {
if self.score == other.score { if self.score == other.score {
return Some(self.uid.cmp(&other.uid)); return Some(self.uid.cmp(&other.uid));
} else { } else {
return self.score.partial_cmp(&other.score); return self.score.partial_cmp(&other.score);
}
} }
}
} }
impl Ord for AuditTransaction { impl Ord for AuditTransaction {
fn cmp(&self, other: &AuditTransaction) -> Ordering { fn cmp(&self, other: &AuditTransaction) -> Ordering {
self.partial_cmp(other).unwrap() self.partial_cmp(other).unwrap()
} }
} }

View File

@ -1,41 +1,45 @@
use priority_queue::PriorityQueue;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use std::f64::INFINITY; use std::f64::INFINITY;
use priority_queue::PriorityQueue;
use crate::thread_transaction::{ThreadTransaction}; use crate::audit_transaction::AuditTransaction;
use crate::audit_transaction::{AuditTransaction}; use crate::thread_transaction::ThreadTransaction;
const BLOCK_WEIGHT_UNITS: u32 = 4_000_000; const BLOCK_WEIGHT_UNITS: u32 = 4_000_000;
const BLOCK_SIGOPS: u32 = 80_000; const BLOCK_SIGOPS: u32 = 80_000;
const BLOCK_RESERVED_WEIGHT: u32 = 4_000;
const MAX_BLOCKS: usize = 8;
struct TxPriority { struct TxPriority {
uid: u32, uid: u32,
score: f64, score: f64,
} }
impl PartialEq for TxPriority { impl PartialEq for TxPriority {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.uid == other.uid self.uid == other.uid
} }
} }
impl Eq for TxPriority {} impl Eq for TxPriority {}
impl PartialOrd for TxPriority { impl PartialOrd for TxPriority {
fn partial_cmp(&self, other: &TxPriority) -> Option<Ordering> { fn partial_cmp(&self, other: &TxPriority) -> Option<Ordering> {
if self.score == other.score { if self.score == other.score {
return Some(self.uid.cmp(&other.uid)); return Some(self.uid.cmp(&other.uid));
} else { } else {
return other.score.partial_cmp(&self.score); return other.score.partial_cmp(&self.score);
}
} }
}
} }
impl Ord for TxPriority { impl Ord for TxPriority {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap() self.partial_cmp(other).unwrap()
} }
} }
pub fn gbt(mempool: &mut HashMap<u32,ThreadTransaction>) -> (Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>) { pub fn gbt(
return make_block_templates(mempool); mempool: &mut HashMap<u32, ThreadTransaction>,
) -> Option<(Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>)> {
make_block_templates(mempool)
} }
/* /*
@ -43,264 +47,310 @@ pub fn gbt(mempool: &mut HashMap<u32,ThreadTransaction>) -> (Vec<Vec<u32>>, Vec<
* (see BlockAssembler in https://github.com/bitcoin/bitcoin/blob/master/src/node/miner.cpp) * (see BlockAssembler in https://github.com/bitcoin/bitcoin/blob/master/src/node/miner.cpp)
* Ported from https://github.com/mempool/mempool/blob/master/backend/src/api/tx-selection-worker.ts * Ported from https://github.com/mempool/mempool/blob/master/backend/src/api/tx-selection-worker.ts
*/ */
fn make_block_templates(mempool: &mut HashMap<u32,ThreadTransaction>) -> (Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>) { fn make_block_templates(
let mut audit_pool: HashMap<u32, AuditTransaction> = HashMap::new(); mempool: &mut HashMap<u32, ThreadTransaction>,
let mut mempool_array: VecDeque<u32> = VecDeque::new(); ) -> Option<(Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>)> {
let mut cluster_array: Vec<Vec<u32>> = Vec::new(); let mut audit_pool: HashMap<u32, AuditTransaction> = HashMap::new();
let mut mempool_array: VecDeque<u32> = VecDeque::new();
let mut cluster_array: Vec<Vec<u32>> = Vec::new();
// Initialize working structs // Initialize working structs
for (uid, tx) in mempool { for (uid, tx) in mempool {
let audit_tx = AuditTransaction { let audit_tx = AuditTransaction {
uid: tx.uid, uid: tx.uid,
fee: tx.fee, fee: tx.fee,
weight: tx.weight, weight: tx.weight,
sigops: tx.sigops, sigops: tx.sigops,
fee_per_vsize: tx.fee_per_vsize, fee_per_vsize: tx.fee_per_vsize,
effective_fee_per_vsize: tx.effective_fee_per_vsize, effective_fee_per_vsize: tx.effective_fee_per_vsize,
dependency_rate: INFINITY, dependency_rate: INFINITY,
inputs: tx.inputs.clone(), inputs: tx.inputs.clone(),
is_relatives_set: false, relatives_set_flag: false,
ancestors: HashSet::new(), ancestors: HashSet::new(),
children: HashSet::new(), children: HashSet::new(),
ancestor_fee: tx.fee, ancestor_fee: tx.fee,
ancestor_weight: tx.weight, ancestor_weight: tx.weight,
ancestor_sigops: tx.sigops, ancestor_sigops: tx.sigops,
score: 0.0, score: 0.0,
used: false, used: false,
modified: false, modified: false,
dirty: false, dirty: false,
}; };
audit_pool.insert(audit_tx.uid, audit_tx); audit_pool.insert(audit_tx.uid, audit_tx);
mempool_array.push_back(*uid); mempool_array.push_back(*uid);
}
// Build relatives graph & calculate ancestor scores
for txid in &mempool_array {
set_relatives(*txid, &mut audit_pool);
}
// Sort by descending ancestor score
mempool_array.make_contiguous().sort_unstable_by(|a, b| {
let a_tx = audit_pool.get(a).unwrap();
let b_tx = audit_pool.get(b).unwrap();
b_tx.cmp(a_tx)
});
// Build blocks by greedily choosing the highest feerate package
// (i.e. the package rooted in the transaction with the best ancestor score)
let mut blocks: Vec<Vec<u32>> = Vec::new();
let mut block_weight: u32 = 4000;
let mut block_sigops: u32 = 0;
let mut transactions: Vec<u32> = Vec::new();
let mut modified: PriorityQueue<u32, TxPriority> = PriorityQueue::new();
let mut overflow: Vec<u32> = Vec::new();
let mut failures = 0;
while mempool_array.len() > 0 || !modified.is_empty() {
let next_txid: u32;
if modified.is_empty() {
next_txid = mempool_array.pop_front().unwrap();
} else if mempool_array.len() == 0 {
next_txid = modified.pop().unwrap().0;
} else {
let next_array_txid = mempool_array.front().unwrap();
let next_modified_txid = modified.peek().unwrap().0;
let array_tx: &AuditTransaction = audit_pool.get(next_array_txid).unwrap();
let modified_tx: &AuditTransaction = audit_pool.get(next_modified_txid).unwrap();
match array_tx.cmp(&modified_tx) {
std::cmp::Ordering::Equal | std::cmp::Ordering::Greater => {
next_txid = mempool_array.pop_front().unwrap();
}
std::cmp::Ordering::Less => {
next_txid = modified.pop().unwrap().0;
}
}
}
let next_tx: AuditTransaction = audit_pool.get(&next_txid).unwrap().clone();
if next_tx.used {
continue;
} }
if blocks.len() < 7 && ((block_weight + next_tx.ancestor_weight >= BLOCK_WEIGHT_UNITS) || (block_sigops + next_tx.ancestor_sigops > BLOCK_SIGOPS)) { // Build relatives graph & calculate ancestor scores
// hold this package in an overflow list while we check for smaller options for txid in &mempool_array {
overflow.push(next_txid); set_relatives(*txid, &mut audit_pool);
failures += 1;
} else {
let mut package: Vec<(u32, usize, u32)> = Vec::new();
let mut cluster: Vec<u32> = Vec::new();
let is_cluster: bool = next_tx.ancestors.len() > 0;
package.push((next_tx.uid, next_tx.ancestors.len(), next_tx.weight));
cluster.push(next_tx.uid);
for ancestor_id in &next_tx.ancestors {
if let Some(ancestor) = audit_pool.get(ancestor_id) {
package.push((*ancestor_id, ancestor.ancestors.len(), ancestor.weight));
cluster.push(*ancestor_id);
}
}
package.sort_unstable_by_key(|a| 0 - a.1);
if is_cluster {
cluster_array.push(cluster);
}
let cluster_rate = next_tx.dependency_rate.min(next_tx.ancestor_fee as f64 / (next_tx.ancestor_weight as f64 / 4.0));
for package_entry in &package {
if let Some(tx) = audit_pool.get_mut(&package_entry.0) {
tx.used = true;
if tx.effective_fee_per_vsize != cluster_rate {
tx.effective_fee_per_vsize = cluster_rate;
tx.dirty = true;
}
transactions.push(tx.uid);
block_weight += tx.weight;
block_sigops += tx.sigops;
}
update_descendants(package_entry.0, &mut audit_pool, &mut modified, cluster_rate);
}
failures = 0;
} }
// this block is full // Sort by descending ancestor score
let exceeded_package_tries = failures > 1000 && block_weight > (BLOCK_WEIGHT_UNITS - 4000); mempool_array.make_contiguous().sort_unstable_by(|a, b| {
let queue_is_empty = mempool_array.len() == 0 && modified.is_empty(); let a_tx = audit_pool.get(a).unwrap();
if (exceeded_package_tries || queue_is_empty) && blocks.len() < 7 { let b_tx = audit_pool.get(b).unwrap();
// finalize this block b_tx.cmp(a_tx)
if transactions.len() > 0 { });
// Build blocks by greedily choosing the highest feerate package
// (i.e. the package rooted in the transaction with the best ancestor score)
let mut blocks: Vec<Vec<u32>> = Vec::new();
let mut block_weight: u32 = BLOCK_RESERVED_WEIGHT;
let mut block_sigops: u32 = 0;
let mut transactions: Vec<u32> = Vec::new();
let mut modified: PriorityQueue<u32, TxPriority> = PriorityQueue::new();
let mut overflow: Vec<u32> = Vec::new();
let mut failures = 0;
while mempool_array.len() > 0 || !modified.is_empty() {
let next_txid: u32;
if modified.is_empty() {
next_txid = mempool_array.pop_front()?;
} else if mempool_array.len() == 0 {
next_txid = modified.pop()?.0;
} else {
let next_array_txid = mempool_array.front()?;
let next_modified_txid = modified.peek()?.0;
let array_tx: &AuditTransaction = audit_pool.get(next_array_txid)?;
let modified_tx: &AuditTransaction = audit_pool.get(next_modified_txid)?;
match array_tx.cmp(&modified_tx) {
std::cmp::Ordering::Equal | std::cmp::Ordering::Greater => {
next_txid = mempool_array.pop_front()?;
}
std::cmp::Ordering::Less => {
next_txid = modified.pop()?.0;
}
}
}
let next_tx = audit_pool.get(&next_txid)?;
if next_tx.used {
continue;
}
if blocks.len() < (MAX_BLOCKS - 1)
&& ((block_weight + next_tx.ancestor_weight >= BLOCK_WEIGHT_UNITS)
|| (block_sigops + next_tx.ancestor_sigops > BLOCK_SIGOPS))
{
// hold this package in an overflow list while we check for smaller options
overflow.push(next_txid);
failures += 1;
} else {
let mut package: Vec<(u32, usize, u32)> = Vec::new();
let mut cluster: Vec<u32> = Vec::new();
let is_cluster: bool = next_tx.ancestors.len() > 0;
package.push((next_txid, next_tx.ancestors.len(), next_tx.weight));
cluster.push(next_txid);
for ancestor_id in &next_tx.ancestors {
if let Some(ancestor) = audit_pool.get(ancestor_id) {
package.push((*ancestor_id, ancestor.ancestors.len(), ancestor.weight));
cluster.push(*ancestor_id);
}
}
package.sort_unstable_by_key(|a| 0 - a.1);
if is_cluster {
cluster_array.push(cluster);
}
let cluster_rate = next_tx
.dependency_rate
.min(next_tx.ancestor_fee as f64 / (next_tx.ancestor_weight as f64 / 4.0));
for package_entry in &package {
if let Some(tx) = audit_pool.get_mut(&package_entry.0) {
tx.used = true;
if tx.effective_fee_per_vsize != cluster_rate {
tx.effective_fee_per_vsize = cluster_rate;
tx.dirty = true;
}
transactions.push(tx.uid);
block_weight += tx.weight;
block_sigops += tx.sigops;
}
update_descendants(
package_entry.0,
&mut audit_pool,
&mut modified,
cluster_rate,
);
}
failures = 0;
}
// this block is full
let exceeded_package_tries =
failures > 1000 && block_weight > (BLOCK_WEIGHT_UNITS - BLOCK_RESERVED_WEIGHT);
let queue_is_empty = mempool_array.len() == 0 && modified.is_empty();
if (exceeded_package_tries || queue_is_empty) && blocks.len() < (MAX_BLOCKS - 1) {
// finalize this block
if transactions.len() > 0 {
blocks.push(transactions);
}
// reset for the next block
transactions = Vec::new();
block_weight = 4000;
// 'overflow' packages didn't fit in this block, but are valid candidates for the next
overflow.reverse();
for overflowed in &overflow {
if let Some(overflowed_tx) = audit_pool.get(overflowed) {
if overflowed_tx.modified {
modified.push(
*overflowed,
TxPriority {
uid: *overflowed,
score: overflowed_tx.score,
},
);
} else {
mempool_array.push_front(*overflowed);
}
}
}
overflow = Vec::new();
}
}
// add the final unbounded block if it contains any transactions
if transactions.len() > 0 {
blocks.push(transactions); blocks.push(transactions);
} }
// reset for the next block
transactions = Vec::new(); // make a list of dirty transactions and their new rates
block_weight = 4000; let mut rates: Vec<(u32, f64)> = Vec::new();
// 'overflow' packages didn't fit in this block, but are valid candidates for the next for (txid, tx) in audit_pool {
overflow.reverse(); if tx.dirty {
for overflowed in &overflow { rates.push((txid, tx.effective_fee_per_vsize));
if let Some(overflowed_tx) = audit_pool.get(overflowed) {
if overflowed_tx.modified {
modified.push(*overflowed, TxPriority{ uid: *overflowed, score: overflowed_tx.score});
} else {
mempool_array.push_front(*overflowed);
}
} }
}
overflow = Vec::new();
} }
}
// add the final unbounded block if it contains any transactions
if transactions.len() > 0 {
blocks.push(transactions);
}
// make a list of dirty transactions and their new rates Some((blocks, rates, cluster_array))
let mut rates: Vec<(u32, f64)> = Vec::new();
for (txid, tx) in audit_pool {
if tx.dirty {
rates.push((txid, tx.effective_fee_per_vsize));
}
}
return (blocks, rates, cluster_array);
} }
fn set_relatives(txid: u32, audit_pool: &mut HashMap<u32, AuditTransaction>) { fn set_relatives(txid: u32, audit_pool: &mut HashMap<u32, AuditTransaction>) {
let mut parents: HashSet<u32> = HashSet::new(); let mut parents: HashSet<u32> = HashSet::new();
if let Some(tx) = audit_pool.get(&txid) { if let Some(tx) = audit_pool.get(&txid) {
if tx.is_relatives_set { if tx.relatives_set_flag {
return; return;
}
for input in &tx.inputs {
parents.insert(*input);
}
} else {
return;
}
let mut ancestors: HashSet<u32> = HashSet::new();
for parent_id in &parents {
set_relatives(*parent_id, audit_pool);
let parent_entry: Option<&mut AuditTransaction> = audit_pool.get_mut(&parent_id);
match parent_entry {
Some(parent) => {
ancestors.insert(*parent_id);
parent.children.insert(txid);
for ancestor in &parent.ancestors {
ancestors.insert(*ancestor);
} }
} for input in &tx.inputs {
parents.insert(*input);
None => {} }
} else {
return;
} }
}
let mut total_fee: u64 = 0; let mut ancestors: HashSet<u32> = HashSet::new();
let mut total_weight: u32 = 0; for parent_id in &parents {
let mut total_sigops: u32 = 0; set_relatives(*parent_id, audit_pool);
for ancestor_id in &ancestors { match audit_pool.get_mut(&parent_id) {
let ancestor = audit_pool.get(&ancestor_id).unwrap(); Some(parent) => {
total_fee += ancestor.fee; ancestors.insert(*parent_id);
total_weight += ancestor.weight; parent.children.insert(txid);
total_sigops += ancestor.sigops; for ancestor in &parent.ancestors {
} ancestors.insert(*ancestor);
}
}
if let Some(tx) = audit_pool.get_mut(&txid) { None => {}
tx.ancestors = ancestors; }
tx.ancestor_fee = tx.fee + total_fee; }
tx.ancestor_weight = tx.weight + total_weight;
tx.ancestor_sigops = tx.sigops + total_sigops; let mut total_fee: u64 = 0;
tx.score = (tx.ancestor_fee as f64) / (if tx.ancestor_weight != 0 {tx.ancestor_weight as f64 / 4.0} else { 1.0 }); let mut total_weight: u32 = 0;
tx.is_relatives_set = true; let mut total_sigops: u32 = 0;
}
for ancestor_id in &ancestors {
let ancestor = audit_pool.get(&ancestor_id).unwrap();
total_fee += ancestor.fee;
total_weight += ancestor.weight;
total_sigops += ancestor.sigops;
}
if let Some(tx) = audit_pool.get_mut(&txid) {
tx.ancestors = ancestors;
tx.ancestor_fee = tx.fee + total_fee;
tx.ancestor_weight = tx.weight + total_weight;
tx.ancestor_sigops = tx.sigops + total_sigops;
tx.score = (tx.ancestor_fee as f64)
/ (if tx.ancestor_weight != 0 {
tx.ancestor_weight as f64 / 4.0
} else {
1.0
});
tx.relatives_set_flag = true;
}
} }
// iterate over remaining descendants, removing the root as a valid ancestor & updating the ancestor score // iterate over remaining descendants, removing the root as a valid ancestor & updating the ancestor score
fn update_descendants(root_txid: u32, audit_pool: &mut HashMap<u32, AuditTransaction>, modified: &mut PriorityQueue<u32, TxPriority>, cluster_rate: f64) { fn update_descendants(
let mut visited: HashSet<u32> = HashSet::new(); root_txid: u32,
let mut descendant_stack: Vec<u32> = Vec::new(); audit_pool: &mut HashMap<u32, AuditTransaction>,
let root_fee: u64; modified: &mut PriorityQueue<u32, TxPriority>,
let root_weight: u32; cluster_rate: f64,
let root_sigops: u32; ) {
if let Some(root_tx) = audit_pool.get(&root_txid) { let mut visited: HashSet<u32> = HashSet::new();
for descendant_id in &root_tx.children { let mut descendant_stack: Vec<u32> = Vec::new();
if !visited.contains(descendant_id) { let root_fee: u64;
descendant_stack.push(*descendant_id); let root_weight: u32;
visited.insert(*descendant_id); let root_sigops: u32;
} if let Some(root_tx) = audit_pool.get(&root_txid) {
} for descendant_id in &root_tx.children {
root_fee = root_tx.fee; if !visited.contains(descendant_id) {
root_weight = root_tx.weight; descendant_stack.push(*descendant_id);
root_sigops = root_tx.sigops; visited.insert(*descendant_id);
} else { }
return;
}
while descendant_stack.len() > 0 {
let next_txid: u32 = descendant_stack.pop().unwrap();
if let Some(descendant) = audit_pool.get_mut(&next_txid) {
// remove root tx as ancestor
descendant.ancestors.remove(&root_txid);
descendant.ancestor_fee -= root_fee;
descendant.ancestor_weight -= root_weight;
descendant.ancestor_sigops -= root_sigops;
let current_score = descendant.score;
descendant.score = (descendant.ancestor_fee as f64) / (if descendant.ancestor_weight != 0 {descendant.ancestor_weight as f64 / 4.0} else { 1.0 });
descendant.dependency_rate = descendant.dependency_rate.min(cluster_rate);
descendant.modified = true;
// update modified priority if score has changed
if !descendant.modified || descendant.score < current_score {
modified.push_decrease(descendant.uid, TxPriority { uid: descendant.uid, score: descendant.score});
} else if descendant.score > current_score {
modified.push_increase(descendant.uid, TxPriority { uid: descendant.uid, score: descendant.score});
}
// add this node's children to the stack
for child_id in &descendant.children {
if !visited.contains(child_id) {
descendant_stack.push(*child_id);
visited.insert(*child_id);
} }
} root_fee = root_tx.fee;
root_weight = root_tx.weight;
root_sigops = root_tx.sigops;
} else {
return;
} }
} while descendant_stack.len() > 0 {
} let next_txid: u32 = descendant_stack.pop().unwrap();
if let Some(descendant) = audit_pool.get_mut(&next_txid) {
// remove root tx as ancestor
descendant.ancestors.remove(&root_txid);
descendant.ancestor_fee -= root_fee;
descendant.ancestor_weight -= root_weight;
descendant.ancestor_sigops -= root_sigops;
let current_score = descendant.score;
descendant.score = (descendant.ancestor_fee as f64)
/ (if descendant.ancestor_weight != 0 {
descendant.ancestor_weight as f64 / 4.0
} else {
1.0
});
descendant.dependency_rate = descendant.dependency_rate.min(cluster_rate);
descendant.modified = true;
// update modified priority if score has changed
if !descendant.modified || descendant.score < current_score {
modified.push_decrease(
descendant.uid,
TxPriority {
uid: descendant.uid,
score: descendant.score,
},
);
} else if descendant.score > current_score {
modified.push_increase(
descendant.uid,
TxPriority {
uid: descendant.uid,
score: descendant.score,
},
);
}
// add this node's children to the stack
for child_id in &descendant.children {
if !visited.contains(child_id) {
descendant_stack.push(*child_id);
visited.insert(*child_id);
}
}
}
}
}

View File

@ -1,123 +1,127 @@
use neon::{prelude::*, types::buffer::TypedArray}; use neon::{prelude::*, types::buffer::TypedArray};
use once_cell::sync::Lazy;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::sync::Mutex; use std::sync::Mutex;
use once_cell::sync::Lazy;
mod audit_transaction;
mod gbt; mod gbt;
mod thread_transaction; mod thread_transaction;
mod audit_transaction;
mod utils; mod utils;
use thread_transaction::ThreadTransaction; use thread_transaction::ThreadTransaction;
static THREAD_TRANSACTIONS: Lazy<Mutex<HashMap<u32, ThreadTransaction>>> = Lazy::new(|| { static THREAD_TRANSACTIONS: Lazy<Mutex<HashMap<u32, ThreadTransaction>>> =
Mutex::new(HashMap::new()) Lazy::new(|| Mutex::new(HashMap::new()));
});
fn make(mut cx: FunctionContext) -> JsResult<JsUndefined> { fn make(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let mempool_arg = cx.argument::<JsArrayBuffer>(0)?.root(&mut cx).into_inner(&mut cx); let mempool_arg = cx
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx); .argument::<JsArrayBuffer>(0)?
let channel = cx.channel(); .root(&mut cx)
.into_inner(&mut cx);
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
let channel = cx.channel();
let buffer = mempool_arg.as_slice(&mut cx); let buffer = mempool_arg.as_slice(&mut cx);
let thread_transactions = ThreadTransaction::batch_from_buffer(buffer);
let mut map = THREAD_TRANSACTIONS.lock().unwrap(); let mut map = HashMap::new();
map.clear(); for tx in ThreadTransaction::batch_from_buffer(buffer) {
for tx in thread_transactions { map.insert(tx.uid, tx);
map.insert(tx.uid, tx); }
}
drop(map);
run_in_thread(channel, callback); let mut global_map = THREAD_TRANSACTIONS.lock().unwrap();
*global_map = map;
Ok(cx.undefined()) run_in_thread(channel, callback);
Ok(cx.undefined())
} }
fn update(mut cx: FunctionContext) -> JsResult<JsUndefined> { fn update(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let new_txs_arg = cx.argument::<JsArrayBuffer>(0)?.root(&mut cx).into_inner(&mut cx); let new_txs_arg = cx
let remove_txs_arg = cx.argument::<JsArrayBuffer>(1)?.root(&mut cx).into_inner(&mut cx); .argument::<JsArrayBuffer>(0)?
let callback = cx.argument::<JsFunction>(2)?.root(&mut cx); .root(&mut cx)
let channel = cx.channel(); .into_inner(&mut cx);
let remove_txs_arg = cx
.argument::<JsArrayBuffer>(1)?
.root(&mut cx)
.into_inner(&mut cx);
let callback = cx.argument::<JsFunction>(2)?.root(&mut cx);
let channel = cx.channel();
let mut map = THREAD_TRANSACTIONS.lock().unwrap(); let mut map = THREAD_TRANSACTIONS.lock().unwrap();
let new_tx_buffer = new_txs_arg.as_slice(&mut cx); let new_tx_buffer = new_txs_arg.as_slice(&mut cx);
let thread_transactions = ThreadTransaction::batch_from_buffer(new_tx_buffer); for tx in ThreadTransaction::batch_from_buffer(new_tx_buffer) {
for tx in thread_transactions { map.insert(tx.uid, tx);
map.insert(tx.uid, tx); }
}
let remove_tx_buffer = remove_txs_arg.as_slice(&mut cx); let remove_tx_buffer = remove_txs_arg.as_slice(&mut cx);
let remove_ids = utils::txids_from_buffer(remove_tx_buffer); for txid in &utils::txids_from_buffer(remove_tx_buffer) {
for txid in &remove_ids { map.remove(txid);
map.remove(txid); }
} drop(map);
drop(map);
run_in_thread(channel, callback); run_in_thread(channel, callback);
Ok(cx.undefined()) Ok(cx.undefined())
} }
fn run_in_thread(channel: Channel, callback: Root<JsFunction>) { fn run_in_thread(channel: Channel, callback: Root<JsFunction>) {
std::thread::spawn(move || { std::thread::spawn(move || {
let mut map = THREAD_TRANSACTIONS.lock().unwrap(); let mut map = THREAD_TRANSACTIONS.lock().unwrap();
let (blocks, rates, clusters) = gbt::gbt(map.deref_mut()); let (blocks, rates, clusters) = gbt::gbt(map.deref_mut()).unwrap();
drop(map); drop(map);
channel.send(move |mut cx| { channel.send(move |mut cx| {
let result = JsObject::new(&mut cx); let result = JsObject::new(&mut cx);
let js_blocks = JsArray::new(&mut cx, blocks.len() as u32); let js_blocks = JsArray::new(&mut cx, blocks.len() as u32);
for (i, block) in blocks.iter().enumerate() { for (i, block) in blocks.iter().enumerate() {
let inner = JsArray::new(&mut cx, block.len() as u32); let inner = JsArray::new(&mut cx, block.len() as u32);
for (j, uid) in block.iter().enumerate() { for (j, uid) in block.iter().enumerate() {
let v = cx.number(*uid); let v = cx.number(*uid);
inner.set(&mut cx, j as u32, v)?; inner.set(&mut cx, j as u32, v)?;
} }
js_blocks.set(&mut cx, i as u32, inner)?; js_blocks.set(&mut cx, i as u32, inner)?;
} }
let js_clusters = JsArray::new(&mut cx, clusters.len() as u32); let js_clusters = JsArray::new(&mut cx, clusters.len() as u32);
for (i, cluster) in clusters.iter().enumerate() { for (i, cluster) in clusters.iter().enumerate() {
let inner = JsArray::new(&mut cx, cluster.len() as u32); let inner = JsArray::new(&mut cx, cluster.len() as u32);
for (j, uid) in cluster.iter().enumerate() { for (j, uid) in cluster.iter().enumerate() {
let v = cx.number(*uid); let v = cx.number(*uid);
inner.set(&mut cx, j as u32, v)?; inner.set(&mut cx, j as u32, v)?;
} }
js_clusters.set(&mut cx, i as u32, inner)?; js_clusters.set(&mut cx, i as u32, inner)?;
} }
let js_rates = JsArray::new(&mut cx, rates.len() as u32); let js_rates = JsArray::new(&mut cx, rates.len() as u32);
for (i, (uid, rate)) in rates.iter().enumerate() { for (i, (uid, rate)) in rates.iter().enumerate() {
let inner = JsArray::new(&mut cx, 2); let inner = JsArray::new(&mut cx, 2);
let js_uid = cx.number(*uid); let js_uid = cx.number(*uid);
let js_rate = cx.number(*rate); let js_rate = cx.number(*rate);
inner.set(&mut cx, 0, js_uid)?; inner.set(&mut cx, 0, js_uid)?;
inner.set(&mut cx, 1, js_rate)?; inner.set(&mut cx, 1, js_rate)?;
js_rates.set(&mut cx, i as u32, inner)?; js_rates.set(&mut cx, i as u32, inner)?;
} }
result.set(&mut cx, "blocks", js_blocks)?; result.set(&mut cx, "blocks", js_blocks)?;
result.set(&mut cx, "clusters", js_clusters)?; result.set(&mut cx, "clusters", js_clusters)?;
result.set(&mut cx, "rates", js_rates)?; result.set(&mut cx, "rates", js_rates)?;
let callback = callback.into_inner(&mut cx); let callback = callback.into_inner(&mut cx);
let this = cx.undefined(); let this = cx.undefined();
let args = vec![ let args = vec![result.upcast()];
result.upcast()
];
callback.call(&mut cx, this, args)?; callback.call(&mut cx, this, args)?;
Ok(()) Ok(())
});
}); });
});
} }
#[neon::main] #[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> { fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("make", make)?; cx.export_function("make", make)?;
cx.export_function("update", update)?; cx.export_function("update", update)?;
Ok(()) Ok(())
} }

View File

@ -1,46 +1,44 @@
// use neon::{types::{JsObject, JsNumber, JsArray, JsValue, JsBoolean, JsArrayBuffer, buffer::TypedArray}, prelude::{Object, FunctionContext, Handle}};
extern crate bytes;
use std::io::Cursor;
use bytes::buf::Buf; use bytes::buf::Buf;
use std::io::Cursor;
pub struct ThreadTransaction { pub struct ThreadTransaction {
pub uid: u32, pub uid: u32,
pub fee: u64, pub fee: u64,
pub weight: u32, pub weight: u32,
pub sigops: u32, pub sigops: u32,
pub fee_per_vsize: f64, pub fee_per_vsize: f64,
pub effective_fee_per_vsize: f64, pub effective_fee_per_vsize: f64,
pub inputs: Vec<u32>, pub inputs: Vec<u32>,
} }
impl ThreadTransaction { impl ThreadTransaction {
pub fn batch_from_buffer(buffer: &[u8]) -> Vec<ThreadTransaction> { pub fn batch_from_buffer(buffer: &[u8]) -> Vec<ThreadTransaction> {
let mut transactions: Vec<ThreadTransaction> = Vec::new(); let mut transactions: Vec<ThreadTransaction> = Vec::new();
let mut cursor = Cursor::new(buffer); let mut cursor = Cursor::new(buffer);
let size = cursor.get_u32(); let size = cursor.get_u32();
for _ in 0..size { for _ in 0..size {
let uid = cursor.get_u32(); let uid = cursor.get_u32();
let fee = cursor.get_f64() as u64; let fee = cursor.get_f64().round() as u64;
let weight = cursor.get_u32(); let weight = cursor.get_u32();
let sigops = cursor.get_u32(); let sigops = cursor.get_u32();
let fee_per_vsize = cursor.get_f64(); let fee_per_vsize = cursor.get_f64();
let effective_fee_per_vsize = cursor.get_f64(); let effective_fee_per_vsize = cursor.get_f64();
let input_count = cursor.get_u32(); let input_count = cursor.get_u32();
let mut inputs: Vec<u32> = Vec::new(); let mut inputs: Vec<u32> = Vec::new();
for _ in 0..input_count { for _ in 0..input_count {
inputs.push(cursor.get_u32()); inputs.push(cursor.get_u32());
} }
transactions.push(ThreadTransaction { transactions.push(ThreadTransaction {
uid, uid,
fee, fee,
weight, weight,
sigops, sigops,
fee_per_vsize, fee_per_vsize,
effective_fee_per_vsize, effective_fee_per_vsize,
inputs, inputs,
}) })
} }
return transactions; transactions
} }
} }

View File

@ -1,14 +1,13 @@
extern crate bytes;
use std::io::Cursor;
use bytes::buf::Buf; use bytes::buf::Buf;
use std::io::Cursor;
pub fn txids_from_buffer(buffer: &[u8]) -> Vec<u32> { pub fn txids_from_buffer(buffer: &[u8]) -> Vec<u32> {
let mut txids: Vec<u32> = Vec::new(); let mut txids: Vec<u32> = Vec::new();
let mut cursor = Cursor::new(buffer); let mut cursor: Cursor<&[u8]> = Cursor::new(buffer);
let size = cursor.get_u32(); let size: u32 = cursor.get_u32();
for _ in 0..size { for _ in 0..size {
txids.push(cursor.get_u32()); txids.push(cursor.get_u32());
} }
return txids; txids
} }