Skip to content

Commit

Permalink
Add MRH scripts the recover and sync
Browse files Browse the repository at this point in the history
  • Loading branch information
dangeross committed Nov 18, 2024
1 parent 0b838eb commit 807bc5b
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 158 deletions.
2 changes: 0 additions & 2 deletions lib/core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,8 +841,6 @@ pub(crate) struct ReceiveSwap {
pub(crate) claim_fees_sat: u64,
/// Persisted as soon as a claim tx is broadcast
pub(crate) claim_tx_id: Option<String>,
/// Persisted only when the lockup tx is broadcast
pub(crate) lockup_tx_id: Option<String>,
/// The address reserved for a magic routing hint payment
pub(crate) mrh_address: String,
/// The script pubkey for a magic routing hint payment
Expand Down
35 changes: 5 additions & 30 deletions lib/core/src/persist/receive.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::HashMap;

use anyhow::Result;
use boltz_client::swaps::boltz::CreateReverseResponse;
use rusqlite::{named_params, params, Connection, Row};
Expand Down Expand Up @@ -93,7 +91,6 @@ impl Persister {
rs.receiver_amount_sat,
rs.claim_fees_sat,
rs.claim_tx_id,
rs.lockup_tx_id,
rs.mrh_address,
rs.mrh_script_pubkey,
rs.mrh_tx_id,
Expand Down Expand Up @@ -138,12 +135,11 @@ impl Persister {
receiver_amount_sat: row.get(8)?,
claim_fees_sat: row.get(9)?,
claim_tx_id: row.get(10)?,
lockup_tx_id: row.get(11)?,
mrh_address: row.get(12)?,
mrh_script_pubkey: row.get(13)?,
mrh_tx_id: row.get(14)?,
created_at: row.get(15)?,
state: row.get(16)?,
mrh_address: row.get(11)?,
mrh_script_pubkey: row.get(12)?,
mrh_tx_id: row.get(13)?,
created_at: row.get(14)?,
state: row.get(15)?,
})
}

Expand Down Expand Up @@ -176,27 +172,6 @@ impl Persister {
self.list_receive_swaps_where(&con, where_clause)
}

/// Ongoing Receive Swaps with no claim or lockup transactions, indexed by mrh_script_pubkey
pub(crate) fn list_ongoing_receive_swaps_by_mrh_script_pubkey(
&self,
) -> Result<HashMap<String, ReceiveSwap>> {
let res = self
.list_ongoing_receive_swaps()?
.iter()
.filter_map(|swap| {
match (
swap.lockup_tx_id.clone(),
swap.claim_tx_id.clone(),
swap.mrh_script_pubkey.is_empty(),
) {
(None, None, false) => Some((swap.mrh_script_pubkey.clone(), swap.clone())),
_ => None,
}
})
.collect();
Ok(res)
}

// Only set the Receive Swap claim_tx_id if not set, otherwise return an error
pub(crate) fn set_receive_swap_claim_tx_id(
&self,
Expand Down
31 changes: 4 additions & 27 deletions lib/core/src/receive_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use boltz_client::swaps::boltz::{self, SwapUpdateTxDetails};
use boltz_client::{Serialize, ToHex};
use log::{debug, error, info, warn};
use lwk_wollet::hashes::hex::DisplayHex;
use lwk_wollet::WalletTx;
use tokio::sync::{broadcast, Mutex};

use crate::chain::liquid::LiquidChainService;
Expand Down Expand Up @@ -231,32 +230,6 @@ impl ReceiveSwapHandler {
}
}

/// Update the swap status according to the MRH tx confirmation state
pub(crate) async fn update_swap_from_mrh_tx(
&self,
swap: &ReceiveSwap,
tx: &WalletTx,
) -> Result<(), PaymentError> {
let tx_id = tx.txid.to_string();
let is_tx_confirmed = tx.height.is_some();
let amount_sat = tx.balance.values().sum::<i64>();
let to_state = match is_tx_confirmed {
true => Complete,
false => Pending,
};
self.update_swap_info(
&swap.id,
to_state,
None,
None,
Some(&tx_id),
Some(amount_sat.unsigned_abs()),
)
.await?;
// Remove the used MRH address from the reserved addresses
self.persister.delete_reserved_address(&swap.mrh_address)
}

