1use bitcoin::{Amount, BlockHash, Transaction, Txid};
4use slotmap::DefaultKey;
5use std::collections::HashSet;
6use std::sync::Arc;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct EntryId(pub(crate) DefaultKey);
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14pub struct FeeRate(pub u64);
15
16impl FeeRate {
17 pub fn from_sat_per_vb(sat_vb: u64) -> Self {
19 Self(sat_vb.checked_mul(1000).expect("Fee rate overflow"))
20 }
21
22 pub fn from_sat_per_kvb(sat_kvb: u64) -> Self {
24 Self(sat_kvb)
25 }
26
27 pub fn from_amount_and_vsize(fee: Amount, vsize: i64) -> Result<Self, &'static str> {
31 if vsize <= 0 {
32 return Err("vsize must be positive");
33 }
34
35 let fee_sat = fee.to_sat();
36 let vsize_u64 = vsize as u64;
37
38 let numerator = fee_sat
40 .checked_mul(1000)
41 .ok_or("Fee rate calculation overflow")?;
42
43 Ok(Self(numerator / vsize_u64))
44 }
45
46 pub fn get_fee(&self, vsize: i64) -> Amount {
48 let fee_sat = (self
49 .0
50 .checked_mul(vsize as u64)
51 .expect("Fee calculation overflow"))
52 .checked_div(1000)
53 .unwrap_or(0);
54 Amount::from_sat(fee_sat)
55 }
56
57 pub fn as_sat_per_kvb(&self) -> u64 {
59 self.0
60 }
61}
62
63#[derive(Debug, Clone, Default)]
65pub struct LockPoints {
66 pub height: i32,
68 pub time: i64,
70 pub max_input_block: Option<BlockHash>,
72}
73
74pub struct ValidationResult {
76 pub base_fee: Amount,
78 pub sigop_cost: i64,
80 pub lock_points: LockPoints,
82 pub ancestors: HashSet<EntryId>,
84 pub conflicts: HashSet<Txid>,
86 pub spends_coinbase: bool,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum RemovalReason {
93 Block,
95 Reorg,
97 Conflict,
99 Replaced,
101 SizeLimit,
103 Expiry,
105}
106
107impl RemovalReason {
108 pub fn as_str(&self) -> &'static str {
109 match self {
110 Self::Block => "block",
111 Self::Reorg => "reorg",
112 Self::Conflict => "conflict",
113 Self::Replaced => "replaced",
114 Self::SizeLimit => "sizelimit",
115 Self::Expiry => "expiry",
116 }
117 }
118}
119
120#[derive(Debug, Clone)]
122pub struct ConflictSet {
123 pub direct_conflicts: HashSet<EntryId>,
125
126 pub all_conflicts: HashSet<EntryId>,
128
129 pub removed_transactions: Vec<Arc<Transaction>>,
132
133 pub replaced_fees: bitcoin::Amount,
135
136 pub replaced_size: i64,
138}
139
140#[derive(Debug, Clone)]
142pub struct Package {
143 pub transactions: Vec<Arc<Transaction>>,
144}
145
146#[derive(Debug)]
148pub struct PackageValidationResult {
149 pub accepted: Vec<Txid>,
150 pub package_feerate: FeeRate,
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_fee_rate_from_amount_and_vsize() {
159 let fee = Amount::from_sat(1000);
161 assert_eq!(
162 FeeRate::from_amount_and_vsize(fee, 250)
163 .unwrap()
164 .as_sat_per_kvb(),
165 4000
166 );
167
168 let fee = Amount::from_sat(500);
170 assert_eq!(
171 FeeRate::from_amount_and_vsize(fee, 200)
172 .unwrap()
173 .as_sat_per_kvb(),
174 2500
175 );
176
177 let fee = Amount::from_sat(1000);
179 assert!(FeeRate::from_amount_and_vsize(fee, 0).is_err());
180
181 assert!(FeeRate::from_amount_and_vsize(fee, -1).is_err());
183 }
184}