Mimic Core's ordering for equal-score transactions
This commit is contained in:
parent
af6de9b72c
commit
23d487b904
1
backend/rust-gbt/index.d.ts
vendored
1
backend/rust-gbt/index.d.ts
vendored
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
export interface ThreadTransaction {
|
export interface ThreadTransaction {
|
||||||
uid: number
|
uid: number
|
||||||
|
order: number
|
||||||
fee: number
|
fee: number
|
||||||
weight: number
|
weight: number
|
||||||
sigops: number
|
sigops: number
|
||||||
|
@ -12,6 +12,7 @@ use std::{
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AuditTransaction {
|
pub struct AuditTransaction {
|
||||||
pub uid: u32,
|
pub uid: u32,
|
||||||
|
order: u32,
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
pub weight: u32,
|
pub weight: u32,
|
||||||
pub sigop_adjusted_vsize: u32,
|
pub sigop_adjusted_vsize: u32,
|
||||||
@ -50,19 +51,24 @@ impl PartialEq for AuditTransaction {
|
|||||||
impl Eq for AuditTransaction {}
|
impl Eq for AuditTransaction {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn partial_cmp_uid_score(a: (u32, f64), b: (u32, f64)) -> Option<Ordering> {
|
pub fn partial_cmp_uid_score(a: (u32, u32, f64), b: (u32, u32, f64)) -> Option<Ordering> {
|
||||||
// If either score is NaN, this is false,
|
// If either score is NaN, this is false,
|
||||||
// and partial_cmp will return None
|
// and partial_cmp will return None
|
||||||
if a.1 == b.1 {
|
if a.2 != b.2 {
|
||||||
Some(a.0.cmp(&b.0))
|
a.2.partial_cmp(&b.2)
|
||||||
|
} else if a.1 != b.1 {
|
||||||
|
Some(b.1.cmp(&a.1))
|
||||||
} else {
|
} else {
|
||||||
a.1.partial_cmp(&b.1)
|
Some(a.0.cmp(&b.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for AuditTransaction {
|
impl PartialOrd for AuditTransaction {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
partial_cmp_uid_score((self.uid, self.score), (other.uid, other.score))
|
partial_cmp_uid_score(
|
||||||
|
(self.uid, self.order, self.score),
|
||||||
|
(other.uid, self.order, other.score),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +92,7 @@ impl AuditTransaction {
|
|||||||
let sigop_adjusted_vsize = ((tx.weight + 3) / 4).max(tx.sigops * 5);
|
let sigop_adjusted_vsize = ((tx.weight + 3) / 4).max(tx.sigops * 5);
|
||||||
Self {
|
Self {
|
||||||
uid: tx.uid,
|
uid: tx.uid,
|
||||||
|
order: tx.order,
|
||||||
fee: tx.fee as u64,
|
fee: tx.fee as u64,
|
||||||
weight: tx.weight,
|
weight: tx.weight,
|
||||||
sigop_adjusted_vsize,
|
sigop_adjusted_vsize,
|
||||||
@ -113,6 +120,11 @@ impl AuditTransaction {
|
|||||||
self.score
|
self.score
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn order(&self) -> u32 {
|
||||||
|
self.order
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn ancestor_sigop_adjusted_vsize(&self) -> u32 {
|
pub const fn ancestor_sigop_adjusted_vsize(&self) -> u32 {
|
||||||
self.ancestor_sigop_adjusted_vsize
|
self.ancestor_sigop_adjusted_vsize
|
||||||
|
@ -25,6 +25,7 @@ type ModifiedQueue = PriorityQueue<u32, TxPriority, U32HasherState>;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TxPriority {
|
struct TxPriority {
|
||||||
uid: u32,
|
uid: u32,
|
||||||
|
order: u32,
|
||||||
score: f64,
|
score: f64,
|
||||||
}
|
}
|
||||||
impl PartialEq for TxPriority {
|
impl PartialEq for TxPriority {
|
||||||
@ -35,10 +36,12 @@ impl PartialEq for TxPriority {
|
|||||||
impl Eq for TxPriority {}
|
impl Eq for TxPriority {}
|
||||||
impl PartialOrd for TxPriority {
|
impl PartialOrd for TxPriority {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
if self.score == other.score {
|
if self.score != other.score {
|
||||||
Some(self.uid.cmp(&other.uid))
|
|
||||||
} else {
|
|
||||||
self.score.partial_cmp(&other.score)
|
self.score.partial_cmp(&other.score)
|
||||||
|
} else if self.order != other.order {
|
||||||
|
Some(other.order.cmp(&self.order))
|
||||||
|
} else {
|
||||||
|
Some(self.uid.cmp(&other.uid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,17 +83,17 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult {
|
|||||||
trace!("Post relative graph Audit Pool: {:#?}", audit_pool);
|
trace!("Post relative graph Audit Pool: {:#?}", audit_pool);
|
||||||
|
|
||||||
info!("Sorting by descending ancestor score");
|
info!("Sorting by descending ancestor score");
|
||||||
let mut mempool_stack: Vec<(u32, f64)> = mempool_stack
|
let mut mempool_stack: Vec<(u32, u32, f64)> = mempool_stack
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|txid| {
|
.map(|txid| {
|
||||||
let atx = audit_pool
|
let atx = audit_pool
|
||||||
.get(&txid)
|
.get(&txid)
|
||||||
.expect("All txids are from audit_pool");
|
.expect("All txids are from audit_pool");
|
||||||
(txid, atx.score())
|
(txid, atx.order(), atx.score())
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
mempool_stack.sort_unstable_by(|a, b| partial_cmp_uid_score(*a, *b).expect("Not NaN"));
|
mempool_stack.sort_unstable_by(|a, b| partial_cmp_uid_score(*a, *b).expect("Not NaN"));
|
||||||
let mut mempool_stack: Vec<u32> = mempool_stack.into_iter().map(|(txid, _)| txid).collect();
|
let mut mempool_stack: Vec<u32> = mempool_stack.into_iter().map(|(txid, _, _)| txid).collect();
|
||||||
|
|
||||||
info!("Building blocks by greedily choosing the highest feerate package");
|
info!("Building blocks by greedily choosing the highest feerate package");
|
||||||
info!("(i.e. the package rooted in the transaction with the best ancestor score)");
|
info!("(i.e. the package rooted in the transaction with the best ancestor score)");
|
||||||
@ -212,6 +215,7 @@ pub fn gbt(mempool: &mut ThreadTransactionsMap) -> GbtResult {
|
|||||||
*overflowed,
|
*overflowed,
|
||||||
TxPriority {
|
TxPriority {
|
||||||
uid: *overflowed,
|
uid: *overflowed,
|
||||||
|
order: overflowed_tx.order(),
|
||||||
score: overflowed_tx.score(),
|
score: overflowed_tx.score(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -376,6 +380,7 @@ fn update_descendants(
|
|||||||
descendant.uid,
|
descendant.uid,
|
||||||
TxPriority {
|
TxPriority {
|
||||||
uid: descendant.uid,
|
uid: descendant.uid,
|
||||||
|
order: descendant.order(),
|
||||||
score: descendant.score(),
|
score: descendant.score(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -385,6 +390,7 @@ fn update_descendants(
|
|||||||
descendant.uid,
|
descendant.uid,
|
||||||
TxPriority {
|
TxPriority {
|
||||||
uid: descendant.uid,
|
uid: descendant.uid,
|
||||||
|
order: descendant.order(),
|
||||||
score: descendant.score(),
|
score: descendant.score(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -23,7 +23,7 @@ mod u32_hasher_types;
|
|||||||
|
|
||||||
use u32_hasher_types::{u32hashmap_with_capacity, U32HasherState};
|
use u32_hasher_types::{u32hashmap_with_capacity, U32HasherState};
|
||||||
|
|
||||||
/// This is the initial capacity of the GbtGenerator struct's inner HashMap.
|
/// This is the initial capacity of the `GbtGenerator` struct's inner `HashMap`.
|
||||||
///
|
///
|
||||||
/// Note: This doesn't *have* to be a power of 2. (uwu)
|
/// Note: This doesn't *have* to be a power of 2. (uwu)
|
||||||
const STARTING_CAPACITY: usize = 1_048_576;
|
const STARTING_CAPACITY: usize = 1_048_576;
|
||||||
|
@ -4,6 +4,7 @@ use napi_derive::napi;
|
|||||||
#[napi(object)]
|
#[napi(object)]
|
||||||
pub struct ThreadTransaction {
|
pub struct ThreadTransaction {
|
||||||
pub uid: u32,
|
pub uid: u32,
|
||||||
|
pub order: u32,
|
||||||
pub fee: f64,
|
pub fee: f64,
|
||||||
pub weight: u32,
|
pub weight: u32,
|
||||||
pub sigops: u32,
|
pub sigops: u32,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { GbtGenerator } from '../../../rust-gbt';
|
import { GbtGenerator, ThreadTransaction } from '../../../rust-gbt';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { CompactThreadTransaction } from '../../mempool.interfaces';
|
|
||||||
|
|
||||||
const baseline = require('./test-data/target-template.json');
|
const baseline = require('./test-data/target-template.json');
|
||||||
const testVector = require('./test-data/test-data-ids.json');
|
const testVector = require('./test-data/test-data-ids.json');
|
||||||
@ -28,18 +27,20 @@ describe('Rust GBT', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function mempoolFromArrayBuffer(buf: ArrayBuffer): CompactThreadTransaction[] {
|
function mempoolFromArrayBuffer(buf: ArrayBuffer): ThreadTransaction[] {
|
||||||
const view = new DataView(buf);
|
const view = new DataView(buf);
|
||||||
const count = view.getUint32(0, false);
|
const count = view.getUint32(0, false);
|
||||||
const txs: CompactThreadTransaction[] = [];
|
const txs: ThreadTransaction[] = [];
|
||||||
let offset = 4;
|
let offset = 4;
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const tx: CompactThreadTransaction = {
|
const uid = view.getUint32(offset, false);
|
||||||
uid: view.getUint32(offset, false),
|
const tx: ThreadTransaction = {
|
||||||
|
uid,
|
||||||
|
order: txidToOrdering(vectorUidMap.get(uid) as string),
|
||||||
fee: view.getFloat64(offset + 4, false),
|
fee: view.getFloat64(offset + 4, false),
|
||||||
weight: view.getUint32(offset + 12, false),
|
weight: view.getUint32(offset + 12, false),
|
||||||
sigops: view.getUint32(offset + 16, false),
|
sigops: view.getUint32(offset + 16, false),
|
||||||
feePerVsize: view.getFloat64(offset + 20, false),
|
// feePerVsize: view.getFloat64(offset + 20, false),
|
||||||
effectiveFeePerVsize: view.getFloat64(offset + 28, false),
|
effectiveFeePerVsize: view.getFloat64(offset + 28, false),
|
||||||
inputs: [],
|
inputs: [],
|
||||||
};
|
};
|
||||||
@ -53,3 +54,7 @@ function mempoolFromArrayBuffer(buf: ArrayBuffer): CompactThreadTransaction[] {
|
|||||||
}
|
}
|
||||||
return txs;
|
return txs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function txidToOrdering(txid: string): number {
|
||||||
|
return parseInt(txid.slice(56).match(/../g)?.reverse().join('') as string, 16);
|
||||||
|
}
|
||||||
|
@ -89,6 +89,9 @@ class Mempool {
|
|||||||
if (this.mempoolCache[txid].sigops == null || this.mempoolCache[txid].effectiveFeePerVsize == null) {
|
if (this.mempoolCache[txid].sigops == null || this.mempoolCache[txid].effectiveFeePerVsize == null) {
|
||||||
this.mempoolCache[txid] = transactionUtils.extendMempoolTransaction(this.mempoolCache[txid]);
|
this.mempoolCache[txid] = transactionUtils.extendMempoolTransaction(this.mempoolCache[txid]);
|
||||||
}
|
}
|
||||||
|
if (this.mempoolCache[txid].order == null) {
|
||||||
|
this.mempoolCache[txid].order = transactionUtils.txidToOrdering(txid);
|
||||||
|
}
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
if (this.mempoolChangedCallback) {
|
if (this.mempoolChangedCallback) {
|
||||||
|
@ -76,6 +76,7 @@ class TransactionUtils {
|
|||||||
const adjustedFeePerVsize = Math.max(Common.isLiquid() ? 0.1 : 1,
|
const adjustedFeePerVsize = Math.max(Common.isLiquid() ? 0.1 : 1,
|
||||||
(transaction.fee || 0) / adjustedVsize);
|
(transaction.fee || 0) / adjustedVsize);
|
||||||
const transactionExtended: MempoolTransactionExtended = Object.assign(transaction, {
|
const transactionExtended: MempoolTransactionExtended = Object.assign(transaction, {
|
||||||
|
order: this.txidToOrdering(transaction.txid),
|
||||||
vsize: Math.round(transaction.weight / 4),
|
vsize: Math.round(transaction.weight / 4),
|
||||||
adjustedVsize,
|
adjustedVsize,
|
||||||
sigops,
|
sigops,
|
||||||
@ -154,6 +155,11 @@ class TransactionUtils {
|
|||||||
|
|
||||||
return sigops;
|
return sigops;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the most significant 4 bytes of the txid as an integer
|
||||||
|
public txidToOrdering(txid: string): number {
|
||||||
|
return parseInt(txid.slice(56).match(/../g)?.reverse().join('') as string, 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new TransactionUtils();
|
export default new TransactionUtils();
|
||||||
|
@ -93,6 +93,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MempoolTransactionExtended extends TransactionExtended {
|
export interface MempoolTransactionExtended extends TransactionExtended {
|
||||||
|
order: number;
|
||||||
sigops: number;
|
sigops: number;
|
||||||
adjustedVsize: number;
|
adjustedVsize: number;
|
||||||
adjustedFeePerVsize: number;
|
adjustedFeePerVsize: number;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user