Cancel the created swap on timeout (#249)

* Cancel slow swap

* Change Cancelled to TimedOut

* Set Created send swaps to TimedOut on start
This commit is contained in:
Ross Savage
2024-05-31 07:37:44 +02:00
committed by GitHub
parent dd00a0b328
commit 585184a35c
8 changed files with 65 additions and 4 deletions

View File

@@ -132,6 +132,7 @@ enum PaymentState {
"Pending",
"Complete",
"Failed",
"TimedOut",
};
[Enum]

View File

@@ -431,6 +431,7 @@ impl CstDecode<crate::model::PaymentState> for i32 {
1 => crate::model::PaymentState::Pending,
2 => crate::model::PaymentState::Complete,
3 => crate::model::PaymentState::Failed,
4 => crate::model::PaymentState::TimedOut,
_ => unreachable!("Invalid variant for PaymentState: {}", self),
}
}
@@ -858,6 +859,7 @@ impl SseDecode for crate::model::PaymentState {
1 => crate::model::PaymentState::Pending,
2 => crate::model::PaymentState::Complete,
3 => crate::model::PaymentState::Failed,
4 => crate::model::PaymentState::TimedOut,
_ => unreachable!("Invalid variant for PaymentState: {}", inner),
};
}
@@ -1292,6 +1294,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::PaymentState {
Self::Pending => 1.into_dart(),
Self::Complete => 2.into_dart(),
Self::Failed => 3.into_dart(),
Self::TimedOut => 4.into_dart(),
}
}
}
@@ -1789,6 +1792,7 @@ impl SseEncode for crate::model::PaymentState {
crate::model::PaymentState::Pending => 1,
crate::model::PaymentState::Complete => 2,
crate::model::PaymentState::Failed => 3,
crate::model::PaymentState::TimedOut => 4,
_ => {
unimplemented!("");
}

View File

@@ -378,6 +378,12 @@ pub enum PaymentState {
///
/// This is the status when a swap refund was initiated and the refund tx is confirmed.
Failed = 3,
/// ## Send Swaps
///
/// This covers the case when the swap state is still Created and the swap fails to reach the
/// Pending state in time. The TimedOut state indicates the lockup tx should never be broadcast.
TimedOut = 4,
}
impl ToSql for PaymentState {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
@@ -392,6 +398,7 @@ impl FromSql for PaymentState {
1 => Ok(PaymentState::Pending),
2 => Ok(PaymentState::Complete),
3 => Ok(PaymentState::Failed),
4 => Ok(PaymentState::TimedOut),
_ => Err(FromSqlError::OutOfRange(i)),
},
_ => Err(FromSqlError::InvalidType),

View File

@@ -46,6 +46,29 @@ impl Persister {
Ok(())
}
pub(crate) fn update_send_swaps_by_state(
&self,
from_state: PaymentState,
to_state: PaymentState,
) -> Result<()> {
let con = self.get_connection()?;
con.execute(
"UPDATE send_swaps
SET
state = :to_state
WHERE
state = :from_state
",
named_params! {
":from_state": from_state,
":to_state": to_state,
},
)
.map_err(|_| PaymentError::PersistError)?;
Ok(())
}
fn list_send_swaps_query(where_clauses: Vec<String>) -> String {
let mut where_clause_str = String::new();
if !where_clauses.is_empty() {

View File

@@ -133,6 +133,9 @@ impl LiquidSdk {
async fn start(self: &Arc<LiquidSdk>) -> LiquidSdkResult<()> {
let mut is_started = self.is_started.write().await;
let start_ts = Instant::now();
self.persister
.update_send_swaps_by_state(Created, TimedOut)?;
self.start_background_tasks().await?;
*is_started = true;
@@ -265,15 +268,20 @@ impl LiquidSdk {
}),
(Created | Pending, Pending) => Ok(()),
(Complete | Failed, Pending) => Err(PaymentError::Generic {
(Complete | Failed | TimedOut, Pending) => Err(PaymentError::Generic {
err: format!("Cannot transition from {from_state:?} to Pending state"),
}),
(Created | Pending, Complete) => Ok(()),
(Complete | Failed, Complete) => Err(PaymentError::Generic {
(Complete | Failed | TimedOut, Complete) => Err(PaymentError::Generic {
err: format!("Cannot transition from {from_state:?} to Complete state"),
}),
(Created, TimedOut) => Ok(()),
(_, TimedOut) => Err(PaymentError::Generic {
err: format!("Cannot transition from {from_state:?} to TimedOut state"),
}),
(_, Failed) => Ok(()),
}
}
@@ -646,6 +654,7 @@ impl LiquidSdk {
None => pending_send_sat += p.amount_sat,
},
Created => pending_send_sat += p.amount_sat,
TimedOut => {}
},
PaymentType::Receive => match p.status {
Complete => confirmed_received_sat += p.amount_sat,
@@ -1058,7 +1067,11 @@ impl LiquidSdk {
tokio::select! {
_ = &mut timeout_fut => match maybe_payment {
Some(payment) => return Ok(payment),
None => return Err(PaymentError::PaymentTimeout),
None => {
debug!("Timeout occured without payment, set swap to timed out");
self.try_handle_send_swap_update(&swap_id, TimedOut, None, None, None).await?;
return Err(PaymentError::PaymentTimeout)
},
},
event = events_stream.recv() => match event {
Ok(LiquidSdkEvent::PaymentPending { details }) => match details.swap_id.clone() {

View File

@@ -331,6 +331,12 @@ enum PaymentState {
///
/// This is the status when a swap refund was initiated and the refund tx is confirmed.
failed,
/// ## Send Swaps
///
/// This covers the case when the swap state is still Created and the swap fails to reach the
/// Pending state in time. The TimedOut state indicates the lockup tx should never be broadcast.
timedOut,
;
}

View File

@@ -907,6 +907,9 @@ enum BreezLiquidSDKMapper {
case "failed":
return PaymentState.failed
case "timedOut":
return PaymentState.timedOut
default: throw LiquidSdkError.Generic(message: "Invalid variant \(paymentState) for enum PaymentState")
}
}
@@ -924,6 +927,9 @@ enum BreezLiquidSDKMapper {
case .failed:
return "failed"
case .timedOut:
return "timedOut"
}
}

View File

@@ -154,7 +154,8 @@ export enum PaymentState {
CREATED = "created",
PENDING = "pending",
COMPLETE = "complete",
FAILED = "failed"
FAILED = "failed",
TIMED_OUT = "timedOut"
}
export enum PaymentType {