1use crate::server::Info;
2use crate::server::VirtualTxOutPoint;
3use crate::ExplorerUtxo;
4use crate::Vtxo;
5use bitcoin::Amount;
6use bitcoin::ScriptBuf;
7use bitcoin::XOnlyPublicKey;
8use std::collections::HashMap;
9use std::time::Duration;
10
11#[derive(Clone, Debug)]
12pub struct VtxoList {
13 pre_confirmed: Vec<VirtualTxOutPoint>,
15 confirmed: Vec<VirtualTxOutPoint>,
16 recoverable: Vec<VirtualTxOutPoint>,
17
18 spent: Vec<VirtualTxOutPoint>,
20}
21
22impl VtxoList {
23 pub fn new(
24 dust: Amount,
26 virtual_tx_outpoints: Vec<VirtualTxOutPoint>,
27 ) -> Self {
28 let mut recoverable = Vec::new();
29 let mut spent = Vec::new();
30 let mut pre_confirmed = Vec::new();
31 let mut confirmed = Vec::new();
32 for virtual_tx_outpoint in virtual_tx_outpoints {
33 if virtual_tx_outpoint.is_recoverable(dust) {
34 recoverable.push(virtual_tx_outpoint);
35 } else if virtual_tx_outpoint.is_unrolled
36 || virtual_tx_outpoint.is_spent
37 || virtual_tx_outpoint.is_swept
38 {
39 spent.push(virtual_tx_outpoint);
40 } else if virtual_tx_outpoint.is_preconfirmed {
41 pre_confirmed.push(virtual_tx_outpoint);
42 } else {
43 confirmed.push(virtual_tx_outpoint);
44 }
45 }
46
47 VtxoList {
48 pre_confirmed,
49 confirmed,
50 recoverable,
51 spent,
52 }
53 }
54
55 pub fn all(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
56 self.all_unspent().chain(self.spent())
57 }
58
59 pub fn all_unspent(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
60 self.pre_confirmed
61 .iter()
62 .chain(self.confirmed.iter())
63 .chain(self.recoverable.iter())
64 }
65
66 pub fn could_exit_unilaterally(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
71 self.pre_confirmed.iter().chain(self.confirmed.iter())
72 }
73
74 pub fn spendable_offchain(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
76 self.pre_confirmed.iter().chain(self.confirmed.iter())
77 }
78
79 pub fn spendable_offchain_at<'a, F>(
85 &'a self,
86 server_info: &'a Info,
87 now_unix_secs: i64,
88 server_pk_for_script: F,
89 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
90 where
91 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
92 {
93 self.spendable_offchain().filter(move |vtxo| {
94 !server_pk_for_script(&vtxo.script)
95 .map(|server_pk| server_info.signer_requires_recovery_at(server_pk, now_unix_secs))
96 .unwrap_or(false)
97 })
98 }
99
100 pub fn pending_recovery_due_to_signer_at<'a, F>(
103 &'a self,
104 server_info: &'a Info,
105 now_unix_secs: i64,
106 server_pk_for_script: F,
107 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
108 where
109 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
110 {
111 self.spendable_offchain().filter(move |vtxo| {
112 server_pk_for_script(&vtxo.script)
113 .map(|server_pk| server_info.signer_requires_recovery_at(server_pk, now_unix_secs))
114 .unwrap_or(false)
115 })
116 }
117
118 pub fn batch_settleable_at<'a, F>(
123 &'a self,
124 server_info: &'a Info,
125 now_unix_secs: i64,
126 server_pk_for_script: F,
127 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
128 where
129 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
130 {
131 let dust = server_info.dust;
132 self.all_unspent().filter(move |vtxo| {
133 vtxo.is_recoverable(dust)
134 || !server_pk_for_script(&vtxo.script)
135 .map(|server_pk| {
136 server_info.signer_requires_recovery_at(server_pk, now_unix_secs)
137 })
138 .unwrap_or(false)
139 })
140 }
141
142 pub fn pre_confirmed(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
143 self.pre_confirmed.iter()
144 }
145
146 pub fn confirmed(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
147 self.confirmed.iter()
148 }
149
150 pub fn recoverable(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
158 self.recoverable.iter()
159 }
160
161 pub fn exit_ready(
163 &self,
164 now: Duration,
165 explorer_utxos: Vec<ExplorerUtxo>,
167 vtxos: HashMap<ScriptBuf, Vtxo>,
169 ) -> impl Iterator<Item = &VirtualTxOutPoint> {
170 self.all_unspent().filter(move |v| {
171 match explorer_utxos
172 .iter()
173 .find(|explorer_utxo| explorer_utxo.outpoint == v.outpoint)
174 {
175 Some(ExplorerUtxo {
177 confirmation_blocktime: Some(confirmation_blocktime),
178 confirmations,
179 ..
180 }) => {
181 if let Some(vtxo) = vtxos.get(&v.script) {
183 vtxo.can_be_claimed_unilaterally_by_owner(
184 now,
185 Duration::from_secs(*confirmation_blocktime),
186 *confirmations,
187 )
188 } else {
189 false
190 }
191 }
192 _ => false,
193 }
194 })
195 }
196
197 pub fn spent(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
198 self.spent.iter()
199 }
200}