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 {
 | 
			
		||||
  uid: number
 | 
			
		||||
  order: number
 | 
			
		||||
  fee: number
 | 
			
		||||
  weight: number
 | 
			
		||||
  sigops: number
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ use std::{
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct AuditTransaction {
 | 
			
		||||
    pub uid: u32,
 | 
			
		||||
    order: u32,
 | 
			
		||||
    pub fee: u64,
 | 
			
		||||
    pub weight: u32,
 | 
			
		||||
    pub sigop_adjusted_vsize: u32,
 | 
			
		||||
@ -50,19 +51,24 @@ impl PartialEq for AuditTransaction {
 | 
			
		||||
impl Eq for AuditTransaction {}
 | 
			
		||||
 | 
			
		||||
#[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,
 | 
			
		||||
    // and partial_cmp will return None
 | 
			
		||||
    if a.1 == b.1 {
 | 
			
		||||
        Some(a.0.cmp(&b.0))
 | 
			
		||||
    if a.2 != b.2 {
 | 
			
		||||
        a.2.partial_cmp(&b.2)
 | 
			
		||||
    } else if a.1 != b.1 {
 | 
			
		||||
        Some(b.1.cmp(&a.1))
 | 
			
		||||
    } else {
 | 
			
		||||
        a.1.partial_cmp(&b.1)
 | 
			
		||||
        Some(a.0.cmp(&b.0))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialOrd for AuditTransaction {
 | 
			
		||||
    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);
 | 
			
		||||
        Self {
 | 
			
		||||
            uid: tx.uid,
 | 
			
		||||
            order: tx.order,
 | 
			
		||||
            fee: tx.fee as u64,
 | 
			
		||||
            weight: tx.weight,
 | 
			
		||||
            sigop_adjusted_vsize,
 | 
			
		||||
@ -113,6 +120,11 @@ impl AuditTransaction {
 | 
			
		||||
        self.score
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn order(&self) -> u32 {
 | 
			
		||||
        self.order
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub const fn ancestor_sigop_adjusted_vsize(&self) -> u32 {
 | 
			
		||||
        self.ancestor_sigop_adjusted_vsize
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ type ModifiedQueue = PriorityQueue<u32, TxPriority, U32HasherState>;
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct TxPriority {
 | 
			
		||||
    uid: u32,
 | 
			
		||||
    order: u32,
 | 
			
		||||
    score: f64,
 | 
			
		||||
}
 | 
			
		||||
impl PartialEq for TxPriority {
 | 
			
		||||
@ -35,10 +36,12 @@ impl PartialEq for TxPriority {
 | 
			
		||||
impl Eq for TxPriority {}
 | 
			
		||||
impl PartialOrd for TxPriority {
 | 
			
		||||
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 | 
			
		||||
        if self.score == other.score {
 | 
			
		||||
            Some(self.uid.cmp(&other.uid))
 | 
			
		||||
        } else {
 | 
			
		||||
        if self.score != 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);
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
        .map(|txid| {
 | 
			
		||||
            let atx = audit_pool
 | 
			
		||||
                .get(&txid)
 | 
			
		||||
                .expect("All txids are from audit_pool");
 | 
			
		||||
            (txid, atx.score())
 | 
			
		||||
            (txid, atx.order(), atx.score())
 | 
			
		||||
        })
 | 
			
		||||
        .collect();
 | 
			
		||||
    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!("(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,
 | 
			
		||||
                            TxPriority {
 | 
			
		||||
                                uid: *overflowed,
 | 
			
		||||
                                order: overflowed_tx.order(),
 | 
			
		||||
                                score: overflowed_tx.score(),
 | 
			
		||||
                            },
 | 
			
		||||
                        );
 | 
			
		||||
@ -376,6 +380,7 @@ fn update_descendants(
 | 
			
		||||
                    descendant.uid,
 | 
			
		||||
                    TxPriority {
 | 
			
		||||
                        uid: descendant.uid,
 | 
			
		||||
                        order: descendant.order(),
 | 
			
		||||
                        score: descendant.score(),
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
@ -385,6 +390,7 @@ fn update_descendants(
 | 
			
		||||
                    descendant.uid,
 | 
			
		||||
                    TxPriority {
 | 
			
		||||
                        uid: descendant.uid,
 | 
			
		||||
                        order: descendant.order(),
 | 
			
		||||
                        score: descendant.score(),
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ mod u32_hasher_types;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
const STARTING_CAPACITY: usize = 1_048_576;
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ use napi_derive::napi;
 | 
			
		||||
#[napi(object)]
 | 
			
		||||
pub struct ThreadTransaction {
 | 
			
		||||
    pub uid: u32,
 | 
			
		||||
    pub order: u32,
 | 
			
		||||
    pub fee: f64,
 | 
			
		||||
    pub weight: u32,
 | 
			
		||||
    pub sigops: u32,
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import { GbtGenerator } from '../../../rust-gbt';
 | 
			
		||||
import { GbtGenerator, ThreadTransaction } from '../../../rust-gbt';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { CompactThreadTransaction } from '../../mempool.interfaces';
 | 
			
		||||
 | 
			
		||||
const baseline = require('./test-data/target-template.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 count = view.getUint32(0, false);
 | 
			
		||||
  const txs: CompactThreadTransaction[] = [];
 | 
			
		||||
  const txs: ThreadTransaction[] = [];
 | 
			
		||||
  let offset = 4;
 | 
			
		||||
  for (let i = 0; i < count; i++) {
 | 
			
		||||
    const tx: CompactThreadTransaction = {
 | 
			
		||||
      uid: view.getUint32(offset, false),
 | 
			
		||||
    const uid = view.getUint32(offset, false);
 | 
			
		||||
    const tx: ThreadTransaction = {
 | 
			
		||||
      uid,
 | 
			
		||||
      order: txidToOrdering(vectorUidMap.get(uid) as string),
 | 
			
		||||
      fee: view.getFloat64(offset + 4, false),
 | 
			
		||||
      weight: view.getUint32(offset + 12, 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),
 | 
			
		||||
      inputs: [],
 | 
			
		||||
    };
 | 
			
		||||
@ -53,3 +54,7 @@ function mempoolFromArrayBuffer(buf: ArrayBuffer): CompactThreadTransaction[] {
 | 
			
		||||
  }
 | 
			
		||||
  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) {
 | 
			
		||||
        this.mempoolCache[txid] = transactionUtils.extendMempoolTransaction(this.mempoolCache[txid]);
 | 
			
		||||
      }
 | 
			
		||||
      if (this.mempoolCache[txid].order == null) {
 | 
			
		||||
        this.mempoolCache[txid].order = transactionUtils.txidToOrdering(txid);
 | 
			
		||||
      }
 | 
			
		||||
      count++;
 | 
			
		||||
    }
 | 
			
		||||
    if (this.mempoolChangedCallback) {
 | 
			
		||||
 | 
			
		||||
@ -76,6 +76,7 @@ class TransactionUtils {
 | 
			
		||||
    const adjustedFeePerVsize = Math.max(Common.isLiquid() ? 0.1 : 1,
 | 
			
		||||
      (transaction.fee || 0) / adjustedVsize);
 | 
			
		||||
    const transactionExtended: MempoolTransactionExtended = Object.assign(transaction, {
 | 
			
		||||
      order: this.txidToOrdering(transaction.txid),
 | 
			
		||||
      vsize: Math.round(transaction.weight / 4),
 | 
			
		||||
      adjustedVsize,
 | 
			
		||||
      sigops,
 | 
			
		||||
@ -154,6 +155,11 @@ class TransactionUtils {
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
@ -93,6 +93,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MempoolTransactionExtended extends TransactionExtended {
 | 
			
		||||
  order: number;
 | 
			
		||||
  sigops: number;
 | 
			
		||||
  adjustedVsize: number;
 | 
			
		||||
  adjustedFeePerVsize: number;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user