breez_sdk_liquid/recover/
model.rs

1use std::collections::HashMap;
2use std::str::FromStr;
3use std::sync::Arc;
4
5use lwk_wollet::elements::AssetId;
6use lwk_wollet::elements_miniscript::slip77::MasterBlindingKey;
7use lwk_wollet::WalletTx;
8
9use crate::chain::liquid::LiquidChainService;
10use crate::prelude::*;
11use crate::swapper::Swapper;
12
13/// A map of all our known LWK onchain txs, indexed by tx ID. Essentially our own cache of the LWK txs.
14#[derive(Clone)]
15pub(crate) struct TxMap {
16    pub(crate) outgoing_tx_map: HashMap<elements::Txid, WalletTx>,
17    pub(crate) incoming_tx_map: HashMap<elements::Txid, WalletTx>,
18}
19impl TxMap {
20    pub(crate) fn from_raw_tx_map(raw_tx_map: HashMap<elements::Txid, WalletTx>) -> Self {
21        let (outgoing_tx_map, incoming_tx_map): (
22            HashMap<elements::Txid, WalletTx>,
23            HashMap<elements::Txid, WalletTx>,
24        ) = raw_tx_map
25            .into_iter()
26            .partition(|(_, tx)| tx.balance.values().sum::<i64>() < 0);
27
28        Self {
29            outgoing_tx_map,
30            incoming_tx_map,
31        }
32    }
33}
34
35/// Swap list containing all swap data indexed by swap ID
36#[derive(Default)]
37pub(crate) struct SwapsList {
38    // Single map for all swap types indexed by swap ID
39    pub(crate) swaps_by_id: HashMap<String, Swap>,
40}
41
42impl TryFrom<Vec<Swap>> for SwapsList {
43    type Error = anyhow::Error;
44
45    fn try_from(swaps: Vec<Swap>) -> std::result::Result<Self, Self::Error> {
46        let mut swaps_list = Self::default();
47
48        for swap in swaps.into_iter() {
49            let swap_id = swap.id();
50            swaps_list.swaps_by_id.insert(swap_id, swap);
51        }
52
53        Ok(swaps_list)
54    }
55}
56
57impl SwapsList {
58    pub(crate) fn get_swap_lbtc_scripts(&self) -> Vec<LBtcScript> {
59        let mut swap_scripts = Vec::new();
60
61        for swap in self.swaps_by_id.values() {
62            match swap {
63                Swap::Send(send_swap) => {
64                    if let Ok(script) = send_swap.get_swap_script() {
65                        if let Some(funding_addr) = script.funding_addrs {
66                            swap_scripts.push(funding_addr.script_pubkey());
67                        }
68                    }
69                }
70                Swap::Receive(receive_swap) => {
71                    // Add claim script
72                    if let Ok(script) = receive_swap.get_swap_script() {
73                        if let Some(funding_addr) = script.funding_addrs {
74                            swap_scripts.push(funding_addr.script_pubkey());
75                        }
76                    }
77
78                    // Add MRH script if available
79                    if let Ok(mrh_address) = elements::Address::from_str(&receive_swap.mrh_address)
80                    {
81                        swap_scripts.push(mrh_address.script_pubkey());
82                    }
83                }
84                Swap::Chain(chain_swap) => {
85                    match chain_swap.direction {
86                        Direction::Outgoing => {
87                            // For outgoing chain swaps, add lockup script
88                            _ = chain_swap
89                                .get_lockup_swap_script()
90                                .and_then(|lockup_script| {
91                                    Ok(lockup_script.as_liquid_script()?.funding_addrs.map(
92                                        |funding_addr| {
93                                            swap_scripts.push(funding_addr.script_pubkey())
94                                        },
95                                    ))
96                                })
97                        }
98                        Direction::Incoming => {
99                            // For incoming chain swaps, add claim script
100                            _ = chain_swap.get_claim_swap_script().and_then(|claim_script| {
101                                Ok(claim_script.as_liquid_script()?.funding_addrs.map(
102                                    |funding_addr| swap_scripts.push(funding_addr.script_pubkey()),
103                                ))
104                            })
105                        }
106                    }
107                }
108            }
109        }
110
111        swap_scripts
112    }
113
114    pub(crate) fn get_swap_btc_scripts(&self) -> Vec<BtcScript> {
115        let mut swap_scripts = Vec::new();
116
117        for swap in self.swaps_by_id.values() {
118            if let Swap::Chain(chain_swap) = swap {
119                match chain_swap.direction {
120                    Direction::Outgoing => {
121                        // For outgoing chain swaps, add claim script (BTC)
122                        _ = chain_swap.get_claim_swap_script().and_then(|claim_script| {
123                            Ok(claim_script.as_bitcoin_script()?.funding_addrs.map(
124                                |funding_addr| swap_scripts.push(funding_addr.script_pubkey()),
125                            ))
126                        })
127                    }
128                    Direction::Incoming => {
129                        // For incoming chain swaps, add lockup script (BTC)
130                        _ = chain_swap
131                            .get_lockup_swap_script()
132                            .and_then(|lockup_script| {
133                                Ok(lockup_script.as_bitcoin_script()?.funding_addrs.map(
134                                    |funding_addr| swap_scripts.push(funding_addr.script_pubkey()),
135                                ))
136                            })
137                    }
138                }
139            }
140        }
141
142        swap_scripts
143    }
144}
145
146pub(crate) struct ReceiveOrSendSwapRecoveryContext {
147    pub(crate) lbtc_script_to_history_map: HashMap<LBtcScript, Vec<LBtcHistory>>,
148    pub(crate) liquid_chain_service: Arc<dyn LiquidChainService>,
149    pub(crate) swapper: Arc<dyn Swapper>,
150    pub(crate) tx_map: TxMap,
151    pub(crate) liquid_tip_height: u32,
152    pub(crate) lbtc_asset_id: AssetId,
153}
154
155pub(crate) struct ChainSwapRecoveryContext {
156    pub(crate) lbtc_script_to_history_map: HashMap<LBtcScript, Vec<LBtcHistory>>,
157    pub(crate) btc_script_to_history_map: HashMap<BtcScript, Vec<BtcHistory>>,
158    pub(crate) btc_script_to_txs_map: HashMap<BtcScript, Vec<bitcoin::Transaction>>,
159    pub(crate) btc_script_to_balance_map: HashMap<BtcScript, BtcScriptBalance>,
160    pub(crate) tx_map: TxMap,
161    pub(crate) master_blinding_key: MasterBlindingKey,
162    pub(crate) liquid_tip_height: u32,
163    pub(crate) bitcoin_tip_height: u32,
164}