/// Transitions a Receive swap to a new state
pub(crate) async fn update_swap_info(
&self,
Expand Down Expand Up @@ -295,6 +268,10 @@ impl ReceiveSwapHandler {
mrh_amount_sat,
)?;

if mrh_tx_id.is_some() {
self.persister.delete_reserved_address(&swap.mrh_address)?;
}

if let Some(payment_id) = payment_id {
let _ = self.subscription_notifier.send(payment_id);
}
Expand Down
142 changes: 106 additions & 36 deletions lib/core/src/restore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,27 @@ impl PartialSwapState for RecoveredOnchainDataSend {
pub(crate) struct RecoveredOnchainDataReceive {
pub(crate) lockup_tx_id: Option<HistoryTxId>,
pub(crate) claim_tx_id: Option<HistoryTxId>,
pub(crate) mrh_tx_id: Option<HistoryTxId>,
pub(crate) mrh_amount_sat: Option<u64>,
}
impl PartialSwapState for RecoveredOnchainDataReceive {
fn derive_partial_state(&self) -> Option<PaymentState> {
match (&self.lockup_tx_id, &self.claim_tx_id) {
(Some(_), Some(claim_tx_id)) => match claim_tx_id.confirmed() {
true => Some(PaymentState::Complete),
false => Some(PaymentState::Pending),
match &self.lockup_tx_id {
Some(_) => match &self.claim_tx_id {
Some(claim_tx_id) => match claim_tx_id.confirmed() {
true => Some(PaymentState::Complete),
false => Some(PaymentState::Pending),
},
None => Some(PaymentState::Pending),
},
None => match &self.mrh_tx_id {
Some(mrh_tx_id) => match mrh_tx_id.confirmed() {
true => Some(PaymentState::Complete),
false => Some(PaymentState::Pending),
},
// We cannot derive the state here, so return None
None => None,
},
(Some(_), None) => Some(PaymentState::Pending),
// TODO How to distinguish between Failed and Created (if in both cases, no lockup or claim tx present)
// See https://docs.boltz.exchange/v/api/lifecycle#reverse-submarine-swaps
_ => None,
}
}
}
Expand Down Expand Up @@ -281,13 +290,23 @@ impl LiquidSdk {
for (swap_id, history) in receive_histories_by_swap_id {
debug!("[Recover Receive] Checking swap {swap_id}");

let (lockup_tx_id, claim_tx_id) = match history.len() {
let mrh_tx_id = history
.lbtc_mrh_script_history
.iter()
.find(|&tx| tx_map.incoming_tx_map.contains_key::<Txid>(&tx.txid))
.cloned();
let mrh_amount_sat = mrh_tx_id
.clone()
.and_then(|h| tx_map.incoming_tx_map.get(&h.txid))
.map(|tx| tx.balance.values().sum::<i64>().unsigned_abs());

let (lockup_tx_id, claim_tx_id) = match history.lbtc_claim_script_history.len() {
// Only lockup tx available
1 => (Some(history[0].clone()), None),
1 => (Some(history.lbtc_claim_script_history[0].clone()), None),

2 => {
let first = history[0].clone();
let second = history[1].clone();
let first = history.lbtc_claim_script_history[0].clone();
let second = history.lbtc_claim_script_history[1].clone();

if tx_map.incoming_tx_map.contains_key::<Txid>(&first.txid) {
// If the first tx is a known incoming tx, it's the claim tx and the second is the lockup
Expand Down Expand Up @@ -330,6 +349,8 @@ impl LiquidSdk {
RecoveredOnchainDataReceive {
lockup_tx_id,
claim_tx_id,
mrh_tx_id,
mrh_amount_sat,
},
);
}
Expand Down Expand Up @@ -525,9 +546,10 @@ impl LiquidSdk {
// TODO Remove once real-time sync is integrated
pub(crate) mod immutable {
use std::collections::HashMap;
use std::str::FromStr;

use anyhow::{anyhow, ensure, Result};
use boltz_client::{BtcSwapScript, LBtcSwapScript};
use boltz_client::{BtcSwapScript, ElementsAddress, LBtcSwapScript};
use log::{debug, error};
use lwk_wollet::elements::Txid;
use lwk_wollet::History;
Expand All @@ -539,7 +561,6 @@ pub(crate) mod immutable {
type LBtcScript = lwk_wollet::elements::Script;

pub(crate) type SendSwapHistory = Vec<HistoryTxId>;
pub(crate) type ReceiveSwapHistory = Vec<HistoryTxId>;

#[derive(Clone)]
pub(crate) struct HistoryTxId {
Expand Down Expand Up @@ -572,15 +593,21 @@ pub(crate) mod immutable {
#[derive(Clone)]
pub(crate) struct SendSwapImmutableData {
pub(crate) swap_id: String,
pub(crate) swap_script: LBtcSwapScript,
pub(crate) script: LBtcScript,
pub(crate) lockup_swap_script: LBtcSwapScript,
pub(crate) lockup_script: LBtcScript,
}

#[derive(Clone)]
pub(crate) struct ReceiveSwapImmutableData {
pub(crate) swap_id: String,
pub(crate) swap_script: LBtcSwapScript,
pub(crate) script: LBtcScript,
pub(crate) claim_swap_script: LBtcSwapScript,
pub(crate) claim_script: LBtcScript,
pub(crate) mrh_script: LBtcScript,
}

pub(crate) struct ReceiveSwapHistory {
pub(crate) lbtc_claim_script_history: Vec<HistoryTxId>,
pub(crate) lbtc_mrh_script_history: Vec<HistoryTxId>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -616,7 +643,7 @@ pub(crate) mod immutable {
/// Swap immutable data
pub(crate) struct SwapsList {
pub(crate) send_swap_immutable_data_by_swap_id: HashMap<String, SendSwapImmutableData>,
pub(crate) receive_swap_immutable_data_by_swap_id_:
pub(crate) receive_swap_immutable_data_by_swap_id:
HashMap<String, ReceiveSwapImmutableData>,
pub(crate) send_chain_swap_immutable_data_by_swap_id:
HashMap<String, SendChainSwapImmutableData>,
Expand All @@ -640,8 +667,8 @@ pub(crate) mod immutable {
swap.id.clone(),
SendSwapImmutableData {
swap_id: swap.id.clone(),
swap_script: swap_script.clone(),
script: address.script_pubkey(),
lockup_swap_script: swap_script.clone(),
lockup_script: address.script_pubkey(),
},
)),
None => {
Expand Down Expand Up @@ -670,14 +697,18 @@ pub(crate) mod immutable {
error!("Failed to get swap script for Receive Swap {swap_id}: {e}")
})
.ok()?;
let mrh_address = ElementsAddress::from_str(&swap.mrh_address)
.map_err(|e| error!("Not a valid ElementsAddress: {e:?}"))
.ok()?;

match &swap_script.funding_addrs {
Some(address) => Some((
swap.id.clone(),
ReceiveSwapImmutableData {
swap_id: swap.id.clone(),
swap_script: swap_script.clone(),
script: address.script_pubkey(),
claim_swap_script: swap_script.clone(),
claim_script: address.script_pubkey(),
mrh_script: mrh_address.script_pubkey(),
},
)),
None => {
Expand Down Expand Up @@ -766,7 +797,7 @@ pub(crate) mod immutable {

Ok(SwapsList {
send_swap_immutable_data_by_swap_id,
receive_swap_immutable_data_by_swap_id_: receive_swap_immutable_data_by_swap_id,
receive_swap_immutable_data_by_swap_id,
send_chain_swap_immutable_data_by_swap_id,
receive_chain_swap_immutable_data_by_swap_id,
})
Expand All @@ -776,7 +807,7 @@ pub(crate) mod immutable {
self.send_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| (imm.script.clone(), imm))
.map(|imm| (imm.lockup_script.clone(), imm))
.collect()
}

Expand All @@ -797,26 +828,58 @@ pub(crate) mod immutable {
data
}

fn receive_swaps_by_script(&self) -> HashMap<LBtcScript, ReceiveSwapImmutableData> {
self.receive_swap_immutable_data_by_swap_id_
fn receive_swaps_by_claim_script(&self) -> HashMap<LBtcScript, ReceiveSwapImmutableData> {
self.receive_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| (imm.script.clone(), imm))
.map(|imm| (imm.claim_script.clone(), imm))
.collect()
}

fn receive_swaps_by_mrh_script(&self) -> HashMap<LBtcScript, ReceiveSwapImmutableData> {
self.receive_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| (imm.mrh_script.clone(), imm))
.collect()
}

fn receive_histories_by_swap_id(
&self,
lbtc_script_to_history_map: &HashMap<LBtcScript, Vec<HistoryTxId>>,
) -> HashMap<String, ReceiveSwapHistory> {
let receive_swaps_by_script = self.receive_swaps_by_script();
let receive_swaps_by_claim_script = self.receive_swaps_by_claim_script();
let receive_swaps_by_mrh_script = self.receive_swaps_by_mrh_script();

let mut data: HashMap<String, ReceiveSwapHistory> = HashMap::new();
lbtc_script_to_history_map
.iter()
.for_each(|(lbtc_script, lbtc_script_history)| {
if let Some(imm) = receive_swaps_by_script.get(lbtc_script) {
data.insert(imm.swap_id.clone(), lbtc_script_history.clone());
if let Some(imm) = receive_swaps_by_claim_script.get(lbtc_script) {
let mrh_script_history = lbtc_script_to_history_map
.get(&imm.mrh_script)
.cloned()
.unwrap_or_default();
data.insert(
imm.swap_id.clone(),
ReceiveSwapHistory {
lbtc_claim_script_history: lbtc_script_history.clone(),
lbtc_mrh_script_history: mrh_script_history,
},
);
}
if let Some(imm) = receive_swaps_by_mrh_script.get(lbtc_script) {
let claim_script_history = lbtc_script_to_history_map
.get(&imm.claim_script)
.cloned()
.unwrap_or_default();
data.insert(
imm.swap_id.clone(),
ReceiveSwapHistory {
lbtc_claim_script_history: claim_script_history,
lbtc_mrh_script_history: lbtc_script_history.clone(),
},
);
}
});
data
Expand Down Expand Up @@ -918,13 +981,19 @@ pub(crate) mod immutable {
.send_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| imm.script)
.map(|imm| imm.lockup_script)
.collect();
let receive_swap_lbtc_claim_scripts: Vec<LBtcScript> = self
.receive_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| imm.claim_script)
.collect();
let receive_swap_scripts: Vec<LBtcScript> = self
.receive_swap_immutable_data_by_swap_id_
let receive_swap_lbtc_mrh_scripts: Vec<LBtcScript> = self
.receive_swap_immutable_data_by_swap_id
.clone()
.into_values()
.map(|imm| imm.script)
.map(|imm| imm.mrh_script)
.collect();
let send_chain_swap_lbtc_lockup_scripts: Vec<LBtcScript> = self
.send_chain_swap_immutable_data_by_swap_id
Expand All @@ -940,7 +1009,8 @@ pub(crate) mod immutable {
.collect();

let mut swap_scripts = send_swap_scripts.clone();
swap_scripts.extend(receive_swap_scripts.clone());
swap_scripts.extend(receive_swap_lbtc_claim_scripts.clone());
swap_scripts.extend(receive_swap_lbtc_mrh_scripts.clone());
swap_scripts.extend(send_chain_swap_lbtc_lockup_scripts.clone());
swap_scripts.extend(receive_chain_swap_lbtc_claim_scripts.clone());
swap_scripts
Expand Down
Loading

0 comments on commit 807bc5b

Please sign in to comment.