Skip to content

Commit b180a65

Browse files
authored
Merge pull request #308 from tnull/2024-06-manual-payment-hash-registrations
2 parents 07d020c + a678c5e commit b180a65

File tree

7 files changed

+589
-177
lines changed

7 files changed

+589
-177
lines changed

bindings/ldk_node.udl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,18 @@ interface Bolt11Payment {
100100
[Throws=NodeError]
101101
void send_probes_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat);
102102
[Throws=NodeError]
103+
void claim_for_hash(PaymentHash payment_hash, u64 claimable_amount_msat, PaymentPreimage preimage);
104+
[Throws=NodeError]
105+
void fail_for_hash(PaymentHash payment_hash);
106+
[Throws=NodeError]
103107
Bolt11Invoice receive(u64 amount_msat, [ByRef]string description, u32 expiry_secs);
104108
[Throws=NodeError]
109+
Bolt11Invoice receive_for_hash(u64 amount_msat, [ByRef]string description, u32 expiry_secs, PaymentHash payment_hash);
110+
[Throws=NodeError]
105111
Bolt11Invoice receive_variable_amount([ByRef]string description, u32 expiry_secs);
106112
[Throws=NodeError]
113+
Bolt11Invoice receive_variable_amount_for_hash([ByRef]string description, u32 expiry_secs, PaymentHash payment_hash);
114+
[Throws=NodeError]
107115
Bolt11Invoice receive_via_jit_channel(u64 amount_msat, [ByRef]string description, u32 expiry_secs, u64? max_lsp_fee_limit_msat);
108116
[Throws=NodeError]
109117
Bolt11Invoice receive_variable_amount_via_jit_channel([ByRef]string description, u32 expiry_secs, u64? max_proportional_lsp_fee_limit_ppm_msat);
@@ -222,6 +230,7 @@ interface Event {
222230
PaymentSuccessful(PaymentId? payment_id, PaymentHash payment_hash, u64? fee_paid_msat);
223231
PaymentFailed(PaymentId? payment_id, PaymentHash payment_hash, PaymentFailureReason? reason);
224232
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat);
233+
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline);
225234
ChannelPending(ChannelId channel_id, UserChannelId user_channel_id, ChannelId former_temporary_channel_id, PublicKey counterparty_node_id, OutPoint funding_txo);
226235
ChannelReady(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id);
227236
ChannelClosed(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id, ClosureReason? reason);
@@ -285,6 +294,7 @@ dictionary PaymentDetails {
285294
u64? amount_msat;
286295
PaymentDirection direction;
287296
PaymentStatus status;
297+
u64 latest_update_timestamp;
288298
};
289299

290300
[NonExhaustive]

src/event.rs

Lines changed: 104 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,28 @@ pub enum Event {
8484
/// The value, in thousandths of a satoshi, that has been received.
8585
amount_msat: u64,
8686
},
87+
/// A payment for a previously-registered payment hash has been received.
88+
///
89+
/// This needs to be manually claimed by supplying the correct preimage to [`claim_for_hash`].
90+
///
91+
/// If the the provided parameters don't match the expectations or the preimage can't be
92+
/// retrieved in time, should be failed-back via [`fail_for_hash`].
93+
///
94+
/// Note claiming will necessarily fail after the `claim_deadline` has been reached.
95+
///
96+
/// [`claim_for_hash`]: crate::payment::Bolt11Payment::claim_for_hash
97+
/// [`fail_for_hash`]: crate::payment::Bolt11Payment::fail_for_hash
98+
PaymentClaimable {
99+
/// A local identifier used to track the payment.
100+
payment_id: PaymentId,
101+
/// The hash of the payment.
102+
payment_hash: PaymentHash,
103+
/// The value, in thousandths of a satoshi, that is claimable.
104+
claimable_amount_msat: u64,
105+
/// The block height at which this payment will be failed back and will no longer be
106+
/// eligible for claiming.
107+
claim_deadline: Option<u32>,
108+
},
87109
/// A channel has been created and is pending confirmation on-chain.
88110
ChannelPending {
89111
/// The `channel_id` of the channel.
@@ -156,6 +178,12 @@ impl_writeable_tlv_based_enum!(Event,
156178
(1, counterparty_node_id, option),
157179
(2, user_channel_id, required),
158180
(3, reason, upgradable_option),
181+
},
182+
(6, PaymentClaimable) => {
183+
(0, payment_hash, required),
184+
(2, payment_id, required),
185+
(4, claimable_amount_msat, required),
186+
(6, claim_deadline, option),
159187
};
160188
);
161189

