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_spent_status(dust) {
36 spent.push(virtual_tx_outpoint);
37 } else if virtual_tx_outpoint.is_pre_confirmed_spendable(dust) {
38 pre_confirmed.push(virtual_tx_outpoint);
39 } else if virtual_tx_outpoint.is_confirmed_spendable(dust) {
40 confirmed.push(virtual_tx_outpoint);
41 }
42 }
43
44 VtxoList {
45 pre_confirmed,
46 confirmed,
47 recoverable,
48 spent,
49 }
50 }
51
52 pub fn all(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
53 self.all_unspent().chain(self.spent())
54 }
55
56 pub fn all_unspent(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
57 self.pre_confirmed
58 .iter()
59 .chain(self.confirmed.iter())
60 .chain(self.recoverable.iter())
61 }
62
63 pub fn could_exit_unilaterally(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
68 self.pre_confirmed.iter().chain(self.confirmed.iter())
69 }
70
71 pub fn spendable_offchain(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
73 self.pre_confirmed.iter().chain(self.confirmed.iter())
74 }
75
76 pub fn spendable_offchain_at<'a, F>(
82 &'a self,
83 server_info: &'a Info,
84 now_unix_secs: i64,
85 server_pk_for_script: F,
86 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
87 where
88 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
89 {
90 self.spendable_offchain().filter(move |vtxo| {
91 !server_pk_for_script(&vtxo.script)
92 .map(|server_pk| server_info.signer_requires_recovery_at(server_pk, now_unix_secs))
93 .unwrap_or(false)
94 })
95 }
96
97 pub fn pending_recovery_due_to_signer_at<'a, F>(
100 &'a self,
101 server_info: &'a Info,
102 now_unix_secs: i64,
103 server_pk_for_script: F,
104 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
105 where
106 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
107 {
108 self.spendable_offchain().filter(move |vtxo| {
109 server_pk_for_script(&vtxo.script)
110 .map(|server_pk| server_info.signer_requires_recovery_at(server_pk, now_unix_secs))
111 .unwrap_or(false)
112 })
113 }
114
115 pub fn batch_settleable_at<'a, F>(
120 &'a self,
121 server_info: &'a Info,
122 now_unix_secs: i64,
123 server_pk_for_script: F,
124 ) -> impl Iterator<Item = &'a VirtualTxOutPoint> + 'a
125 where
126 F: Fn(&ScriptBuf) -> Option<XOnlyPublicKey> + 'a,
127 {
128 let dust = server_info.dust;
129 self.all_unspent().filter(move |vtxo| {
130 vtxo.is_recoverable(dust)
131 || !server_pk_for_script(&vtxo.script)
132 .map(|server_pk| {
133 server_info.signer_requires_recovery_at(server_pk, now_unix_secs)
134 })
135 .unwrap_or(false)
136 })
137 }
138
139 pub fn pre_confirmed(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
140 self.pre_confirmed.iter()
141 }
142
143 pub fn confirmed(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
144 self.confirmed.iter()
145 }
146
147 pub fn recoverable(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
155 self.recoverable.iter()
156 }
157
158 pub fn exit_ready(
160 &self,
161 now: Duration,
162 explorer_utxos: Vec<ExplorerUtxo>,
164 vtxos: HashMap<ScriptBuf, Vtxo>,
166 ) -> impl Iterator<Item = &VirtualTxOutPoint> {
167 self.all_unspent().filter(move |v| {
168 match explorer_utxos
169 .iter()
170 .find(|explorer_utxo| explorer_utxo.outpoint == v.outpoint)
171 {
172 Some(ExplorerUtxo {
174 confirmation_blocktime: Some(confirmation_blocktime),
175 confirmations,
176 ..
177 }) => {
178 if let Some(vtxo) = vtxos.get(&v.script) {
180 vtxo.can_be_claimed_unilaterally_by_owner(
181 now,
182 Duration::from_secs(*confirmation_blocktime),
183 *confirmations,
184 )
185 } else {
186 false
187 }
188 }
189 _ => false,
190 }
191 })
192 }
193
194 pub fn spent(&self) -> impl Iterator<Item = &VirtualTxOutPoint> {
195 self.spent.iter()
196 }
197}