mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-24 01:14:22 +01:00
Fix cooperative refund and improve keypair generation (#223)
* feat: add random keypair generation * Encapsulate decode_keypair in SendSwap::get_refund_keypair() * Add refund_tx_id and refund_tx_amount_sat to Payment * fix: remove blocking on refund * fix: change `refund_private_key` order * fix: rebasing * fix: set `next_unused_address` as refund output * Handle refunds in `get_info`, `list_payments` (#226) * Exclude refund txs from payment list * Adjust balance calculation to account for refunds * fix: revert boltz changes and fix locktime * Replace subquery with LEFT JOIN to get refund data * Rewrite locktime check for more clarity * Rewrite locktime check for more clarity * Fix select_payment_query in case of refunds * Include boltz-client fixes (handling of unwraps for failed broadcasts) * Cargo.toml: Use boltz-client branch instead of commit --------- Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
This commit is contained in:
2
cli/Cargo.lock
generated
2
cli/Cargo.lock
generated
@@ -370,7 +370,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "boltz-client"
|
name = "boltz-client"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-21#a6254147a0d00756880f5de38ac6000e49f61560"
|
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
"bitcoin 0.31.2",
|
"bitcoin 0.31.2",
|
||||||
|
|||||||
2
lib/Cargo.lock
generated
2
lib/Cargo.lock
generated
@@ -490,7 +490,7 @@ checksum = "829a082bd3761fde7476dc2ed85ca56c11628948460ece621e4f56fef5046567"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "boltz-client"
|
name = "boltz-client"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-21#a6254147a0d00756880f5de38ac6000e49f61560"
|
source = "git+https://github.com/ok300/boltz-rust?branch=ok300-breez-latest-05-27#faecead854e8b0803f744b25bd0c47853cc487da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
"bitcoin 0.31.2",
|
"bitcoin 0.31.2",
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ typedef struct wire_cst_payment {
|
|||||||
uint64_t amount_sat;
|
uint64_t amount_sat;
|
||||||
uint64_t *fees_sat;
|
uint64_t *fees_sat;
|
||||||
struct wire_cst_list_prim_u_8_strict *preimage;
|
struct wire_cst_list_prim_u_8_strict *preimage;
|
||||||
|
struct wire_cst_list_prim_u_8_strict *refund_tx_id;
|
||||||
|
uint64_t *refund_tx_amount_sat;
|
||||||
int32_t payment_type;
|
int32_t payment_type;
|
||||||
int32_t status;
|
int32_t status;
|
||||||
} wire_cst_payment;
|
} wire_cst_payment;
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ dictionary Payment {
|
|||||||
u64 amount_sat;
|
u64 amount_sat;
|
||||||
u64? fees_sat = null;
|
u64? fees_sat = null;
|
||||||
string? preimage = null;
|
string? preimage = null;
|
||||||
|
string? refund_tx_id = null;
|
||||||
|
u64? refund_tx_amount_sat = null;
|
||||||
PaymentType payment_type;
|
PaymentType payment_type;
|
||||||
PaymentState status;
|
PaymentState status;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ frb = ["dep:flutter_rust_bridge"]
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
bip39 = { version = "2.0.0", features = ["serde"] }
|
bip39 = { version = "2.0.0", features = ["serde"] }
|
||||||
#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" }
|
#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" }
|
||||||
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-21" }
|
boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-27" }
|
||||||
flutter_rust_bridge = { version = "=2.0.0-dev.35", features = ["chrono"], optional = true }
|
flutter_rust_bridge = { version = "=2.0.0-dev.35", features = ["chrono"], optional = true }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
lwk_common = "0.5.1"
|
lwk_common = "0.5.1"
|
||||||
|
|||||||
@@ -254,6 +254,8 @@ impl CstDecode<crate::model::Payment> for wire_cst_payment {
|
|||||||
amount_sat: self.amount_sat.cst_decode(),
|
amount_sat: self.amount_sat.cst_decode(),
|
||||||
fees_sat: self.fees_sat.cst_decode(),
|
fees_sat: self.fees_sat.cst_decode(),
|
||||||
preimage: self.preimage.cst_decode(),
|
preimage: self.preimage.cst_decode(),
|
||||||
|
refund_tx_id: self.refund_tx_id.cst_decode(),
|
||||||
|
refund_tx_amount_sat: self.refund_tx_amount_sat.cst_decode(),
|
||||||
payment_type: self.payment_type.cst_decode(),
|
payment_type: self.payment_type.cst_decode(),
|
||||||
status: self.status.cst_decode(),
|
status: self.status.cst_decode(),
|
||||||
}
|
}
|
||||||
@@ -453,6 +455,8 @@ impl NewWithNullPtr for wire_cst_payment {
|
|||||||
amount_sat: Default::default(),
|
amount_sat: Default::default(),
|
||||||
fees_sat: core::ptr::null_mut(),
|
fees_sat: core::ptr::null_mut(),
|
||||||
preimage: core::ptr::null_mut(),
|
preimage: core::ptr::null_mut(),
|
||||||
|
refund_tx_id: core::ptr::null_mut(),
|
||||||
|
refund_tx_amount_sat: core::ptr::null_mut(),
|
||||||
payment_type: Default::default(),
|
payment_type: Default::default(),
|
||||||
status: Default::default(),
|
status: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -893,6 +897,8 @@ pub struct wire_cst_payment {
|
|||||||
amount_sat: u64,
|
amount_sat: u64,
|
||||||
fees_sat: *mut u64,
|
fees_sat: *mut u64,
|
||||||
preimage: *mut wire_cst_list_prim_u_8_strict,
|
preimage: *mut wire_cst_list_prim_u_8_strict,
|
||||||
|
refund_tx_id: *mut wire_cst_list_prim_u_8_strict,
|
||||||
|
refund_tx_amount_sat: *mut u64,
|
||||||
payment_type: i32,
|
payment_type: i32,
|
||||||
status: i32,
|
status: i32,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -778,6 +778,8 @@ impl SseDecode for crate::model::Payment {
|
|||||||
let mut var_amountSat = <u64>::sse_decode(deserializer);
|
let mut var_amountSat = <u64>::sse_decode(deserializer);
|
||||||
let mut var_feesSat = <Option<u64>>::sse_decode(deserializer);
|
let mut var_feesSat = <Option<u64>>::sse_decode(deserializer);
|
||||||
let mut var_preimage = <Option<String>>::sse_decode(deserializer);
|
let mut var_preimage = <Option<String>>::sse_decode(deserializer);
|
||||||
|
let mut var_refundTxId = <Option<String>>::sse_decode(deserializer);
|
||||||
|
let mut var_refundTxAmountSat = <Option<u64>>::sse_decode(deserializer);
|
||||||
let mut var_paymentType = <crate::model::PaymentType>::sse_decode(deserializer);
|
let mut var_paymentType = <crate::model::PaymentType>::sse_decode(deserializer);
|
||||||
let mut var_status = <crate::model::PaymentState>::sse_decode(deserializer);
|
let mut var_status = <crate::model::PaymentState>::sse_decode(deserializer);
|
||||||
return crate::model::Payment {
|
return crate::model::Payment {
|
||||||
@@ -787,6 +789,8 @@ impl SseDecode for crate::model::Payment {
|
|||||||
amount_sat: var_amountSat,
|
amount_sat: var_amountSat,
|
||||||
fees_sat: var_feesSat,
|
fees_sat: var_feesSat,
|
||||||
preimage: var_preimage,
|
preimage: var_preimage,
|
||||||
|
refund_tx_id: var_refundTxId,
|
||||||
|
refund_tx_amount_sat: var_refundTxAmountSat,
|
||||||
payment_type: var_paymentType,
|
payment_type: var_paymentType,
|
||||||
status: var_status,
|
status: var_status,
|
||||||
};
|
};
|
||||||
@@ -1172,6 +1176,8 @@ impl flutter_rust_bridge::IntoDart for crate::model::Payment {
|
|||||||
self.amount_sat.into_into_dart().into_dart(),
|
self.amount_sat.into_into_dart().into_dart(),
|
||||||
self.fees_sat.into_into_dart().into_dart(),
|
self.fees_sat.into_into_dart().into_dart(),
|
||||||
self.preimage.into_into_dart().into_dart(),
|
self.preimage.into_into_dart().into_dart(),
|
||||||
|
self.refund_tx_id.into_into_dart().into_dart(),
|
||||||
|
self.refund_tx_amount_sat.into_into_dart().into_dart(),
|
||||||
self.payment_type.into_into_dart().into_dart(),
|
self.payment_type.into_into_dart().into_dart(),
|
||||||
self.status.into_into_dart().into_dart(),
|
self.status.into_into_dart().into_dart(),
|
||||||
]
|
]
|
||||||
@@ -1577,6 +1583,8 @@ impl SseEncode for crate::model::Payment {
|
|||||||
<u64>::sse_encode(self.amount_sat, serializer);
|
<u64>::sse_encode(self.amount_sat, serializer);
|
||||||
<Option<u64>>::sse_encode(self.fees_sat, serializer);
|
<Option<u64>>::sse_encode(self.fees_sat, serializer);
|
||||||
<Option<String>>::sse_encode(self.preimage, serializer);
|
<Option<String>>::sse_encode(self.preimage, serializer);
|
||||||
|
<Option<String>>::sse_encode(self.refund_tx_id, serializer);
|
||||||
|
<Option<u64>>::sse_encode(self.refund_tx_amount_sat, serializer);
|
||||||
<crate::model::PaymentType>::sse_encode(self.payment_type, serializer);
|
<crate::model::PaymentType>::sse_encode(self.payment_type, serializer);
|
||||||
<crate::model::PaymentState>::sse_encode(self.status, serializer);
|
<crate::model::PaymentState>::sse_encode(self.status, serializer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use boltz_client::network::Chain;
|
|||||||
use boltz_client::swaps::boltzv2::{
|
use boltz_client::swaps::boltzv2::{
|
||||||
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree,
|
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree,
|
||||||
};
|
};
|
||||||
use boltz_client::SwapType;
|
use boltz_client::{Keypair, SwapType};
|
||||||
use lwk_signer::SwSigner;
|
use lwk_signer::SwSigner;
|
||||||
use lwk_wollet::{ElectrumUrl, ElementsNetwork, WolletDescriptor};
|
use lwk_wollet::{ElectrumUrl, ElementsNetwork, WolletDescriptor};
|
||||||
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
|
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
|
||||||
@@ -201,8 +201,13 @@ pub(crate) struct SendSwap {
|
|||||||
pub(crate) refund_tx_id: Option<String>,
|
pub(crate) refund_tx_id: Option<String>,
|
||||||
pub(crate) created_at: u32,
|
pub(crate) created_at: u32,
|
||||||
pub(crate) state: PaymentState,
|
pub(crate) state: PaymentState,
|
||||||
|
pub(crate) refund_private_key: String,
|
||||||
}
|
}
|
||||||
impl SendSwap {
|
impl SendSwap {
|
||||||
|
pub(crate) fn get_refund_keypair(&self) -> Result<Keypair, PaymentError> {
|
||||||
|
utils::decode_keypair(&self.refund_private_key).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_boltz_create_response(
|
pub(crate) fn get_boltz_create_response(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<CreateSubmarineResponse, PaymentError> {
|
) -> Result<CreateSubmarineResponse, PaymentError> {
|
||||||
@@ -460,6 +465,9 @@ pub struct PaymentSwapData {
|
|||||||
/// Amount received by the swap receiver
|
/// Amount received by the swap receiver
|
||||||
pub receiver_amount_sat: u64,
|
pub receiver_amount_sat: u64,
|
||||||
|
|
||||||
|
pub refund_tx_id: Option<String>,
|
||||||
|
pub refund_tx_amount_sat: Option<u64>,
|
||||||
|
|
||||||
/// Payment status derived from the swap status
|
/// Payment status derived from the swap status
|
||||||
pub status: PaymentState,
|
pub status: PaymentState,
|
||||||
}
|
}
|
||||||
@@ -495,6 +503,12 @@ pub struct Payment {
|
|||||||
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
||||||
pub preimage: Option<String>,
|
pub preimage: Option<String>,
|
||||||
|
|
||||||
|
/// For a Send swap which was refunded, this is the refund tx id
|
||||||
|
pub refund_tx_id: Option<String>,
|
||||||
|
|
||||||
|
/// For a Send swap which was refunded, this is the refund amount
|
||||||
|
pub refund_tx_amount_sat: Option<u64>,
|
||||||
|
|
||||||
pub payment_type: PaymentType,
|
pub payment_type: PaymentType,
|
||||||
|
|
||||||
/// Composite status representing the overall status of the payment.
|
/// Composite status representing the overall status of the payment.
|
||||||
@@ -518,6 +532,8 @@ impl Payment {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| s.payer_amount_sat - s.receiver_amount_sat),
|
.map(|s| s.payer_amount_sat - s.receiver_amount_sat),
|
||||||
preimage: swap.as_ref().and_then(|s| s.preimage.clone()),
|
preimage: swap.as_ref().and_then(|s| s.preimage.clone()),
|
||||||
|
refund_tx_id: swap.as_ref().and_then(|s| s.refund_tx_id.clone()),
|
||||||
|
refund_tx_amount_sat: swap.as_ref().and_then(|s| s.refund_tx_amount_sat),
|
||||||
payment_type: tx.payment_type,
|
payment_type: tx.payment_type,
|
||||||
status: match swap {
|
status: match swap {
|
||||||
Some(swap) => swap.status,
|
Some(swap) => swap.status,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub(crate) fn current_migrations() -> Vec<&'static str> {
|
|||||||
payer_amount_sat INTEGER NOT NULL,
|
payer_amount_sat INTEGER NOT NULL,
|
||||||
receiver_amount_sat INTEGER NOT NULL,
|
receiver_amount_sat INTEGER NOT NULL,
|
||||||
create_response_json TEXT NOT NULL,
|
create_response_json TEXT NOT NULL,
|
||||||
|
refund_private_key TEXT NOT NULL,
|
||||||
lockup_tx_id TEXT,
|
lockup_tx_id TEXT,
|
||||||
refund_tx_id TEXT,
|
refund_tx_id TEXT,
|
||||||
created_at INTEGER NOT NULL,
|
created_at INTEGER NOT NULL,
|
||||||
|
|||||||
@@ -109,15 +109,21 @@ impl Persister {
|
|||||||
ss.id,
|
ss.id,
|
||||||
ss.created_at,
|
ss.created_at,
|
||||||
ss.preimage,
|
ss.preimage,
|
||||||
|
ss.refund_tx_id,
|
||||||
ss.payer_amount_sat,
|
ss.payer_amount_sat,
|
||||||
ss.receiver_amount_sat,
|
ss.receiver_amount_sat,
|
||||||
ss.state
|
ss.state,
|
||||||
FROM payment_tx_data AS ptx
|
rtx.amount_sat
|
||||||
LEFT JOIN receive_swaps AS rs
|
FROM payment_tx_data AS ptx -- Payment tx (each tx results in a Payment)
|
||||||
|
LEFT JOIN receive_swaps AS rs -- Receive Swap data
|
||||||
ON ptx.tx_id = rs.claim_tx_id
|
ON ptx.tx_id = rs.claim_tx_id
|
||||||
LEFT JOIN send_swaps AS ss
|
LEFT JOIN send_swaps AS ss -- Send Swap data
|
||||||
ON ptx.tx_id = ss.lockup_tx_id
|
ON ptx.tx_id = ss.lockup_tx_id
|
||||||
WHERE {}
|
LEFT JOIN payment_tx_data AS rtx -- Refund tx data
|
||||||
|
ON rtx.tx_id = ss.refund_tx_id
|
||||||
|
WHERE -- Filter out refund txs from Payment tx list
|
||||||
|
ptx.tx_id NOT IN (SELECT refund_tx_id FROM send_swaps WHERE refund_tx_id NOT NULL)
|
||||||
|
AND {}
|
||||||
",
|
",
|
||||||
where_clause.unwrap_or("true")
|
where_clause.unwrap_or("true")
|
||||||
)
|
)
|
||||||
@@ -137,12 +143,15 @@ impl Persister {
|
|||||||
let maybe_receive_swap_payer_amount_sat: Option<u64> = row.get(7)?;
|
let maybe_receive_swap_payer_amount_sat: Option<u64> = row.get(7)?;
|
||||||
let maybe_receive_swap_receiver_amount_sat: Option<u64> = row.get(8)?;
|
let maybe_receive_swap_receiver_amount_sat: Option<u64> = row.get(8)?;
|
||||||
let maybe_receive_swap_receiver_state: Option<PaymentState> = row.get(9)?;
|
let maybe_receive_swap_receiver_state: Option<PaymentState> = row.get(9)?;
|
||||||
|
|
||||||
let maybe_send_swap_id: Option<String> = row.get(10)?;
|
let maybe_send_swap_id: Option<String> = row.get(10)?;
|
||||||
let maybe_send_swap_created_at: Option<u32> = row.get(11)?;
|
let maybe_send_swap_created_at: Option<u32> = row.get(11)?;
|
||||||
let maybe_send_swap_preimage: Option<String> = row.get(12)?;
|
let maybe_send_swap_preimage: Option<String> = row.get(12)?;
|
||||||
let maybe_send_swap_payer_amount_sat: Option<u64> = row.get(13)?;
|
let maybe_send_swap_refund_tx_id: Option<String> = row.get(13)?;
|
||||||
let maybe_send_swap_receiver_amount_sat: Option<u64> = row.get(14)?;
|
let maybe_send_swap_payer_amount_sat: Option<u64> = row.get(14)?;
|
||||||
let maybe_send_swap_state: Option<PaymentState> = row.get(15)?;
|
let maybe_send_swap_receiver_amount_sat: Option<u64> = row.get(15)?;
|
||||||
|
let maybe_send_swap_state: Option<PaymentState> = row.get(16)?;
|
||||||
|
let maybe_send_swap_refund_tx_amount_sat: Option<u64> = row.get(17)?;
|
||||||
|
|
||||||
let swap = match maybe_receive_swap_id {
|
let swap = match maybe_receive_swap_id {
|
||||||
Some(receive_swap_id) => Some(PaymentSwapData {
|
Some(receive_swap_id) => Some(PaymentSwapData {
|
||||||
@@ -151,6 +160,8 @@ impl Persister {
|
|||||||
preimage: None,
|
preimage: None,
|
||||||
payer_amount_sat: maybe_receive_swap_payer_amount_sat.unwrap_or(0),
|
payer_amount_sat: maybe_receive_swap_payer_amount_sat.unwrap_or(0),
|
||||||
receiver_amount_sat: maybe_receive_swap_receiver_amount_sat.unwrap_or(0),
|
receiver_amount_sat: maybe_receive_swap_receiver_amount_sat.unwrap_or(0),
|
||||||
|
refund_tx_id: None,
|
||||||
|
refund_tx_amount_sat: None,
|
||||||
status: maybe_receive_swap_receiver_state.unwrap_or(PaymentState::Created),
|
status: maybe_receive_swap_receiver_state.unwrap_or(PaymentState::Created),
|
||||||
}),
|
}),
|
||||||
None => maybe_send_swap_id.map(|send_swap_id| PaymentSwapData {
|
None => maybe_send_swap_id.map(|send_swap_id| PaymentSwapData {
|
||||||
@@ -159,6 +170,8 @@ impl Persister {
|
|||||||
preimage: maybe_send_swap_preimage,
|
preimage: maybe_send_swap_preimage,
|
||||||
payer_amount_sat: maybe_send_swap_payer_amount_sat.unwrap_or(0),
|
payer_amount_sat: maybe_send_swap_payer_amount_sat.unwrap_or(0),
|
||||||
receiver_amount_sat: maybe_send_swap_receiver_amount_sat.unwrap_or(0),
|
receiver_amount_sat: maybe_send_swap_receiver_amount_sat.unwrap_or(0),
|
||||||
|
refund_tx_id: maybe_send_swap_refund_tx_id,
|
||||||
|
refund_tx_amount_sat: maybe_send_swap_refund_tx_amount_sat,
|
||||||
status: maybe_send_swap_state.unwrap_or(PaymentState::Created),
|
status: maybe_send_swap_state.unwrap_or(PaymentState::Created),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use rusqlite::{named_params, params, Connection, Row};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
impl Persister {
|
impl Persister {
|
||||||
pub(crate) fn insert_receive_swap(&self, receive_swap: ReceiveSwap) -> Result<()> {
|
pub(crate) fn insert_receive_swap(&self, receive_swap: &ReceiveSwap) -> Result<()> {
|
||||||
let con = self.get_connection()?;
|
let con = self.get_connection()?;
|
||||||
|
|
||||||
let mut stmt = con.prepare(
|
let mut stmt = con.prepare(
|
||||||
@@ -31,16 +31,16 @@ impl Persister {
|
|||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
)?;
|
)?;
|
||||||
_ = stmt.execute((
|
_ = stmt.execute((
|
||||||
receive_swap.id,
|
&receive_swap.id,
|
||||||
receive_swap.preimage,
|
&receive_swap.preimage,
|
||||||
receive_swap.create_response_json,
|
&receive_swap.create_response_json,
|
||||||
receive_swap.invoice,
|
&receive_swap.invoice,
|
||||||
receive_swap.payer_amount_sat,
|
&receive_swap.payer_amount_sat,
|
||||||
receive_swap.receiver_amount_sat,
|
&receive_swap.receiver_amount_sat,
|
||||||
receive_swap.created_at,
|
&receive_swap.created_at,
|
||||||
receive_swap.claim_fees_sat,
|
&receive_swap.claim_fees_sat,
|
||||||
receive_swap.claim_tx_id,
|
&receive_swap.claim_tx_id,
|
||||||
receive_swap.state,
|
&receive_swap.state,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::model::*;
|
|||||||
use crate::persist::Persister;
|
use crate::persist::Persister;
|
||||||
|
|
||||||
impl Persister {
|
impl Persister {
|
||||||
pub(crate) fn insert_send_swap(&self, send_swap: SendSwap) -> Result<()> {
|
pub(crate) fn insert_send_swap(&self, send_swap: &SendSwap) -> Result<()> {
|
||||||
let con = self.get_connection()?;
|
let con = self.get_connection()?;
|
||||||
|
|
||||||
let mut stmt = con.prepare(
|
let mut stmt = con.prepare(
|
||||||
@@ -22,23 +22,25 @@ impl Persister {
|
|||||||
payer_amount_sat,
|
payer_amount_sat,
|
||||||
receiver_amount_sat,
|
receiver_amount_sat,
|
||||||
create_response_json,
|
create_response_json,
|
||||||
|
refund_private_key,
|
||||||
lockup_tx_id,
|
lockup_tx_id,
|
||||||
refund_tx_id,
|
refund_tx_id,
|
||||||
created_at,
|
created_at,
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
)?;
|
)?;
|
||||||
_ = stmt.execute((
|
_ = stmt.execute((
|
||||||
send_swap.id,
|
&send_swap.id,
|
||||||
send_swap.invoice,
|
&send_swap.invoice,
|
||||||
send_swap.payer_amount_sat,
|
&send_swap.payer_amount_sat,
|
||||||
send_swap.receiver_amount_sat,
|
&send_swap.receiver_amount_sat,
|
||||||
send_swap.create_response_json,
|
&send_swap.create_response_json,
|
||||||
send_swap.lockup_tx_id,
|
&send_swap.refund_private_key,
|
||||||
send_swap.refund_tx_id,
|
&send_swap.lockup_tx_id,
|
||||||
send_swap.created_at,
|
&send_swap.refund_tx_id,
|
||||||
send_swap.state,
|
&send_swap.created_at,
|
||||||
|
&send_swap.state,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -59,6 +61,7 @@ impl Persister {
|
|||||||
payer_amount_sat,
|
payer_amount_sat,
|
||||||
receiver_amount_sat,
|
receiver_amount_sat,
|
||||||
create_response_json,
|
create_response_json,
|
||||||
|
refund_private_key,
|
||||||
lockup_tx_id,
|
lockup_tx_id,
|
||||||
refund_tx_id,
|
refund_tx_id,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -85,10 +88,11 @@ impl Persister {
|
|||||||
payer_amount_sat: row.get(2)?,
|
payer_amount_sat: row.get(2)?,
|
||||||
receiver_amount_sat: row.get(3)?,
|
receiver_amount_sat: row.get(3)?,
|
||||||
create_response_json: row.get(4)?,
|
create_response_json: row.get(4)?,
|
||||||
lockup_tx_id: row.get(5)?,
|
refund_private_key: row.get(5)?,
|
||||||
refund_tx_id: row.get(6)?,
|
lockup_tx_id: row.get(6)?,
|
||||||
created_at: row.get(7)?,
|
refund_tx_id: row.get(7)?,
|
||||||
state: row.get(8)?,
|
created_at: row.get(8)?,
|
||||||
|
state: row.get(9)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use boltz_client::network::Chain;
|
||||||
|
use boltz_client::ToHex;
|
||||||
use boltz_client::{
|
use boltz_client::{
|
||||||
network::electrum::ElectrumConfig,
|
network::electrum::ElectrumConfig,
|
||||||
swaps::{
|
swaps::{
|
||||||
@@ -13,7 +15,7 @@ use log::{debug, error, info, warn};
|
|||||||
use lwk_common::{singlesig_desc, Signer, Singlesig};
|
use lwk_common::{singlesig_desc, Signer, Singlesig};
|
||||||
use lwk_signer::{AnySigner, SwSigner};
|
use lwk_signer::{AnySigner, SwSigner};
|
||||||
use lwk_wollet::bitcoin::Witness;
|
use lwk_wollet::bitcoin::Witness;
|
||||||
use lwk_wollet::elements::hex::ToHex;
|
use lwk_wollet::elements::{LockTime, LockTime::*};
|
||||||
use lwk_wollet::hashes::{sha256, Hash};
|
use lwk_wollet::hashes::{sha256, Hash};
|
||||||
use lwk_wollet::{
|
use lwk_wollet::{
|
||||||
elements::{Address, Transaction},
|
elements::{Address, Transaction},
|
||||||
@@ -148,23 +150,6 @@ impl LiquidSdk {
|
|||||||
Ok(descriptor_str.parse()?)
|
Ok(descriptor_str.parse()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_submarine_keys(&self, derivation_index: i32) -> Result<Keypair, PaymentError> {
|
|
||||||
let mnemonic = self
|
|
||||||
.lwk_signer
|
|
||||||
.mnemonic()
|
|
||||||
.ok_or(PaymentError::SignerError {
|
|
||||||
err: "Could not claim: Mnemonic not found".to_string(),
|
|
||||||
})?;
|
|
||||||
let swap_key = SwapKey::from_submarine_account(
|
|
||||||
&mnemonic.to_string(),
|
|
||||||
"",
|
|
||||||
self.network.into(),
|
|
||||||
derivation_index as u64,
|
|
||||||
)?;
|
|
||||||
let lsk = LiquidSwapKey::try_from(swap_key)?;
|
|
||||||
Ok(lsk.keypair)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_state_transition(
|
fn validate_state_transition(
|
||||||
from_state: PaymentState,
|
from_state: PaymentState,
|
||||||
to_state: PaymentState,
|
to_state: PaymentState,
|
||||||
@@ -406,20 +391,12 @@ impl LiquidSdk {
|
|||||||
.persister
|
.persister
|
||||||
.fetch_send_swap(id)?
|
.fetch_send_swap(id)?
|
||||||
.ok_or(anyhow!("No ongoing Send Swap found for ID {id}"))?;
|
.ok_or(anyhow!("No ongoing Send Swap found for ID {id}"))?;
|
||||||
let create_response: CreateSubmarineResponse =
|
|
||||||
ongoing_send_swap.get_boltz_create_response()?;
|
|
||||||
|
|
||||||
let receiver_amount_sat = get_invoice_amount!(ongoing_send_swap.invoice);
|
|
||||||
let keypair = self.get_submarine_keys(0)?;
|
|
||||||
|
|
||||||
match swap_state {
|
match swap_state {
|
||||||
SubSwapStates::TransactionClaimPending => {
|
SubSwapStates::TransactionClaimPending => {
|
||||||
let lockup_tx_id = ongoing_send_swap.lockup_tx_id.ok_or(anyhow!(
|
let keypair = ongoing_send_swap.get_refund_keypair()?;
|
||||||
"Swap-in {id} is pending but no lockup txid is present"
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
||||||
&create_response,
|
&ongoing_send_swap.get_boltz_create_response()?,
|
||||||
keypair.public_key().into(),
|
keypair.public_key().into(),
|
||||||
)
|
)
|
||||||
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
||||||
@@ -445,21 +422,13 @@ impl LiquidSdk {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If swap state is unrecoverable, try refunding
|
||||||
SubSwapStates::TransactionLockupFailed
|
SubSwapStates::TransactionLockupFailed
|
||||||
| SubSwapStates::InvoiceFailedToPay
|
| SubSwapStates::InvoiceFailedToPay
|
||||||
| SubSwapStates::SwapExpired => {
|
| SubSwapStates::SwapExpired => {
|
||||||
warn!("Swap-in {id} is in an unrecoverable state: {swap_state:?}");
|
warn!("Swap-in {id} is in an unrecoverable state: {swap_state:?}");
|
||||||
|
|
||||||
// If swap state is unrecoverable, try refunding
|
let refund_tx_id = self.try_refund(&ongoing_send_swap).await?;
|
||||||
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
|
||||||
&create_response,
|
|
||||||
keypair.public_key().into(),
|
|
||||||
)
|
|
||||||
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
|
||||||
|
|
||||||
let refund_tx_id = self
|
|
||||||
.try_refund(id, &swap_script, &keypair, receiver_amount_sat)
|
|
||||||
.await?;
|
|
||||||
info!("Broadcast refund tx for Swap-in {id}. Tx id: {refund_tx_id}");
|
info!("Broadcast refund tx for Swap-in {id}. Tx id: {refund_tx_id}");
|
||||||
self.try_handle_send_swap_update(id, Pending, None, None, Some(&refund_tx_id))
|
self.try_handle_send_swap_update(id, Pending, None, None, Some(&refund_tx_id))
|
||||||
.await?;
|
.await?;
|
||||||
@@ -495,13 +464,23 @@ impl LiquidSdk {
|
|||||||
for p in self.list_payments()? {
|
for p in self.list_payments()? {
|
||||||
match p.payment_type {
|
match p.payment_type {
|
||||||
PaymentType::Send => match p.status {
|
PaymentType::Send => match p.status {
|
||||||
PaymentState::Complete => confirmed_sent_sat += p.amount_sat,
|
Complete => confirmed_sent_sat += p.amount_sat,
|
||||||
PaymentState::Failed => {}
|
Failed => {
|
||||||
_ => pending_send_sat += p.amount_sat,
|
confirmed_sent_sat += p.amount_sat;
|
||||||
|
confirmed_received_sat += p.refund_tx_amount_sat.unwrap_or_default();
|
||||||
|
}
|
||||||
|
Pending => match p.refund_tx_amount_sat {
|
||||||
|
Some(refund_tx_amount_sat) => {
|
||||||
|
confirmed_sent_sat += p.amount_sat;
|
||||||
|
pending_receive_sat += refund_tx_amount_sat;
|
||||||
|
}
|
||||||
|
None => pending_send_sat += p.amount_sat,
|
||||||
|
},
|
||||||
|
Created => pending_send_sat += p.amount_sat,
|
||||||
},
|
},
|
||||||
PaymentType::Receive => match p.status {
|
PaymentType::Receive => match p.status {
|
||||||
PaymentState::Complete => confirmed_received_sat += p.amount_sat,
|
Complete => confirmed_received_sat += p.amount_sat,
|
||||||
PaymentState::Failed => {}
|
Failed => {}
|
||||||
_ => pending_receive_sat += p.amount_sat,
|
_ => pending_receive_sat += p.amount_sat,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -652,50 +631,118 @@ impl LiquidSdk {
|
|||||||
|
|
||||||
async fn new_refund_tx(
|
async fn new_refund_tx(
|
||||||
&self,
|
&self,
|
||||||
|
swap_id: &str,
|
||||||
swap_script: &LBtcSwapScriptV2,
|
swap_script: &LBtcSwapScriptV2,
|
||||||
) -> Result<LBtcSwapTxV2, PaymentError> {
|
) -> Result<LBtcSwapTxV2, PaymentError> {
|
||||||
let wallet = self.lwk_wollet.lock().await;
|
let output_address = self.next_unused_address().await?.to_string();
|
||||||
let output_address = wallet.address(Some(0))?.address().to_string();
|
|
||||||
let network_config = self.network_config();
|
let network_config = self.network_config();
|
||||||
Ok(LBtcSwapTxV2::new_refund(
|
Ok(LBtcSwapTxV2::new_refund(
|
||||||
swap_script.clone(),
|
swap_script.clone(),
|
||||||
&output_address,
|
&output_address,
|
||||||
&network_config,
|
&network_config,
|
||||||
|
self.boltz_url_v2().to_string(),
|
||||||
|
swap_id.to_string(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_refund(
|
async fn try_refund_cooperative(
|
||||||
&self,
|
&self,
|
||||||
swap_id: &str,
|
swap: &SendSwap,
|
||||||
swap_script: &LBtcSwapScriptV2,
|
refund_tx: &LBtcSwapTxV2,
|
||||||
keypair: &Keypair,
|
broadcast_fees_sat: Amount,
|
||||||
amount_sat: u64,
|
is_lowball: Option<(&BoltzApiClientV2, Chain)>,
|
||||||
) -> Result<String, PaymentError> {
|
) -> Result<String, PaymentError> {
|
||||||
let refund_tx = self.new_refund_tx(swap_script).await?;
|
info!("Initiating cooperative refund for Send Swap {}", &swap.id);
|
||||||
|
let tx = refund_tx.sign_refund(
|
||||||
|
&swap.get_refund_keypair()?,
|
||||||
|
broadcast_fees_sat,
|
||||||
|
Some((&self.boltz_client_v2(), &swap.id)),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||||
|
info!(
|
||||||
|
"Successfully broadcast cooperative refund for Send Swap {}",
|
||||||
|
&swap.id
|
||||||
|
);
|
||||||
|
Ok(refund_tx_id.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_refund_non_cooperative(
|
||||||
|
&self,
|
||||||
|
swap: &SendSwap,
|
||||||
|
swap_script: &LBtcSwapScriptV2,
|
||||||
|
refund_tx: LBtcSwapTxV2,
|
||||||
|
broadcast_fees_sat: Amount,
|
||||||
|
is_lowball: Option<(&BoltzApiClientV2, Chain)>,
|
||||||
|
) -> Result<String, PaymentError> {
|
||||||
|
info!(
|
||||||
|
"Initiating non-cooperative refund for Send Swap {}",
|
||||||
|
&swap.id
|
||||||
|
);
|
||||||
|
|
||||||
|
let current_height = self.lwk_wollet.lock().await.tip().height();
|
||||||
|
let locktime_from_height =
|
||||||
|
LockTime::from_height(current_height).map_err(|e| PaymentError::Generic {
|
||||||
|
err: format!("Cannot convert current block height to lock time: {e:?}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
info!("locktime info: locktime_from_height = {locktime_from_height:?}, swap_script.locktime = {:?}", swap_script.locktime);
|
||||||
|
let is_locktime_satisfied = match (locktime_from_height, swap_script.locktime) {
|
||||||
|
(Blocks(n), Blocks(lock_time)) => n >= lock_time,
|
||||||
|
(Seconds(n), Seconds(lock_time)) => n >= lock_time,
|
||||||
|
_ => false, // Not using the same units
|
||||||
|
};
|
||||||
|
if !is_locktime_satisfied {
|
||||||
|
return Err(PaymentError::Generic {
|
||||||
|
err: format!(
|
||||||
|
"Cannot refund non-cooperatively. Lock time not elapsed yet. Current tip: {:?}. Script lock time: {:?}",
|
||||||
|
locktime_from_height, swap_script.locktime
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx = refund_tx.sign_refund(&swap.get_refund_keypair()?, broadcast_fees_sat, None)?;
|
||||||
|
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
||||||
|
info!(
|
||||||
|
"Successfully broadcast non-cooperative refund for swap-in {}",
|
||||||
|
swap.id
|
||||||
|
);
|
||||||
|
Ok(refund_tx_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_refund(&self, swap: &SendSwap) -> Result<String, PaymentError> {
|
||||||
|
let id = &swap.id;
|
||||||
|
let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp(
|
||||||
|
&swap.get_boltz_create_response()?,
|
||||||
|
swap.get_refund_keypair()?.public_key().into(),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!("Could not rebuild refund details for swap-in {id}: {e:?}"))?;
|
||||||
|
|
||||||
|
let refund_tx = self.new_refund_tx(&swap.id, &swap_script).await?;
|
||||||
|
let amount_sat = get_invoice_amount!(swap.invoice);
|
||||||
let broadcast_fees_sat =
|
let broadcast_fees_sat =
|
||||||
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
|
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
|
||||||
let client = self.boltz_client_v2();
|
let client = self.boltz_client_v2();
|
||||||
let is_lowball = Some((&client, boltz_client::network::Chain::from(self.network)));
|
let is_lowball = match self.network {
|
||||||
|
Network::Liquid => None,
|
||||||
|
Network::LiquidTestnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
|
||||||
|
};
|
||||||
|
|
||||||
match refund_tx.sign_refund(
|
match self
|
||||||
keypair,
|
.try_refund_cooperative(swap, &refund_tx, broadcast_fees_sat, is_lowball)
|
||||||
broadcast_fees_sat,
|
.await
|
||||||
Some((&client, &swap_id.to_string())),
|
{
|
||||||
) {
|
Ok(res) => Ok(res),
|
||||||
// Try with cooperative refund
|
|
||||||
Ok(tx) => {
|
|
||||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
|
||||||
debug!("Successfully broadcast cooperative refund for swap-in {swap_id}");
|
|
||||||
Ok(refund_tx_id)
|
|
||||||
}
|
|
||||||
// Try with non-cooperative refund
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("Cooperative refund failed: {:?}", e);
|
warn!("Cooperative refund failed: {:?}", e);
|
||||||
let tx = refund_tx.sign_refund(keypair, broadcast_fees_sat, None)?;
|
self.try_refund_non_cooperative(
|
||||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
swap,
|
||||||
debug!("Successfully broadcast non-cooperative refund for swap-in {swap_id}");
|
&swap_script,
|
||||||
Ok(refund_tx_id)
|
refund_tx,
|
||||||
|
broadcast_fees_sat,
|
||||||
|
is_lowball,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,7 +770,7 @@ impl LiquidSdk {
|
|||||||
) -> Result<(), PaymentError> {
|
) -> Result<(), PaymentError> {
|
||||||
debug!("Claim is pending for swap-in {swap_id}. Initiating cooperative claim");
|
debug!("Claim is pending for swap-in {swap_id}. Initiating cooperative claim");
|
||||||
let client = self.boltz_client_v2();
|
let client = self.boltz_client_v2();
|
||||||
let refund_tx = self.new_refund_tx(swap_script).await?;
|
let refund_tx = self.new_refund_tx(swap_id, swap_script).await?;
|
||||||
|
|
||||||
let claim_tx_response = client.get_claim_tx_details(&swap_id.to_string())?;
|
let claim_tx_response = client.get_claim_tx_details(&swap_id.to_string())?;
|
||||||
debug!("Received claim tx details: {:?}", &claim_tx_response);
|
debug!("Received claim tx details: {:?}", &claim_tx_response);
|
||||||
@@ -783,7 +830,7 @@ impl LiquidSdk {
|
|||||||
PaymentError::InvalidOrExpiredFees
|
PaymentError::InvalidOrExpiredFees
|
||||||
);
|
);
|
||||||
|
|
||||||
let keypair = self.get_submarine_keys(0)?;
|
let keypair = utils::generate_keypair();
|
||||||
let refund_public_key = boltz_client::PublicKey {
|
let refund_public_key = boltz_client::PublicKey {
|
||||||
compressed: true,
|
compressed: true,
|
||||||
inner: keypair.public_key(),
|
inner: keypair.public_key(),
|
||||||
@@ -820,7 +867,7 @@ impl LiquidSdk {
|
|||||||
BoltzStatusStream::mark_swap_as_tracked(swap_id, SwapType::Submarine);
|
BoltzStatusStream::mark_swap_as_tracked(swap_id, SwapType::Submarine);
|
||||||
|
|
||||||
let payer_amount_sat = req.fees_sat + receiver_amount_sat;
|
let payer_amount_sat = req.fees_sat + receiver_amount_sat;
|
||||||
self.persister.insert_send_swap(SendSwap {
|
let swap = SendSwap {
|
||||||
id: swap_id.clone(),
|
id: swap_id.clone(),
|
||||||
invoice: req.invoice.clone(),
|
invoice: req.invoice.clone(),
|
||||||
payer_amount_sat,
|
payer_amount_sat,
|
||||||
@@ -830,7 +877,9 @@ impl LiquidSdk {
|
|||||||
refund_tx_id: None,
|
refund_tx_id: None,
|
||||||
created_at: utils::now(),
|
created_at: utils::now(),
|
||||||
state: PaymentState::Created,
|
state: PaymentState::Created,
|
||||||
})?;
|
refund_private_key: keypair.display_secret().to_string(),
|
||||||
|
};
|
||||||
|
self.persister.insert_send_swap(&swap)?;
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
let mut lockup_tx_id = String::new();
|
let mut lockup_tx_id = String::new();
|
||||||
@@ -913,9 +962,16 @@ impl LiquidSdk {
|
|||||||
SubSwapStates::InvoiceFailedToPay
|
SubSwapStates::InvoiceFailedToPay
|
||||||
| SubSwapStates::SwapExpired
|
| SubSwapStates::SwapExpired
|
||||||
| SubSwapStates::TransactionLockupFailed => {
|
| SubSwapStates::TransactionLockupFailed => {
|
||||||
let refund_tx_id = self
|
let refund_tx_id = self.try_refund(&swap).await?;
|
||||||
.try_refund(swap_id, &swap_script, &keypair, receiver_amount_sat)
|
|
||||||
.await?;
|
self.try_handle_send_swap_update(
|
||||||
|
swap_id,
|
||||||
|
Pending,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(&refund_tx_id),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
result = Err(PaymentError::Refunded {
|
result = Err(PaymentError::Refunded {
|
||||||
err: format!(
|
err: format!(
|
||||||
@@ -1080,7 +1136,7 @@ impl LiquidSdk {
|
|||||||
&invoice.to_string(),
|
&invoice.to_string(),
|
||||||
)?;
|
)?;
|
||||||
self.persister
|
self.persister
|
||||||
.insert_receive_swap(ReceiveSwap {
|
.insert_receive_swap(&ReceiveSwap {
|
||||||
id: swap_id.clone(),
|
id: swap_id.clone(),
|
||||||
preimage: preimage_str,
|
preimage: preimage_str,
|
||||||
create_response_json,
|
create_response_json,
|
||||||
@@ -1153,7 +1209,7 @@ impl LiquidSdk {
|
|||||||
info!("Retrieving preimage from non-cooperative claim tx");
|
info!("Retrieving preimage from non-cooperative claim tx");
|
||||||
|
|
||||||
let id = &swap.id;
|
let id = &swap.id;
|
||||||
let keypair = self.get_submarine_keys(0)?;
|
let keypair = swap.get_refund_keypair()?;
|
||||||
let create_response = swap.get_boltz_create_response()?;
|
let create_response = swap.get_boltz_create_response()?;
|
||||||
let electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
let electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,13 @@ pub(crate) fn get_swap_status_v2(
|
|||||||
|
|
||||||
return match args.first() {
|
return match args.first() {
|
||||||
Some(update) if update.id == swap_id => {
|
Some(update) if update.id == swap_id => {
|
||||||
info!("Got new swap status: {}", update.status);
|
info!("Got new swap status for {swap_id}: {}", update.status);
|
||||||
|
|
||||||
Ok(update.status.clone())
|
Ok(update.status.clone())
|
||||||
}
|
}
|
||||||
Some(update) => Err(anyhow!("WS reply has wrong swap ID {update:?}")),
|
Some(update) => Err(anyhow!(
|
||||||
|
"WS reply has wrong swap ID {update:?}. Should be {swap_id}"
|
||||||
|
)),
|
||||||
None => Err(anyhow!("WS reply contains no update")),
|
None => Err(anyhow!("WS reply contains no update")),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,9 @@ pub(crate) fn get_swap_status_v2(
|
|||||||
for e in &args {
|
for e in &args {
|
||||||
error!("Got error: {} for swap: {}", e.error, e.id);
|
error!("Got error: {} for swap: {}", e.error, e.id);
|
||||||
}
|
}
|
||||||
return Err(anyhow!("Got SwapUpdate errors: {args:?}"));
|
return Err(anyhow!(
|
||||||
|
"Got SwapUpdate errors for swap {swap_id}: {args:?}"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,3 +86,16 @@ pub(crate) fn json_to_pubkey(json: &str) -> Result<boltz_client::PublicKey, Paym
|
|||||||
err: format!("Failed to deserialize PublicKey: {e:?}"),
|
err: format!("Failed to deserialize PublicKey: {e:?}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_keypair() -> boltz_client::Keypair {
|
||||||
|
let secp = boltz_client::Secp256k1::new();
|
||||||
|
let mut rng = bip39::rand::rngs::OsRng;
|
||||||
|
let secret_key = lwk_wollet::secp256k1::SecretKey::new(&mut rng);
|
||||||
|
boltz_client::Keypair::from_secret_key(&secp, &secret_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn decode_keypair(secret_key: &str) -> Result<boltz_client::Keypair, lwk_wollet::Error> {
|
||||||
|
let secp = boltz_client::Secp256k1::new();
|
||||||
|
let secret_key = lwk_wollet::secp256k1::SecretKey::from_str(secret_key)?;
|
||||||
|
Ok(boltz_client::Keypair::from_secret_key(&secp, &secret_key))
|
||||||
|
}
|
||||||
|
|||||||
@@ -669,7 +669,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
Payment dco_decode_payment(dynamic raw) {
|
Payment dco_decode_payment(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
final arr = raw as List<dynamic>;
|
final arr = raw as List<dynamic>;
|
||||||
if (arr.length != 8) throw Exception('unexpected arr length: expect 8 but see ${arr.length}');
|
if (arr.length != 10) throw Exception('unexpected arr length: expect 10 but see ${arr.length}');
|
||||||
return Payment(
|
return Payment(
|
||||||
txId: dco_decode_String(arr[0]),
|
txId: dco_decode_String(arr[0]),
|
||||||
swapId: dco_decode_opt_String(arr[1]),
|
swapId: dco_decode_opt_String(arr[1]),
|
||||||
@@ -677,8 +677,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
amountSat: dco_decode_u_64(arr[3]),
|
amountSat: dco_decode_u_64(arr[3]),
|
||||||
feesSat: dco_decode_opt_box_autoadd_u_64(arr[4]),
|
feesSat: dco_decode_opt_box_autoadd_u_64(arr[4]),
|
||||||
preimage: dco_decode_opt_String(arr[5]),
|
preimage: dco_decode_opt_String(arr[5]),
|
||||||
paymentType: dco_decode_payment_type(arr[6]),
|
refundTxId: dco_decode_opt_String(arr[6]),
|
||||||
status: dco_decode_payment_state(arr[7]),
|
refundTxAmountSat: dco_decode_opt_box_autoadd_u_64(arr[7]),
|
||||||
|
paymentType: dco_decode_payment_type(arr[8]),
|
||||||
|
status: dco_decode_payment_state(arr[9]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1090,6 +1092,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
var var_amountSat = sse_decode_u_64(deserializer);
|
var var_amountSat = sse_decode_u_64(deserializer);
|
||||||
var var_feesSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
var var_feesSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||||
var var_preimage = sse_decode_opt_String(deserializer);
|
var var_preimage = sse_decode_opt_String(deserializer);
|
||||||
|
var var_refundTxId = sse_decode_opt_String(deserializer);
|
||||||
|
var var_refundTxAmountSat = sse_decode_opt_box_autoadd_u_64(deserializer);
|
||||||
var var_paymentType = sse_decode_payment_type(deserializer);
|
var var_paymentType = sse_decode_payment_type(deserializer);
|
||||||
var var_status = sse_decode_payment_state(deserializer);
|
var var_status = sse_decode_payment_state(deserializer);
|
||||||
return Payment(
|
return Payment(
|
||||||
@@ -1099,6 +1103,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
amountSat: var_amountSat,
|
amountSat: var_amountSat,
|
||||||
feesSat: var_feesSat,
|
feesSat: var_feesSat,
|
||||||
preimage: var_preimage,
|
preimage: var_preimage,
|
||||||
|
refundTxId: var_refundTxId,
|
||||||
|
refundTxAmountSat: var_refundTxAmountSat,
|
||||||
paymentType: var_paymentType,
|
paymentType: var_paymentType,
|
||||||
status: var_status);
|
status: var_status);
|
||||||
}
|
}
|
||||||
@@ -1546,6 +1552,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
sse_encode_u_64(self.amountSat, serializer);
|
sse_encode_u_64(self.amountSat, serializer);
|
||||||
sse_encode_opt_box_autoadd_u_64(self.feesSat, serializer);
|
sse_encode_opt_box_autoadd_u_64(self.feesSat, serializer);
|
||||||
sse_encode_opt_String(self.preimage, serializer);
|
sse_encode_opt_String(self.preimage, serializer);
|
||||||
|
sse_encode_opt_String(self.refundTxId, serializer);
|
||||||
|
sse_encode_opt_box_autoadd_u_64(self.refundTxAmountSat, serializer);
|
||||||
sse_encode_payment_type(self.paymentType, serializer);
|
sse_encode_payment_type(self.paymentType, serializer);
|
||||||
sse_encode_payment_state(self.status, serializer);
|
sse_encode_payment_state(self.status, serializer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -569,6 +569,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
wireObj.amount_sat = cst_encode_u_64(apiObj.amountSat);
|
wireObj.amount_sat = cst_encode_u_64(apiObj.amountSat);
|
||||||
wireObj.fees_sat = cst_encode_opt_box_autoadd_u_64(apiObj.feesSat);
|
wireObj.fees_sat = cst_encode_opt_box_autoadd_u_64(apiObj.feesSat);
|
||||||
wireObj.preimage = cst_encode_opt_String(apiObj.preimage);
|
wireObj.preimage = cst_encode_opt_String(apiObj.preimage);
|
||||||
|
wireObj.refund_tx_id = cst_encode_opt_String(apiObj.refundTxId);
|
||||||
|
wireObj.refund_tx_amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.refundTxAmountSat);
|
||||||
wireObj.payment_type = cst_encode_payment_type(apiObj.paymentType);
|
wireObj.payment_type = cst_encode_payment_type(apiObj.paymentType);
|
||||||
wireObj.status = cst_encode_payment_state(apiObj.status);
|
wireObj.status = cst_encode_payment_state(apiObj.status);
|
||||||
}
|
}
|
||||||
@@ -1369,6 +1371,10 @@ final class wire_cst_payment extends ffi.Struct {
|
|||||||
|
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
||||||
|
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
|
||||||
|
|
||||||
|
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
|
||||||
|
|
||||||
@ffi.Int32()
|
@ffi.Int32()
|
||||||
external int payment_type;
|
external int payment_type;
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,12 @@ class Payment {
|
|||||||
|
|
||||||
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
/// In case of a Send swap, this is the preimage of the paid invoice (proof of payment).
|
||||||
final String? preimage;
|
final String? preimage;
|
||||||
|
|
||||||
|
/// For a Send swap which was refunded, this is the refund tx id
|
||||||
|
final String? refundTxId;
|
||||||
|
|
||||||
|
/// For a Send swap which was refunded, this is the refund amount
|
||||||
|
final int? refundTxAmountSat;
|
||||||
final PaymentType paymentType;
|
final PaymentType paymentType;
|
||||||
|
|
||||||
/// Composite status representing the overall status of the payment.
|
/// Composite status representing the overall status of the payment.
|
||||||
@@ -177,6 +183,8 @@ class Payment {
|
|||||||
required this.amountSat,
|
required this.amountSat,
|
||||||
this.feesSat,
|
this.feesSat,
|
||||||
this.preimage,
|
this.preimage,
|
||||||
|
this.refundTxId,
|
||||||
|
this.refundTxAmountSat,
|
||||||
required this.paymentType,
|
required this.paymentType,
|
||||||
required this.status,
|
required this.status,
|
||||||
});
|
});
|
||||||
@@ -189,6 +197,8 @@ class Payment {
|
|||||||
amountSat.hashCode ^
|
amountSat.hashCode ^
|
||||||
feesSat.hashCode ^
|
feesSat.hashCode ^
|
||||||
preimage.hashCode ^
|
preimage.hashCode ^
|
||||||
|
refundTxId.hashCode ^
|
||||||
|
refundTxAmountSat.hashCode ^
|
||||||
paymentType.hashCode ^
|
paymentType.hashCode ^
|
||||||
status.hashCode;
|
status.hashCode;
|
||||||
|
|
||||||
@@ -203,6 +213,8 @@ class Payment {
|
|||||||
amountSat == other.amountSat &&
|
amountSat == other.amountSat &&
|
||||||
feesSat == other.feesSat &&
|
feesSat == other.feesSat &&
|
||||||
preimage == other.preimage &&
|
preimage == other.preimage &&
|
||||||
|
refundTxId == other.refundTxId &&
|
||||||
|
refundTxAmountSat == other.refundTxAmountSat &&
|
||||||
paymentType == other.paymentType &&
|
paymentType == other.paymentType &&
|
||||||
status == other.status;
|
status == other.status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -539,6 +539,10 @@ final class wire_cst_payment extends ffi.Struct {
|
|||||||
|
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> preimage;
|
||||||
|
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> refund_tx_id;
|
||||||
|
|
||||||
|
external ffi.Pointer<ffi.Uint64> refund_tx_amount_sat;
|
||||||
|
|
||||||
@ffi.Int32()
|
@ffi.Int32()
|
||||||
external int payment_type;
|
external int payment_type;
|
||||||
|
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ fun asPayment(payment: ReadableMap): Payment? {
|
|||||||
val amountSat = payment.getDouble("amountSat").toULong()
|
val amountSat = payment.getDouble("amountSat").toULong()
|
||||||
val feesSat = if (hasNonNullKey(payment, "feesSat")) payment.getDouble("feesSat").toULong() else null
|
val feesSat = if (hasNonNullKey(payment, "feesSat")) payment.getDouble("feesSat").toULong() else null
|
||||||
val preimage = if (hasNonNullKey(payment, "preimage")) payment.getString("preimage") else null
|
val preimage = if (hasNonNullKey(payment, "preimage")) payment.getString("preimage") else null
|
||||||
|
val refundTxId = if (hasNonNullKey(payment, "refundTxId")) payment.getString("refundTxId") else null
|
||||||
|
val refundTxAmountSat = if (hasNonNullKey(payment, "refundTxAmountSat")) payment.getDouble("refundTxAmountSat").toULong() else null
|
||||||
val paymentType = payment.getString("paymentType")?.let { asPaymentType(it) }!!
|
val paymentType = payment.getString("paymentType")?.let { asPaymentType(it) }!!
|
||||||
val status = payment.getString("status")?.let { asPaymentState(it) }!!
|
val status = payment.getString("status")?.let { asPaymentState(it) }!!
|
||||||
return Payment(
|
return Payment(
|
||||||
@@ -181,6 +183,8 @@ fun asPayment(payment: ReadableMap): Payment? {
|
|||||||
amountSat,
|
amountSat,
|
||||||
feesSat,
|
feesSat,
|
||||||
preimage,
|
preimage,
|
||||||
|
refundTxId,
|
||||||
|
refundTxAmountSat,
|
||||||
paymentType,
|
paymentType,
|
||||||
status,
|
status,
|
||||||
)
|
)
|
||||||
@@ -194,6 +198,8 @@ fun readableMapOf(payment: Payment): ReadableMap {
|
|||||||
"amountSat" to payment.amountSat,
|
"amountSat" to payment.amountSat,
|
||||||
"feesSat" to payment.feesSat,
|
"feesSat" to payment.feesSat,
|
||||||
"preimage" to payment.preimage,
|
"preimage" to payment.preimage,
|
||||||
|
"refundTxId" to payment.refundTxId,
|
||||||
|
"refundTxAmountSat" to payment.refundTxAmountSat,
|
||||||
"paymentType" to payment.paymentType.name.lowercase(),
|
"paymentType" to payment.paymentType.name.lowercase(),
|
||||||
"status" to payment.status.name.lowercase(),
|
"status" to payment.status.name.lowercase(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -198,6 +198,20 @@ enum BreezLiquidSDKMapper {
|
|||||||
}
|
}
|
||||||
preimage = preimageTmp
|
preimage = preimageTmp
|
||||||
}
|
}
|
||||||
|
var refundTxId: String?
|
||||||
|
if hasNonNilKey(data: payment, key: "refundTxId") {
|
||||||
|
guard let refundTxIdTmp = payment["refundTxId"] as? String else {
|
||||||
|
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxId"))
|
||||||
|
}
|
||||||
|
refundTxId = refundTxIdTmp
|
||||||
|
}
|
||||||
|
var refundTxAmountSat: UInt64?
|
||||||
|
if hasNonNilKey(data: payment, key: "refundTxAmountSat") {
|
||||||
|
guard let refundTxAmountSatTmp = payment["refundTxAmountSat"] as? UInt64 else {
|
||||||
|
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "refundTxAmountSat"))
|
||||||
|
}
|
||||||
|
refundTxAmountSat = refundTxAmountSatTmp
|
||||||
|
}
|
||||||
guard let paymentTypeTmp = payment["paymentType"] as? String else {
|
guard let paymentTypeTmp = payment["paymentType"] as? String else {
|
||||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentType", typeName: "Payment"))
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentType", typeName: "Payment"))
|
||||||
}
|
}
|
||||||
@@ -215,6 +229,8 @@ enum BreezLiquidSDKMapper {
|
|||||||
amountSat: amountSat,
|
amountSat: amountSat,
|
||||||
feesSat: feesSat,
|
feesSat: feesSat,
|
||||||
preimage: preimage,
|
preimage: preimage,
|
||||||
|
refundTxId: refundTxId,
|
||||||
|
refundTxAmountSat: refundTxAmountSat,
|
||||||
paymentType: paymentType,
|
paymentType: paymentType,
|
||||||
status: status
|
status: status
|
||||||
)
|
)
|
||||||
@@ -228,6 +244,8 @@ enum BreezLiquidSDKMapper {
|
|||||||
"amountSat": payment.amountSat,
|
"amountSat": payment.amountSat,
|
||||||
"feesSat": payment.feesSat == nil ? nil : payment.feesSat,
|
"feesSat": payment.feesSat == nil ? nil : payment.feesSat,
|
||||||
"preimage": payment.preimage == nil ? nil : payment.preimage,
|
"preimage": payment.preimage == nil ? nil : payment.preimage,
|
||||||
|
"refundTxId": payment.refundTxId == nil ? nil : payment.refundTxId,
|
||||||
|
"refundTxAmountSat": payment.refundTxAmountSat == nil ? nil : payment.refundTxAmountSat,
|
||||||
"paymentType": valueOf(paymentType: payment.paymentType),
|
"paymentType": valueOf(paymentType: payment.paymentType),
|
||||||
"status": valueOf(paymentState: payment.status),
|
"status": valueOf(paymentState: payment.status),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ export interface Payment {
|
|||||||
amountSat: number
|
amountSat: number
|
||||||
feesSat?: number
|
feesSat?: number
|
||||||
preimage?: string
|
preimage?: string
|
||||||
|
refundTxId?: string
|
||||||
|
refundTxAmountSat?: number
|
||||||
paymentType: PaymentType
|
paymentType: PaymentType
|
||||||
status: PaymentState
|
status: PaymentState
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user