@@ -434,12 +462,31 @@ where
434462
receiver_node_id: _,
435463
via_channel_id: _,
436464
via_user_channel_id: _,
437-
claim_deadline: _,
465+
claim_deadline,
438466
onion_fields: _,
439467
counterparty_skimmed_fee_msat,
440468
} => {
441469
let payment_id = PaymentId(payment_hash.0);
442470
if let Some(info) = self.payment_store.get(&payment_id) {
471+
if info.direction == PaymentDirection::Outbound {
472+
log_info!(
473+
self.logger,
474+
"Refused inbound payment with ID {}: circular payments are unsupported.",
475+
payment_id
476+
);
477+
self.channel_manager.fail_htlc_backwards(&payment_hash);
478+
479+
let update = PaymentDetailsUpdate {
480+
status: Some(PaymentStatus::Failed),
481+
..PaymentDetailsUpdate::new(payment_id)
482+
};
483+
self.payment_store.update(&update).unwrap_or_else(|e| {
484+
log_error!(self.logger, "Failed to access payment store: {}", e);
485+
panic!("Failed to access payment store");
486+
});
487+
return;
488+
}
489+
443490
if info.status == PaymentStatus::Succeeded
444491
|| matches!(info.kind, PaymentKind::Spontaneous { .. })
445492
{
@@ -500,6 +547,38 @@ where
500547
});
501548
return;
502549
}
550+
551+
// If this is known by the store but ChannelManager doesn't know the preimage,
552+
// the payment has been registered via `_for_hash` variants and needs to be manually claimed via
553+
// user interaction.
554+
match info.kind {
555+
PaymentKind::Bolt11 { preimage, .. } => {
556+
if purpose.preimage().is_none() {
557+
debug_assert!(
558+
preimage.is_none(),
559+
"We would have registered the preimage if we knew"
560+
);
561+
562+
self.event_queue
563+
.add_event(Event::PaymentClaimable {
564+
payment_id,
565+
payment_hash,
566+
claimable_amount_msat: amount_msat,
567+
claim_deadline,
568+
})
569+
.unwrap_or_else(|e| {
570+
log_error!(
571+
self.logger,
572+
"Failed to push to event queue: {}",
573+
e
574+
);
575+
panic!("Failed to push to event queue");
576+
});
577+
return;
578+
}
579+
},
580+
_ => {},
581+
}
503582
}
504583

505584
log_info!(
@@ -519,19 +598,21 @@ where
519598
..
520599
} => {
521600
let offer_id = payment_context.offer_id;
522-
let payment = PaymentDetails {
523-
id: payment_id,
524-
kind: PaymentKind::Bolt12Offer {
525-
hash: Some(payment_hash),
526-
preimage: payment_preimage,
527-
secret: Some(payment_secret),
528-
offer_id,
529-
},
530-
amount_msat: Some(amount_msat),
531-
direction: PaymentDirection::Inbound,
532-
status: PaymentStatus::Pending,
601+
let kind = PaymentKind::Bolt12Offer {
602+
hash: Some(payment_hash),
603+
preimage: payment_preimage,
604+
secret: Some(payment_secret),
605+
offer_id,
533606
};
534607

608+
let payment = PaymentDetails::new(
609+
payment_id,
610+
kind,
611+
Some(amount_msat),
612+
PaymentDirection::Inbound,
613+
PaymentStatus::Pending,
614+
);
615+
535616
match self.payment_store.insert(payment) {
536617
Ok(false) => (),
537618
Ok(true) => {
@@ -559,17 +640,19 @@ where
559640
},
560641
PaymentPurpose::SpontaneousPayment(preimage) => {
561642
// Since it's spontaneous, we insert it now into our store.
562-
let payment = PaymentDetails {
563-
id: payment_id,
564-
kind: PaymentKind::Spontaneous {
565-
hash: payment_hash,
566-
preimage: Some(preimage),
567-
},
568-
amount_msat: Some(amount_msat),
569-
direction: PaymentDirection::Inbound,
570-
status: PaymentStatus::Pending,
643+
let kind = PaymentKind::Spontaneous {
644+
hash: payment_hash,
645+
preimage: Some(preimage),
571646
};
572647

648+
let payment = PaymentDetails::new(
649+
payment_id,
650+
kind,
651+
Some(amount_msat),
652+
PaymentDirection::Inbound,
653+
PaymentStatus::Pending,
654+
);
655+
573656
match self.payment_store.insert(payment) {
574657
Ok(false) => (),
575658
Ok(true) => {

0 commit comments

Comments
 (0)