Refactor rust code for style
This commit is contained in:
parent
1d51b01bd1
commit
0e00881826
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
}
|
||||||
}
|
root_fee = root_tx.fee;
|
||||||
while descendant_stack.len() > 0 {
|
root_weight = root_tx.weight;
|
||||||
let next_txid: u32 = descendant_stack.pop().unwrap();
|
root_sigops = root_tx.sigops;
|
||||||
if let Some(descendant) = audit_pool.get_mut(&next_txid) {
|
} else {
|
||||||
// remove root tx as ancestor
|
return;
|
||||||
descendant.ancestors.remove(&root_txid);
|
}
|
||||||
descendant.ancestor_fee -= root_fee;
|
while descendant_stack.len() > 0 {
|
||||||
descendant.ancestor_weight -= root_weight;
|
let next_txid: u32 = descendant_stack.pop().unwrap();
|
||||||
descendant.ancestor_sigops -= root_sigops;
|
if let Some(descendant) = audit_pool.get_mut(&next_txid) {
|
||||||
let current_score = descendant.score;
|
// remove root tx as ancestor
|
||||||
descendant.score = (descendant.ancestor_fee as f64) / (if descendant.ancestor_weight != 0 {descendant.ancestor_weight as f64 / 4.0} else { 1.0 });
|
descendant.ancestors.remove(&root_txid);
|
||||||
descendant.dependency_rate = descendant.dependency_rate.min(cluster_rate);
|
descendant.ancestor_fee -= root_fee;
|
||||||
descendant.modified = true;
|
descendant.ancestor_weight -= root_weight;
|
||||||
// update modified priority if score has changed
|
descendant.ancestor_sigops -= root_sigops;
|
||||||
if !descendant.modified || descendant.score < current_score {
|
let current_score = descendant.score;
|
||||||
modified.push_decrease(descendant.uid, TxPriority { uid: descendant.uid, score: descendant.score});
|
descendant.score = (descendant.ancestor_fee as f64)
|
||||||
} else if descendant.score > current_score {
|
/ (if descendant.ancestor_weight != 0 {
|
||||||
modified.push_increase(descendant.uid, TxPriority { uid: descendant.uid, score: descendant.score});
|
descendant.ancestor_weight as f64 / 4.0
|
||||||
}
|
} else {
|
||||||
|
1.0
|
||||||
// add this node's children to the stack
|
});
|
||||||
for child_id in &descendant.children {
|
descendant.dependency_rate = descendant.dependency_rate.min(cluster_rate);
|
||||||
if !visited.contains(child_id) {
|
descendant.modified = true;
|
||||||
descendant_stack.push(*child_id);
|
// update modified priority if score has changed
|
||||||
visited.insert(*child_id);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user