mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-01-28 10:24:28 +01:00
Restore: Associate swap tx IDs from onchain data (#399)
* Add extend_incomplete_failed_send_swaps() on first sync * Find lockup txs * Send Swaps: find refund txs * Simplify recover_send_swap_tx_ids, add recover_receive_swap_tx_ids * recover_receive_swap_tx_ids: batch tx lookups * Move onchain-restore methods to own module * Store restored data in own struct * Fix CI: bump pubspec.lock dependencies * LiquidChainService: add get_scripts_history_electrum() * restore_onchain: rely on batch call to fetch histories of all known swaps * Rename get_scripts_history_electrum * Rename restore_onchain.rs, flatten onchain inner module * Rename ImmutableDb to SwapsList * Simplify logic in restore.rs * restore.rs: Add chain swap support, simplify logic * restore.rs: add logging when script retrieval fails * restore.rs: remove unused field create_resp * restore.rs: rename SwapCompositeData to SwapHistory * restore.rs: allow unused fields in simulated immutable data * restore.rs: cargo fmt * Cargo fmt * Fix failing test * When fetching script history, also fetch if tx is confirmed or not * Recover send swaps: fetch claim tx IDs * Recover onchain data: persist reconstructed swaps * Simplify recover_from_onchain: store swap txs per swap ID * Receive swaps: do not treat lockup/claim txs as pair * Clarify meaning of partial swap state * Cargo clippy * Receive Chain Swap: distinguish BTC lockup from claim/refund tx * Send Chain Swap: distinguish BTC lockup/claim by vout, not by history order * get_partial_state: default to Created when state is unclear * Receive Chain Swaps: differentiate BTC refund from BTC claim txs * Send Swaps: clarify reason for defaulting to TimedOut on no lockup * Chain swaps: add docs for meaning of server, user txs * Recover Receive swaps: cover the case when only the lockup is available * HistoryTxId: store confirmation block height * Receive swaps: differentiate claim tx from swapper refund tx * recover_from_onchain: extract immutable DB (swaps list) as arg * Rename get_partial_state to derive_partial_state * Restore: remove validation steps * Restore chain swaps: treat as Complete only when claim is confirmed * Fix clippy warnings * Remove restore call from sync call
This commit is contained in:
@@ -33,6 +33,9 @@ pub trait BitcoinChainService: Send + Sync {
|
||||
/// Get the transactions involved for a script
|
||||
fn get_script_history(&self, script: &Script) -> Result<Vec<History>>;
|
||||
|
||||
/// Get the transactions involved in a list of scripts.
|
||||
fn get_scripts_history(&self, scripts: &[&Script]) -> Result<Vec<Vec<History>>>;
|
||||
|
||||
/// Get the transactions involved for a script
|
||||
async fn get_script_history_with_retry(
|
||||
&self,
|
||||
@@ -132,6 +135,15 @@ impl BitcoinChainService for HybridBitcoinChainService {
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn get_scripts_history(&self, scripts: &[&Script]) -> Result<Vec<Vec<History>>> {
|
||||
Ok(self
|
||||
.client
|
||||
.batch_script_get_history(scripts)?
|
||||
.into_iter()
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn get_script_history_with_retry(
|
||||
&self,
|
||||
script: &Script,
|
||||
|
||||
@@ -31,9 +31,16 @@ pub trait LiquidChainService: Send + Sync {
|
||||
/// Get a list of transactions
|
||||
async fn get_transactions(&self, txids: &[Txid]) -> Result<Vec<Transaction>>;
|
||||
|
||||
/// Get the transactions involved in a list of scripts including lowball
|
||||
/// Get the transactions involved in a script, including lowball transactions.
|
||||
///
|
||||
/// On mainnet, the data is fetched from Esplora. On testnet, it's fetched from Electrum.
|
||||
async fn get_script_history(&self, scripts: &Script) -> Result<Vec<History>>;
|
||||
|
||||
/// Get the transactions involved in a list of scripts, including lowball transactions.
|
||||
///
|
||||
/// The data is fetched in a single call from the Electrum endpoint.
|
||||
async fn get_scripts_history(&self, scripts: &[&Script]) -> Result<Vec<Vec<History>>>;
|
||||
|
||||
/// Get the transactions involved in a list of scripts including lowball
|
||||
async fn get_script_history_with_retry(
|
||||
&self,
|
||||
@@ -131,6 +138,12 @@ impl LiquidChainService for HybridLiquidChainService {
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_scripts_history(&self, scripts: &[&Script]) -> Result<Vec<Vec<History>>> {
|
||||
self.electrum_client
|
||||
.get_scripts_history(scripts)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_script_history_with_retry(
|
||||
&self,
|
||||
script: &Script,
|
||||
|
||||
@@ -175,6 +175,8 @@ pub mod logger;
|
||||
pub mod model;
|
||||
pub mod persist;
|
||||
pub(crate) mod receive_swap;
|
||||
#[allow(dead_code)]
|
||||
mod restore;
|
||||
pub mod sdk;
|
||||
pub(crate) mod send_swap;
|
||||
pub(crate) mod swapper;
|
||||
|
||||
@@ -519,6 +519,8 @@ impl FromSql for Direction {
|
||||
}
|
||||
|
||||
/// A chain swap
|
||||
///
|
||||
/// See <https://docs.boltz.exchange/v/api/lifecycle#chain-swaps>
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ChainSwap {
|
||||
pub(crate) id: String,
|
||||
|
||||
@@ -119,11 +119,16 @@ impl Persister {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn list_receive_swaps(
|
||||
pub(crate) fn list_receive_swaps(&self) -> Result<Vec<ReceiveSwap>> {
|
||||
let con: Connection = self.get_connection()?;
|
||||
self.list_receive_swaps_where(&con, vec![])
|
||||
}
|
||||
|
||||
pub(crate) fn list_receive_swaps_where(
|
||||
&self,
|
||||
con: &Connection,
|
||||
where_clauses: Vec<String>,
|
||||
) -> rusqlite::Result<Vec<ReceiveSwap>> {
|
||||
) -> Result<Vec<ReceiveSwap>> {
|
||||
let query = Self::list_receive_swaps_query(where_clauses);
|
||||
let ongoing_receive = con
|
||||
.prepare(&query)?
|
||||
@@ -133,16 +138,13 @@ impl Persister {
|
||||
Ok(ongoing_receive)
|
||||
}
|
||||
|
||||
pub(crate) fn list_ongoing_receive_swaps(
|
||||
&self,
|
||||
con: &Connection,
|
||||
) -> rusqlite::Result<Vec<ReceiveSwap>> {
|
||||
pub(crate) fn list_ongoing_receive_swaps(&self, con: &Connection) -> Result<Vec<ReceiveSwap>> {
|
||||
let where_clause = vec![get_where_clause_state_in(&[
|
||||
PaymentState::Created,
|
||||
PaymentState::Pending,
|
||||
])];
|
||||
|
||||
self.list_receive_swaps(con, where_clause)
|
||||
self.list_receive_swaps_where(con, where_clause)
|
||||
}
|
||||
|
||||
pub(crate) fn list_pending_receive_swaps(&self) -> Result<Vec<ReceiveSwap>> {
|
||||
@@ -288,7 +290,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let con = storage.get_connection()?;
|
||||
let swaps = storage.list_receive_swaps(&con, vec![])?;
|
||||
let swaps = storage.list_receive_swaps_where(&con, vec![])?;
|
||||
assert_eq!(swaps.len(), range.len());
|
||||
|
||||
// List ongoing receive swaps
|
||||
|
||||
@@ -137,11 +137,16 @@ impl Persister {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn list_send_swaps(
|
||||
pub(crate) fn list_send_swaps(&self) -> Result<Vec<SendSwap>> {
|
||||
let con: Connection = self.get_connection()?;
|
||||
self.list_send_swaps_where(&con, vec![])
|
||||
}
|
||||
|
||||
pub(crate) fn list_send_swaps_where(
|
||||
&self,
|
||||
con: &Connection,
|
||||
where_clauses: Vec<String>,
|
||||
) -> rusqlite::Result<Vec<SendSwap>> {
|
||||
) -> Result<Vec<SendSwap>> {
|
||||
let query = Self::list_send_swaps_query(where_clauses);
|
||||
let ongoing_send = con
|
||||
.prepare(&query)?
|
||||
@@ -151,16 +156,13 @@ impl Persister {
|
||||
Ok(ongoing_send)
|
||||
}
|
||||
|
||||
pub(crate) fn list_ongoing_send_swaps(
|
||||
&self,
|
||||
con: &Connection,
|
||||
) -> rusqlite::Result<Vec<SendSwap>> {
|
||||
pub(crate) fn list_ongoing_send_swaps(&self, con: &Connection) -> Result<Vec<SendSwap>> {
|
||||
let where_clause = vec![get_where_clause_state_in(&[
|
||||
PaymentState::Created,
|
||||
PaymentState::Pending,
|
||||
])];
|
||||
|
||||
self.list_send_swaps(con, where_clause)
|
||||
self.list_send_swaps_where(con, where_clause)
|
||||
}
|
||||
|
||||
pub(crate) fn list_pending_send_swaps(&self) -> Result<Vec<SendSwap>> {
|
||||
@@ -319,7 +321,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let con = storage.get_connection()?;
|
||||
let swaps = storage.list_send_swaps(&con, vec![])?;
|
||||
let swaps = storage.list_send_swaps_where(&con, vec![])?;
|
||||
assert_eq!(swaps.len(), range.len());
|
||||
|
||||
// List ongoing send swaps
|
||||
|
||||
1056
lib/core/src/restore.rs
Normal file
1056
lib/core/src/restore.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,10 @@ use futures_util::stream::select_all;
|
||||
use futures_util::StreamExt;
|
||||
use log::{debug, error, info};
|
||||
use lwk_wollet::bitcoin::hex::DisplayHex;
|
||||
use lwk_wollet::elements::AssetId;
|
||||
use lwk_wollet::elements::{AssetId, Txid};
|
||||
use lwk_wollet::hashes::{sha256, Hash};
|
||||
use lwk_wollet::secp256k1::ThirtyTwoByteHash;
|
||||
use lwk_wollet::{elements, ElementsNetwork};
|
||||
use lwk_wollet::{elements, ElementsNetwork, WalletTx};
|
||||
use sdk_common::bitcoin::secp256k1::Secp256k1;
|
||||
use sdk_common::bitcoin::util::bip32::ChildNumber;
|
||||
use sdk_common::liquid::LiquidAddressData;
|
||||
@@ -1871,7 +1871,15 @@ impl LiquidSdk {
|
||||
let pending_chain_swaps_by_refund_tx_id =
|
||||
self.persister.list_pending_chain_swaps_by_refund_tx_id()?;
|
||||
|
||||
for tx in self.onchain_wallet.transactions().await? {
|
||||
let tx_map: HashMap<Txid, WalletTx> = self
|
||||
.onchain_wallet
|
||||
.transactions()
|
||||
.await?
|
||||
.iter()
|
||||
.map(|tx| (tx.txid, tx.clone()))
|
||||
.collect();
|
||||
|
||||
for tx in tx_map.values() {
|
||||
let tx_id = tx.txid.to_string();
|
||||
let is_tx_confirmed = tx.height.is_some();
|
||||
let amount_sat = tx.balance.values().sum::<i64>();
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use boltz_client::elements::Script;
|
||||
use lwk_wollet::elements::{BlockHash, Txid};
|
||||
use lwk_wollet::{bitcoin::consensus::deserialize, elements::hex::FromHex};
|
||||
use lwk_wollet::{bitcoin::consensus::deserialize, elements::hex::FromHex, History};
|
||||
|
||||
use crate::{
|
||||
chain::{bitcoin::BitcoinChainService, liquid::LiquidChainService},
|
||||
@@ -82,6 +83,10 @@ impl LiquidChainService for MockLiquidChainService {
|
||||
Ok(self.history.clone().into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
async fn get_scripts_history(&self, _scripts: &[&Script]) -> Result<Vec<Vec<History>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn verify_tx(
|
||||
&self,
|
||||
_address: &boltz_client::ElementsAddress,
|
||||
@@ -143,6 +148,13 @@ impl BitcoinChainService for MockBitcoinChainService {
|
||||
Ok(self.history.clone().into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
fn get_scripts_history(
|
||||
&self,
|
||||
_scripts: &[&boltz_client::bitcoin::Script],
|
||||
) -> Result<Vec<Vec<History>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn script_get_balance(
|
||||
&self,
|
||||
_script: &boltz_client::bitcoin::Script,
|
||||
|
||||
Reference in New Issue
Block a user