Return pending payment (#245)

* Return pending payment

* Simplify select loop

* Revert dart pubspec

* Add error logging for payment events without swap ids

Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>

---------

Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
This commit is contained in:
Ross Savage
2024-05-30 17:59:35 +02:00
committed by GitHub
parent 9554dcad19
commit dd00a0b328
9 changed files with 139 additions and 47 deletions

View File

@@ -16,6 +16,7 @@ enum PaymentError {
"InvalidPreimage",
"LwkError",
"PairsNotFound",
"PaymentTimeout",
"PersistError",
"Refunded",
"SendError",

View File

@@ -59,6 +59,9 @@ pub enum PaymentError {
#[error("Boltz did not return any pairs from the request")]
PairsNotFound,
#[error("The payment timed out")]
PaymentTimeout,
#[error("Could not store the swap details locally")]
PersistError,

View File

@@ -326,21 +326,22 @@ impl CstDecode<crate::error::PaymentError> for wire_cst_payment_error {
}
}
8 => crate::error::PaymentError::PairsNotFound,
9 => crate::error::PaymentError::PersistError,
10 => {
9 => crate::error::PaymentError::PaymentTimeout,
10 => crate::error::PaymentError::PersistError,
11 => {
let ans = unsafe { self.kind.Refunded };
crate::error::PaymentError::Refunded {
err: ans.err.cst_decode(),
refund_tx_id: ans.refund_tx_id.cst_decode(),
}
}
11 => {
12 => {
let ans = unsafe { self.kind.SendError };
crate::error::PaymentError::SendError {
err: ans.err.cst_decode(),
}
}
12 => {
13 => {
let ans = unsafe { self.kind.SignerError };
crate::error::PaymentError::SignerError {
err: ans.err.cst_decode(),

View File

@@ -821,9 +821,12 @@ impl SseDecode for crate::error::PaymentError {
return crate::error::PaymentError::PairsNotFound;
}
9 => {
return crate::error::PaymentError::PersistError;
return crate::error::PaymentError::PaymentTimeout;
}
10 => {
return crate::error::PaymentError::PersistError;
}
11 => {
let mut var_err = <String>::sse_decode(deserializer);
let mut var_refundTxId = <String>::sse_decode(deserializer);
return crate::error::PaymentError::Refunded {
@@ -831,11 +834,11 @@ impl SseDecode for crate::error::PaymentError {
refund_tx_id: var_refundTxId,
};
}
11 => {
12 => {
let mut var_err = <String>::sse_decode(deserializer);
return crate::error::PaymentError::SendError { err: var_err };
}
12 => {
13 => {
let mut var_err = <String>::sse_decode(deserializer);
return crate::error::PaymentError::SignerError { err: var_err };
}
@@ -1258,18 +1261,19 @@ impl flutter_rust_bridge::IntoDart for crate::error::PaymentError {
[7.into_dart(), err.into_into_dart().into_dart()].into_dart()
}
crate::error::PaymentError::PairsNotFound => [8.into_dart()].into_dart(),
crate::error::PaymentError::PersistError => [9.into_dart()].into_dart(),
crate::error::PaymentError::PaymentTimeout => [9.into_dart()].into_dart(),
crate::error::PaymentError::PersistError => [10.into_dart()].into_dart(),
crate::error::PaymentError::Refunded { err, refund_tx_id } => [
10.into_dart(),
11.into_dart(),
err.into_into_dart().into_dart(),
refund_tx_id.into_into_dart().into_dart(),
]
.into_dart(),
crate::error::PaymentError::SendError { err } => {
[11.into_dart(), err.into_into_dart().into_dart()].into_dart()
[12.into_dart(), err.into_into_dart().into_dart()].into_dart()
}
crate::error::PaymentError::SignerError { err } => {
[12.into_dart(), err.into_into_dart().into_dart()].into_dart()
[13.into_dart(), err.into_into_dart().into_dart()].into_dart()
}
}
}
@@ -1753,20 +1757,23 @@ impl SseEncode for crate::error::PaymentError {
crate::error::PaymentError::PairsNotFound => {
<i32>::sse_encode(8, serializer);
}
crate::error::PaymentError::PersistError => {
crate::error::PaymentError::PaymentTimeout => {
<i32>::sse_encode(9, serializer);
}
crate::error::PaymentError::Refunded { err, refund_tx_id } => {
crate::error::PaymentError::PersistError => {
<i32>::sse_encode(10, serializer);
}
crate::error::PaymentError::Refunded { err, refund_tx_id } => {
<i32>::sse_encode(11, serializer);
<String>::sse_encode(err, serializer);
<String>::sse_encode(refund_tx_id, serializer);
}
crate::error::PaymentError::SendError { err } => {
<i32>::sse_encode(11, serializer);
<i32>::sse_encode(12, serializer);
<String>::sse_encode(err, serializer);
}
crate::error::PaymentError::SignerError { err } => {
<i32>::sse_encode(12, serializer);
<i32>::sse_encode(13, serializer);
<String>::sse_encode(err, serializer);
}
}

View File

@@ -1019,6 +1019,7 @@ impl LiquidSdk {
})?;
let swap_id = &create_response.id;
let accept_zero_conf = create_response.accept_zero_conf;
let create_response_json = SendSwap::from_boltz_struct_to_json(&create_response, swap_id)?;
let payer_amount_sat = req.fees_sat + receiver_amount_sat;
@@ -1035,28 +1036,55 @@ impl LiquidSdk {
refund_private_key: keypair.display_secret().to_string(),
};
self.persister.insert_send_swap(&swap)?;
let mut events_stream = self.event_manager.subscribe();
self.status_stream.track_swap_id(swap_id)?;
self.wait_for_payment(swap.id, accept_zero_conf)
.await
.map(|payment| SendPaymentResponse { payment })
}
async fn wait_for_payment(
&self,
swap_id: String,
accept_zero_conf: bool,
) -> Result<Payment, PaymentError> {
let timeout_fut = tokio::time::sleep(Duration::from_secs(15));
tokio::pin!(timeout_fut);
let mut events_stream = self.event_manager.subscribe();
let mut maybe_payment: Option<Payment> = None;
loop {
match events_stream.recv().await {
Ok(LiquidSdkEvent::PaymentFailed { details }) => match details.swap_id {
Some(id) if id == swap.id => {
return Err(PaymentError::SendError {
err: "Payment failed".to_string(),
})
}
_ => (),
tokio::select! {
_ = &mut timeout_fut => match maybe_payment {
Some(payment) => return Ok(payment),
None => return Err(PaymentError::PaymentTimeout),
},
Ok(LiquidSdkEvent::PaymentSucceed { details }) => match details.swap_id.clone() {
Some(id) if id == swap.id => {
return Ok(SendPaymentResponse { payment: details })
}
_ => (),
},
Ok(event) => debug!("Unhandled event: {event:?}"),
Err(e) => debug!("Received error waiting for event: {e:?}"),
event = events_stream.recv() => match event {
Ok(LiquidSdkEvent::PaymentPending { details }) => match details.swap_id.clone() {
Some(id) if id == swap_id => match accept_zero_conf {
true => {
debug!("Received Send Payment pending event with zero-conf accepted");
return Ok(details)
}
false => {
debug!("Received Send Payment pending event, waiting for confirmation");
maybe_payment = Some(details);
}
},
_ => error!("Received Send Payment pending event for payment without swap ID"),
},
Ok(LiquidSdkEvent::PaymentSucceed { details }) => match details.swap_id.clone()
{
Some(id) if id == swap_id => {
debug!("Received Send Payment succeed event");
return Ok(details);
}
_ => error!("Received Send Payment succeed event for payment without swap ID"),
},
Ok(event) => debug!("Unhandled event: {event:?}"),
Err(e) => debug!("Received error waiting for event: {e:?}"),
}
}
}
}

View File

@@ -36,6 +36,7 @@ sealed class PaymentError with _$PaymentError implements FrbException {
required String err,
}) = PaymentError_LwkError;
const factory PaymentError.pairsNotFound() = PaymentError_PairsNotFound;
const factory PaymentError.paymentTimeout() = PaymentError_PaymentTimeout;
const factory PaymentError.persistError() = PaymentError_PersistError;
const factory PaymentError.refunded({
required String err,

View File

@@ -638,6 +638,47 @@ abstract class PaymentError_PairsNotFound extends PaymentError {
const PaymentError_PairsNotFound._() : super._();
}
/// @nodoc
abstract class _$$PaymentError_PaymentTimeoutImplCopyWith<$Res> {
factory _$$PaymentError_PaymentTimeoutImplCopyWith(
_$PaymentError_PaymentTimeoutImpl value, $Res Function(_$PaymentError_PaymentTimeoutImpl) then) =
__$$PaymentError_PaymentTimeoutImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$PaymentError_PaymentTimeoutImplCopyWithImpl<$Res>
extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_PaymentTimeoutImpl>
implements _$$PaymentError_PaymentTimeoutImplCopyWith<$Res> {
__$$PaymentError_PaymentTimeoutImplCopyWithImpl(
_$PaymentError_PaymentTimeoutImpl _value, $Res Function(_$PaymentError_PaymentTimeoutImpl) _then)
: super(_value, _then);
}
/// @nodoc
class _$PaymentError_PaymentTimeoutImpl extends PaymentError_PaymentTimeout {
const _$PaymentError_PaymentTimeoutImpl() : super._();
@override
String toString() {
return 'PaymentError.paymentTimeout()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$PaymentError_PaymentTimeoutImpl);
}
@override
int get hashCode => runtimeType.hashCode;
}
abstract class PaymentError_PaymentTimeout extends PaymentError {
const factory PaymentError_PaymentTimeout() = _$PaymentError_PaymentTimeoutImpl;
const PaymentError_PaymentTimeout._() : super._();
}
/// @nodoc
abstract class _$$PaymentError_PersistErrorImplCopyWith<$Res> {
factory _$$PaymentError_PersistErrorImplCopyWith(

View File

@@ -800,17 +800,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
case 8:
return PaymentError_PairsNotFound();
case 9:
return PaymentError_PersistError();
return PaymentError_PaymentTimeout();
case 10:
return PaymentError_PersistError();
case 11:
return PaymentError_Refunded(
err: dco_decode_String(raw[1]),
refundTxId: dco_decode_String(raw[2]),
);
case 11:
case 12:
return PaymentError_SendError(
err: dco_decode_String(raw[1]),
);
case 12:
case 13:
return PaymentError_SignerError(
err: dco_decode_String(raw[1]),
);
@@ -1309,15 +1311,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
case 8:
return PaymentError_PairsNotFound();
case 9:
return PaymentError_PersistError();
return PaymentError_PaymentTimeout();
case 10:
return PaymentError_PersistError();
case 11:
var var_err = sse_decode_String(deserializer);
var var_refundTxId = sse_decode_String(deserializer);
return PaymentError_Refunded(err: var_err, refundTxId: var_refundTxId);
case 11:
case 12:
var var_err = sse_decode_String(deserializer);
return PaymentError_SendError(err: var_err);
case 12:
case 13:
var var_err = sse_decode_String(deserializer);
return PaymentError_SignerError(err: var_err);
default:
@@ -1815,17 +1819,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(err, serializer);
case PaymentError_PairsNotFound():
sse_encode_i_32(8, serializer);
case PaymentError_PersistError():
case PaymentError_PaymentTimeout():
sse_encode_i_32(9, serializer);
case PaymentError_Refunded(err: final err, refundTxId: final refundTxId):
case PaymentError_PersistError():
sse_encode_i_32(10, serializer);
case PaymentError_Refunded(err: final err, refundTxId: final refundTxId):
sse_encode_i_32(11, serializer);
sse_encode_String(err, serializer);
sse_encode_String(refundTxId, serializer);
case PaymentError_SendError(err: final err):
sse_encode_i_32(11, serializer);
sse_encode_i_32(12, serializer);
sse_encode_String(err, serializer);
case PaymentError_SignerError(err: final err):
sse_encode_i_32(12, serializer);
sse_encode_i_32(13, serializer);
sse_encode_String(err, serializer);
}
}

View File

@@ -697,27 +697,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
wireObj.tag = 8;
return;
}
if (apiObj is PaymentError_PersistError) {
if (apiObj is PaymentError_PaymentTimeout) {
wireObj.tag = 9;
return;
}
if (apiObj is PaymentError_PersistError) {
wireObj.tag = 10;
return;
}
if (apiObj is PaymentError_Refunded) {
var pre_err = cst_encode_String(apiObj.err);
var pre_refund_tx_id = cst_encode_String(apiObj.refundTxId);
wireObj.tag = 10;
wireObj.tag = 11;
wireObj.kind.Refunded.err = pre_err;
wireObj.kind.Refunded.refund_tx_id = pre_refund_tx_id;
return;
}
if (apiObj is PaymentError_SendError) {
var pre_err = cst_encode_String(apiObj.err);
wireObj.tag = 11;
wireObj.tag = 12;
wireObj.kind.SendError.err = pre_err;
return;
}
if (apiObj is PaymentError_SignerError) {
var pre_err = cst_encode_String(apiObj.err);
wireObj.tag = 12;
wireObj.tag = 13;
wireObj.kind.SignerError.err = pre_err;
return;
}