1use alloc::sync::Arc;
13use bitcoin::secp256k1::{All, Secp256k1};
14use bitcoin::{
15 absolute, relative, Amount, FeeRate, Script, Sequence, SignedAmount, Transaction, Txid,
16};
17use chain::{ChainPosition, ConfirmationBlockTime};
18use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
19
20use rand_core::RngCore;
21
22pub trait IsDust {
28 fn is_dust(&self, script: &Script) -> bool;
30}
31
32impl IsDust for Amount {
33 fn is_dust(&self, script: &Script) -> bool {
34 *self < script.minimal_non_dust()
35 }
36}
37
38impl IsDust for u64 {
39 fn is_dust(&self, script: &Script) -> bool {
40 Amount::from_sat(*self).is_dust(script)
41 }
42}
43
44pub struct After {
45 pub current_height: Option<u32>,
46 pub assume_height_reached: bool,
47}
48
49impl After {
50 pub(crate) fn new(current_height: Option<u32>, assume_height_reached: bool) -> After {
51 After {
52 current_height,
53 assume_height_reached,
54 }
55 }
56}
57
58pub(crate) fn check_nsequence_rbf(sequence: Sequence, csv: Sequence) -> bool {
59 if !sequence.is_relative_lock_time() {
61 return false;
62 }
63
64 if sequence.is_time_locked() != csv.is_time_locked() {
67 return false;
68 }
69
70 if sequence < csv {
72 return false;
73 }
74
75 true
76}
77
78impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
79 fn check_after(&self, n: absolute::LockTime) -> bool {
80 if let Some(current_height) = self.current_height {
81 current_height >= n.to_consensus_u32()
82 } else {
83 self.assume_height_reached
84 }
85 }
86}
87
88pub struct Older {
89 pub current_height: Option<u32>,
90 pub create_height: Option<u32>,
91 pub assume_height_reached: bool,
92}
93
94impl Older {
95 pub(crate) fn new(
96 current_height: Option<u32>,
97 create_height: Option<u32>,
98 assume_height_reached: bool,
99 ) -> Older {
100 Older {
101 current_height,
102 create_height,
103 assume_height_reached,
104 }
105 }
106}
107
108impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
109 fn check_older(&self, n: relative::LockTime) -> bool {
110 if let Some(current_height) = self.current_height {
111 current_height
113 >= self
114 .create_height
115 .unwrap_or(0)
116 .checked_add(n.to_consensus_u32())
117 .expect("Overflowing addition")
118 } else {
119 self.assume_height_reached
120 }
121 }
122}
123
124pub(crate) fn shuffle_slice<T>(list: &mut [T], rng: &mut impl RngCore) {
126 if list.is_empty() {
127 return;
128 }
129 let mut current_index = list.len() - 1;
130 while current_index > 0 {
131 let random_index = rng.next_u32() as usize % (current_index + 1);
132 list.swap(current_index, random_index);
133 current_index -= 1;
134 }
135}
136
137pub(crate) type SecpCtx = Secp256k1<All>;
138
139#[derive(Debug)]
141pub struct TxDetails {
142 pub txid: Txid,
144 pub sent: Amount,
147 pub received: Amount,
149 pub fee: Option<Amount>,
153 pub fee_rate: Option<FeeRate>,
158 pub balance_delta: SignedAmount,
160 pub chain_position: ChainPosition<ConfirmationBlockTime>,
162 pub tx: Arc<Transaction>,
164}
165
166#[cfg(test)]
167mod test {
168 pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
171
172 use super::{check_nsequence_rbf, shuffle_slice, IsDust};
173 use crate::bitcoin::{Address, Network, Sequence};
174 use alloc::vec::Vec;
175 use core::str::FromStr;
176 use rand::{rngs::StdRng, thread_rng, SeedableRng};
177
178 #[test]
179 fn test_is_dust() {
180 let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
181 .unwrap()
182 .require_network(Network::Bitcoin)
183 .unwrap()
184 .script_pubkey();
185 assert!(script_p2pkh.is_p2pkh());
186 assert!(545.is_dust(&script_p2pkh));
187 assert!(!546.is_dust(&script_p2pkh));
188
189 let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
190 .unwrap()
191 .require_network(Network::Bitcoin)
192 .unwrap()
193 .script_pubkey();
194 assert!(script_p2wpkh.is_p2wpkh());
195 assert!(293.is_dust(&script_p2wpkh));
196 assert!(!294.is_dust(&script_p2wpkh));
197 }
198
199 #[test]
200 fn test_check_nsequence_rbf_msb_set() {
201 let result = check_nsequence_rbf(Sequence(0x80000000), Sequence(5000));
202 assert!(!result);
203 }
204
205 #[test]
206 fn test_check_nsequence_rbf_lt_csv() {
207 let result = check_nsequence_rbf(Sequence(4000), Sequence(5000));
208 assert!(!result);
209 }
210
211 #[test]
212 fn test_check_nsequence_rbf_different_unit() {
213 let result =
214 check_nsequence_rbf(Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000), Sequence(5000));
215 assert!(!result);
216 }
217
218 #[test]
219 fn test_check_nsequence_rbf_mask() {
220 let result = check_nsequence_rbf(Sequence(0x3f + 10_000), Sequence(5000));
221 assert!(result);
222 }
223
224 #[test]
225 fn test_check_nsequence_rbf_same_unit_blocks() {
226 let result = check_nsequence_rbf(Sequence(10_000), Sequence(5000));
227 assert!(result);
228 }
229
230 #[test]
231 fn test_check_nsequence_rbf_same_unit_time() {
232 let result = check_nsequence_rbf(
233 Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 10_000),
234 Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000),
235 );
236 assert!(result);
237 }
238
239 #[test]
240 #[cfg(feature = "std")]
241 fn test_shuffle_slice_empty_vec() {
242 let mut test: Vec<u8> = vec![];
243 shuffle_slice(&mut test, &mut thread_rng());
244 }
245
246 #[test]
247 #[cfg(feature = "std")]
248 fn test_shuffle_slice_single_vec() {
249 let mut test: Vec<u8> = vec![0];
250 shuffle_slice(&mut test, &mut thread_rng());
251 }
252
253 #[test]
254 fn test_shuffle_slice_duple_vec() {
255 let seed = [0; 32];
256 let mut rng: StdRng = SeedableRng::from_seed(seed);
257 let mut test: Vec<u8> = vec![0, 1];
258 shuffle_slice(&mut test, &mut rng);
259 assert_eq!(test, &[0, 1]);
260 let seed = [6; 32];
261 let mut rng: StdRng = SeedableRng::from_seed(seed);
262 let mut test: Vec<u8> = vec![0, 1];
263 shuffle_slice(&mut test, &mut rng);
264 assert_eq!(test, &[1, 0]);
265 }
266
267 #[test]
268 fn test_shuffle_slice_multi_vec() {
269 let seed = [0; 32];
270 let mut rng: StdRng = SeedableRng::from_seed(seed);
271 let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
272 shuffle_slice(&mut test, &mut rng);
273 assert_eq!(test, &[2, 1, 0, 4, 5]);
274 let seed = [25; 32];
275 let mut rng: StdRng = SeedableRng::from_seed(seed);
276 let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
277 shuffle_slice(&mut test, &mut rng);
278 assert_eq!(test, &[0, 4, 1, 2, 5]);
279 }
280}