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