Skip to content

Commit

Permalink
Single chain source
Browse files Browse the repository at this point in the history
  • Loading branch information
dangeross committed Dec 5, 2024
1 parent ef157fd commit 8b3cf1e
Show file tree
Hide file tree
Showing 16 changed files with 1,210 additions and 921 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_signer_slip77_master_b
);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_signer_hmac_sha256(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_signer_ecies_encrypt(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_signer_ecies_decrypt(void

);
uint32_t ffi_breez_sdk_liquid_bindings_uniffi_contract_version(void

Expand Down
597 changes: 281 additions & 316 deletions lib/core/src/chain_swap.rs

Large diffs are not rendered by default.

33 changes: 24 additions & 9 deletions lib/core/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use anyhow::{anyhow, Result};

use async_trait::async_trait;
use boltz_client::{
bitcoin::ScriptBuf,
network::Chain,
Expand Down Expand Up @@ -565,6 +565,13 @@ pub enum GetPaymentRequest {
Lightning { payment_hash: String },
}

/// Trait that can be used to react to new blocks from Bitcoin and Liquid chains
#[async_trait]
pub(crate) trait BlockListener: Send + Sync {
async fn on_bitcoin_block(&self, height: u32);
async fn on_liquid_block(&self, height: u32);
}

// A swap enum variant
#[derive(Clone, Debug)]
pub(crate) enum Swap {
Expand Down Expand Up @@ -629,7 +636,7 @@ impl FromSql for Direction {
/// A chain swap
///
/// See <https://docs.boltz.exchange/v/api/lifecycle#chain-swaps>
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ChainSwap {
pub(crate) id: String,
pub(crate) direction: Direction,
Expand Down Expand Up @@ -758,8 +765,19 @@ impl ChainSwap {
}
}

#[derive(Clone, Debug, Default)]
pub(crate) struct ChainSwapUpdate {
pub(crate) swap_id: String,
pub(crate) to_state: PaymentState,
pub(crate) server_lockup_tx_id: Option<String>,
pub(crate) user_lockup_tx_id: Option<String>,
pub(crate) claim_address: Option<String>,
pub(crate) claim_tx_id: Option<String>,
pub(crate) refund_tx_id: Option<String>,
}

/// A submarine swap, used for Send
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct SendSwap {
pub(crate) id: String,
/// Bolt11 or Bolt12 invoice. This is determined by whether `bolt12_offer` is set or not.
Expand Down Expand Up @@ -844,7 +862,7 @@ impl SendSwap {
}

/// A reverse swap, used for Receive
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ReceiveSwap {
pub(crate) id: String,
pub(crate) preimage: String,
Expand All @@ -860,12 +878,8 @@ 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
pub(crate) mrh_script_pubkey: String,
/// Persisted only if a transaction is sent to the `mrh_address`
pub(crate) mrh_tx_id: Option<String>,
/// Until the lockup tx is seen in the mempool, it contains the swap creation time.
Expand Down Expand Up @@ -953,8 +967,9 @@ pub struct RefundableSwap {
}

/// The payment state of an individual payment.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Hash)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Hash)]
pub enum PaymentState {
#[default]
Created = 0,

/// ## Receive Swaps
Expand Down
88 changes: 27 additions & 61 deletions lib/core/src/persist/chain.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::{ChainSwapDetails, CreateChainResponse};
use rusqlite::{named_params, params, Connection, Row, TransactionBehavior};
Expand Down Expand Up @@ -190,63 +188,29 @@ impl Persister {

pub(crate) fn list_chain_swaps_by_state(
&self,
con: &Connection,
states: Vec<PaymentState>,
) -> Result<Vec<ChainSwap>> {
let con = self.get_connection()?;
let where_clause = vec![get_where_clause_state_in(&states)];
self.list_chain_swaps_where(con, where_clause)
self.list_chain_swaps_where(&con, where_clause)
}

pub(crate) fn list_ongoing_chain_swaps(&self, con: &Connection) -> Result<Vec<ChainSwap>> {
self.list_chain_swaps_by_state(con, vec![PaymentState::Created, PaymentState::Pending])
pub(crate) fn list_ongoing_chain_swaps(&self) -> Result<Vec<ChainSwap>> {
let con = self.get_connection()?;
let where_clause = vec![get_where_clause_state_in(&[
PaymentState::Created,
PaymentState::Pending,
])];

self.list_chain_swaps_where(&con, where_clause)
}

pub(crate) fn list_pending_chain_swaps(&self) -> Result<Vec<ChainSwap>> {
let con: Connection = self.get_connection()?;
self.list_chain_swaps_by_state(
&con,
vec![PaymentState::Pending, PaymentState::RefundPending],
)
self.list_chain_swaps_by_state(vec![PaymentState::Pending, PaymentState::RefundPending])
}

pub(crate) fn list_refundable_chain_swaps(&self) -> Result<Vec<ChainSwap>> {
let con: Connection = self.get_connection()?;
self.list_chain_swaps_by_state(&con, vec![PaymentState::Refundable])
}

/// Pending Chain swaps, indexed by refund tx id
pub(crate) fn list_pending_chain_swaps_by_refund_tx_id(
&self,
) -> Result<HashMap<String, ChainSwap>> {
let res: HashMap<String, ChainSwap> = self
.list_pending_chain_swaps()?
.iter()
.filter_map(|pending_chain_swap| {
pending_chain_swap
.refund_tx_id
.as_ref()
.map(|refund_tx_id| (refund_tx_id.clone(), pending_chain_swap.clone()))
})
.collect();
Ok(res)
}

/// This only returns the swaps that have a claim tx, skipping the pending ones that are being refunded.
pub(crate) fn list_pending_chain_swaps_by_claim_tx_id(
&self,
) -> Result<HashMap<String, ChainSwap>> {
let con: Connection = self.get_connection()?;
let res: HashMap<String, ChainSwap> = self
.list_chain_swaps_by_state(&con, vec![PaymentState::Pending])?
.iter()
.filter_map(|pending_chain_swap| {
pending_chain_swap
.claim_tx_id
.as_ref()
.map(|claim_tx_id| (claim_tx_id.clone(), pending_chain_swap.clone()))
})
.collect();
Ok(res)
self.list_chain_swaps_by_state(vec![PaymentState::Refundable])
}

pub(crate) fn update_chain_swap_accept_zero_conf(
Expand Down Expand Up @@ -332,14 +296,9 @@ impl Persister {

pub(crate) fn try_handle_chain_swap_update(
&self,
swap_id: &str,
to_state: PaymentState,
server_lockup_tx_id: Option<&str>,
user_lockup_tx_id: Option<&str>,
claim_tx_id: Option<&str>,
refund_tx_id: Option<&str>,
swap_update: &ChainSwapUpdate,
) -> Result<(), PaymentError> {
// Do not overwrite server_lockup_tx_id, user_lockup_tx_id, claim_tx_id, refund_tx_id
// Do not overwrite server_lockup_tx_id, user_lockup_tx_id, claim_address, claim_tx_id, refund_tx_id
let mut con = self.get_connection()?;
let tx = con.transaction_with_behavior(TransactionBehavior::Immediate)?;

Expand All @@ -358,6 +317,12 @@ impl Persister {
ELSE user_lockup_tx_id
END,
claim_address =
CASE
WHEN claim_address IS NULL THEN :claim_address
ELSE claim_address
END,
claim_tx_id =
CASE
WHEN claim_tx_id IS NULL THEN :claim_tx_id
Expand All @@ -374,12 +339,13 @@ impl Persister {
WHERE
id = :id",
named_params! {
":id": swap_id,
":server_lockup_tx_id": server_lockup_tx_id,
":user_lockup_tx_id": user_lockup_tx_id,
":claim_tx_id": claim_tx_id,
":refund_tx_id": refund_tx_id,
":state": to_state,
":id": swap_update.swap_id,
":server_lockup_tx_id": swap_update.server_lockup_tx_id,
":user_lockup_tx_id": swap_update.user_lockup_tx_id,
":claim_address": swap_update.claim_address,
":claim_tx_id": swap_update.claim_tx_id,
":refund_tx_id": swap_update.refund_tx_id,
":state": swap_update.to_state,
},
)?;

Expand Down
1 change: 1 addition & 0 deletions lib/core/src/persist/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,6 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
schema_version TEXT NOT NULL,
data BLOB NOT NULL
) STRICT;",
"ALTER TABLE receive_swaps DROP COLUMN mrh_script_pubkey;",
]
}
62 changes: 57 additions & 5 deletions lib/core/src/persist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ pub(crate) mod receive;
pub(crate) mod send;
pub(crate) mod sync;

use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::{fs::create_dir_all, path::PathBuf, str::FromStr};

use crate::error::PaymentError;
use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription};
use crate::model::*;
use crate::{get_invoice_description, utils};
use anyhow::{anyhow, Result};
use lwk_wollet::WalletTx;
use migrations::current_migrations;
use rusqlite::{params, params_from_iter, Connection, OptionalExtension, Row, ToSql};
use rusqlite_migration::{Migrations, M};
use sdk_common::bitcoin::hashes::hex::ToHex;
use tokio::sync::mpsc::Sender;

const DEFAULT_DB_FILENAME: &str = "storage.sql";
Expand Down Expand Up @@ -93,6 +95,35 @@ impl Persister {
}
}

pub(crate) fn insert_or_update_payment_with_wallet_tx(
&self,
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 maybe_script_pubkey = tx
.outputs
.iter()
.find(|output| output.is_some())
.and_then(|output| output.clone().map(|o| o.script_pubkey.to_hex()));
self.insert_or_update_payment(
PaymentTxData {
tx_id: tx_id.clone(),
timestamp: tx.timestamp,
amount_sat: amount_sat.unsigned_abs(),
fees_sat: tx.fee,
payment_type: match amount_sat >= 0 {
true => PaymentType::Receive,
false => PaymentType::Send,
},
is_confirmed: is_tx_confirmed,
},
maybe_script_pubkey,
None,
)
}

pub(crate) fn insert_or_update_payment(
&self,
ptx: PaymentTxData,
Expand Down Expand Up @@ -138,19 +169,18 @@ impl Persister {
}

pub(crate) fn list_ongoing_swaps(&self) -> Result<Vec<Swap>> {
let con = self.get_connection()?;
let ongoing_send_swaps: Vec<Swap> = self
.list_ongoing_send_swaps(&con)?
.list_ongoing_send_swaps()?
.into_iter()
.map(Swap::Send)
.collect();
let ongoing_receive_swaps: Vec<Swap> = self
.list_ongoing_receive_swaps(&con)?
.list_ongoing_receive_swaps()?
.into_iter()
.map(Swap::Receive)
.collect();
let ongoing_chain_swaps: Vec<Swap> = self
.list_ongoing_chain_swaps(&con)?
.list_ongoing_chain_swaps()?
.into_iter()
.map(Swap::Chain)
.collect();
Expand Down Expand Up @@ -478,6 +508,28 @@ impl Persister {
.collect();
Ok(payments)
}

pub fn get_payments_by_tx_id(
&self,
req: &ListPaymentsRequest,
) -> Result<HashMap<String, Payment>> {
let res: HashMap<String, Payment> = self
.get_payments(req)?
.into_iter()
.flat_map(|payment| {
// Index payments by both tx_id (lockup/claim) and refund_tx_id
let mut res = vec![];
if let Some(tx_id) = payment.tx_id.clone() {
res.push((tx_id, payment.clone()));
}
if let Some(refund_tx_id) = payment.get_refund_tx_id() {
res.push((refund_tx_id, payment));
}
res
})
.collect();
Ok(res)
}
}

fn filter_to_where_clause(req: &ListPaymentsRequest) -> (String, Vec<Box<dyn ToSql + '_>>) {
Expand Down
Loading

0 comments on commit 8b3cf1e

Please sign in to comment.