Skip to content

Commit 3a87ab4

Browse files
committed
feat: add ability to set payment preimage for spontaneous payments
1 parent d2cadd0 commit 3a87ab4

File tree

6 files changed

+131
-36
lines changed

6 files changed

+131
-36
lines changed

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ interface SpontaneousPayment {
213213
[Throws=NodeError]
214214
PaymentId send_with_custom_tlvs(u64 amount_msat, PublicKey node_id, SendingParameters? sending_parameters, sequence<CustomTlvRecord> custom_tlvs);
215215
[Throws=NodeError]
216+
PaymentId send_with_preimage(u64 amount_msat, PublicKey node_id, PaymentPreimage preimage, SendingParameters? sending_parameters);
217+
[Throws=NodeError]
218+
PaymentId send_with_preimage_and_custom_tlvs(u64 amount_msat, PublicKey node_id, sequence<CustomTlvRecord> custom_tlvs, PaymentPreimage preimage, SendingParameters? sending_parameters);
219+
[Throws=NodeError]
216220
void send_probes(u64 amount_msat, PublicKey node_id);
217221
};
218222

src/payment/bolt11.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ impl Bolt11Payment {
539539
} else {
540540
None
541541
};
542+
542543
let kind = PaymentKind::Bolt11 {
543544
hash: payment_hash,
544545
preimage,

src/payment/spontaneous.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,45 @@ impl SpontaneousPayment {
5757
pub fn send(
5858
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
5959
) -> Result<PaymentId, Error> {
60-
self.send_inner(amount_msat, node_id, sending_parameters, None)
60+
self.send_inner(amount_msat, node_id, sending_parameters, None, None)
6161
}
6262

6363
/// Send a spontaneous payment including a list of custom TLVs.
6464
pub fn send_with_custom_tlvs(
6565
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
6666
custom_tlvs: Vec<CustomTlvRecord>,
6767
) -> Result<PaymentId, Error> {
68-
self.send_inner(amount_msat, node_id, sending_parameters, Some(custom_tlvs))
68+
self.send_inner(amount_msat, node_id, sending_parameters, Some(custom_tlvs), None)
69+
}
70+
71+
/// Send a spontaneous payment with custom preimage
72+
pub fn send_with_preimage(
73+
&self, amount_msat: u64, node_id: PublicKey, preimage: PaymentPreimage,
74+
sending_parameters: Option<SendingParameters>,
75+
) -> Result<PaymentId, Error> {
76+
self.send_inner(amount_msat, node_id, sending_parameters, None, Some(preimage))
77+
}
78+
79+
/// Send a spontaneous payment with custom preimage including a list of custom TLVs.
80+
pub fn send_with_preimage_and_custom_tlvs(
81+
&self, amount_msat: u64, node_id: PublicKey, custom_tlvs: Vec<CustomTlvRecord>,
82+
preimage: PaymentPreimage, sending_parameters: Option<SendingParameters>,
83+
) -> Result<PaymentId, Error> {
84+
self.send_inner(amount_msat, node_id, sending_parameters, Some(custom_tlvs), Some(preimage))
6985
}
7086

7187
fn send_inner(
7288
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
73-
custom_tlvs: Option<Vec<CustomTlvRecord>>,
89+
custom_tlvs: Option<Vec<CustomTlvRecord>>, preimage: Option<PaymentPreimage>,
7490
) -> Result<PaymentId, Error> {
7591
let rt_lock = self.runtime.read().unwrap();
7692
if rt_lock.is_none() {
7793
return Err(Error::NotRunning);
7894
}
7995

80-
let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
96+
let payment_preimage = preimage
97+
.unwrap_or_else(|| PaymentPreimage(self.keys_manager.get_secure_random_bytes()));
98+
8199
let payment_hash = PaymentHash::from(payment_preimage);
82100
let payment_id = PaymentId(payment_hash.0);
83101

src/payment/store.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -221,40 +221,40 @@ impl StorableObject for PaymentDetails {
221221
},
222222
}
223223
}
224-
if let Some(preimage_opt) = update.preimage {
224+
if let Some(preimage_opt) = &update.preimage {
225225
match self.kind {
226226
PaymentKind::Bolt11 { ref mut preimage, .. } => {
227-
update_if_necessary!(*preimage, preimage_opt)
227+
update_if_necessary!(*preimage, preimage_opt.clone())
228228
},
229229
PaymentKind::Bolt11Jit { ref mut preimage, .. } => {
230-
update_if_necessary!(*preimage, preimage_opt)
230+
update_if_necessary!(*preimage, preimage_opt.clone())
231231
},
232232
PaymentKind::Bolt12Offer { ref mut preimage, .. } => {
233-
update_if_necessary!(*preimage, preimage_opt)
233+
update_if_necessary!(*preimage, preimage_opt.clone())
234234
},
235235
PaymentKind::Bolt12Refund { ref mut preimage, .. } => {
236-
update_if_necessary!(*preimage, preimage_opt)
236+
update_if_necessary!(*preimage, preimage_opt.clone())
237237
},
238238
PaymentKind::Spontaneous { ref mut preimage, .. } => {
239-
update_if_necessary!(*preimage, preimage_opt)
239+
update_if_necessary!(*preimage, preimage_opt.clone())
240240
},
241241
_ => {},
242242
}
243243
}
244244

245-
if let Some(secret_opt) = update.secret {
245+
if let Some(secret_opt) = &update.secret {
246246
match self.kind {
247247
PaymentKind::Bolt11 { ref mut secret, .. } => {
248-
update_if_necessary!(*secret, secret_opt)
248+
update_if_necessary!(*secret, *secret_opt)
249249
},
250250
PaymentKind::Bolt11Jit { ref mut secret, .. } => {
251-
update_if_necessary!(*secret, secret_opt)
251+
update_if_necessary!(*secret, *secret_opt)
252252
},
253253
PaymentKind::Bolt12Offer { ref mut secret, .. } => {
254-
update_if_necessary!(*secret, secret_opt)
254+
update_if_necessary!(*secret, *secret_opt)
255255
},
256256
PaymentKind::Bolt12Refund { ref mut secret, .. } => {
257-
update_if_necessary!(*secret, secret_opt)
257+
update_if_necessary!(*secret, *secret_opt)
258258
},
259259
_ => {},
260260
}
@@ -563,13 +563,17 @@ impl PaymentDetailsUpdate {
563563

564564
impl From<&PaymentDetails> for PaymentDetailsUpdate {
565565
fn from(value: &PaymentDetails) -> Self {
566-
let (hash, preimage, secret) = match value.kind {
566+
let (hash, preimage, secret) = match &value.kind {
567567
PaymentKind::Bolt11 { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
568568
PaymentKind::Bolt11Jit { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
569-
PaymentKind::Bolt12Offer { hash, preimage, secret, .. } => (hash, preimage, secret),
570-
PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => (hash, preimage, secret),
571-
PaymentKind::Spontaneous { hash, preimage, .. } => (Some(hash), preimage, None),
572-
_ => (None, None, None),
569+
PaymentKind::Bolt12Offer { hash, preimage, secret, .. } => {
570+
(hash.as_ref(), preimage, secret)
571+
},
572+
PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => {
573+
(hash.as_ref(), preimage, secret)
574+
},
575+
PaymentKind::Spontaneous { hash, preimage, .. } => (Some(hash), preimage, &None),
576+
_ => (None, &None, &None),
573577
};
574578

575579
let confirmation_status = match value.kind {
@@ -586,9 +590,9 @@ impl From<&PaymentDetails> for PaymentDetailsUpdate {
586590

587591
Self {
588592
id: value.id,
589-
hash: Some(hash),
590-
preimage: Some(preimage),
591-
secret: Some(secret),
593+
hash: Some(hash.copied()),
594+
preimage: Some(preimage.clone()),
595+
secret: Some(*secret),
592596
amount_msat: Some(value.amount_msat),
593597
fee_paid_msat: Some(value.fee_paid_msat),
594598
counterparty_skimmed_fee_msat,

tests/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
802802
);
803803
node_b
804804
.bolt11_payment()
805-
.claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage)
805+
.claim_for_hash(manual_payment_hash, claimable_amount_msat, manual_preimage.into())
806806
.unwrap();
807807
expect_payment_received_event!(node_b, claimable_amount_msat);
808808
expect_payment_successful_event!(node_a, Some(manual_payment_id), None);

tests/integration_tests_rust.rs

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use common::{
1919
use ldk_node::config::EsploraSyncConfig;
2020
use ldk_node::liquidity::LSPS2ServiceConfig;
2121
use ldk_node::payment::{
22-
ConfirmationStatus, PaymentDirection, PaymentKind, PaymentStatus, QrPaymentResult,
23-
SendingParameters,
22+
ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus,
23+
QrPaymentResult, SendingParameters,
2424
};
2525
use ldk_node::{Builder, Event, NodeError};
2626

@@ -29,8 +29,10 @@ use lightning::routing::gossip::{NodeAlias, NodeId};
2929
use lightning::util::persist::KVStore;
3030

3131
use lightning_invoice::{Bolt11InvoiceDescription, Description};
32+
use lightning_types::payment::PaymentPreimage;
3233

3334
use bitcoin::address::NetworkUnchecked;
35+
use bitcoin::hashes::sha256::Hash as Sha256Hash;
3436
use bitcoin::hashes::Hash;
3537
use bitcoin::Address;
3638
use bitcoin::Amount;
@@ -817,7 +819,7 @@ fn simple_bolt12_send_receive() {
817819
let node_a_payments =
818820
node_a.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. }));
819821
assert_eq!(node_a_payments.len(), 1);
820-
match node_a_payments.first().unwrap().kind {
822+
match &node_a_payments.first().unwrap().kind {
821823
PaymentKind::Bolt12Offer {
822824
hash,
823825
preimage,
@@ -828,7 +830,7 @@ fn simple_bolt12_send_receive() {
828830
} => {
829831
assert!(hash.is_some());
830832
assert!(preimage.is_some());
831-
assert_eq!(offer_id, offer.id());
833+
assert_eq!(offer_id, &offer.id());
832834
assert_eq!(&expected_quantity, qty);
833835
assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0);
834836
//TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12
@@ -844,12 +846,12 @@ fn simple_bolt12_send_receive() {
844846
let node_b_payments =
845847
node_b.list_payments_with_filter(|p| matches!(p.kind, PaymentKind::Bolt12Offer { .. }));
846848
assert_eq!(node_b_payments.len(), 1);
847-
match node_b_payments.first().unwrap().kind {
849+
match &node_b_payments.first().unwrap().kind {
848850
PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, .. } => {
849851
assert!(hash.is_some());
850852
assert!(preimage.is_some());
851853
assert!(secret.is_some());
852-
assert_eq!(offer_id, offer.id());
854+
assert_eq!(offer_id, &offer.id());
853855
},
854856
_ => {
855857
panic!("Unexpected payment kind");
@@ -883,7 +885,7 @@ fn simple_bolt12_send_receive() {
883885
matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == payment_id
884886
});
885887
assert_eq!(node_a_payments.len(), 1);
886-
let payment_hash = match node_a_payments.first().unwrap().kind {
888+
let payment_hash = match &node_a_payments.first().unwrap().kind {
887889
PaymentKind::Bolt12Offer {
888890
hash,
889891
preimage,
@@ -894,7 +896,7 @@ fn simple_bolt12_send_receive() {
894896
} => {
895897
assert!(hash.is_some());
896898
assert!(preimage.is_some());
897-
assert_eq!(offer_id, offer.id());
899+
assert_eq!(offer_id, &offer.id());
898900
assert_eq!(&expected_quantity, qty);
899901
assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0);
900902
//TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12
@@ -913,12 +915,12 @@ fn simple_bolt12_send_receive() {
913915
matches!(p.kind, PaymentKind::Bolt12Offer { .. }) && p.id == node_b_payment_id
914916
});
915917
assert_eq!(node_b_payments.len(), 1);
916-
match node_b_payments.first().unwrap().kind {
918+
match &node_b_payments.first().unwrap().kind {
917919
PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, .. } => {
918920
assert!(hash.is_some());
919921
assert!(preimage.is_some());
920922
assert!(secret.is_some());
921-
assert_eq!(offer_id, offer.id());
923+
assert_eq!(offer_id, &offer.id());
922924
},
923925
_ => {
924926
panic!("Unexpected payment kind");
@@ -951,7 +953,7 @@ fn simple_bolt12_send_receive() {
951953
matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_b_payment_id
952954
});
953955
assert_eq!(node_b_payments.len(), 1);
954-
match node_b_payments.first().unwrap().kind {
956+
match &node_b_payments.first().unwrap().kind {
955957
PaymentKind::Bolt12Refund {
956958
hash,
957959
preimage,
@@ -977,7 +979,7 @@ fn simple_bolt12_send_receive() {
977979
matches!(p.kind, PaymentKind::Bolt12Refund { .. }) && p.id == node_a_payment_id
978980
});
979981
assert_eq!(node_a_payments.len(), 1);
980-
match node_a_payments.first().unwrap().kind {
982+
match &node_a_payments.first().unwrap().kind {
981983
PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => {
982984
assert!(hash.is_some());
983985
assert!(preimage.is_some());
@@ -1389,3 +1391,69 @@ fn facade_logging() {
13891391
validate_log_entry(entry);
13901392
}
13911393
}
1394+
1395+
#[test]
1396+
fn spontaneous_send_with_custom_preimage() {
1397+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
1398+
let chain_source = TestChainSource::Esplora(&electrsd);
1399+
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
1400+
1401+
let address_a = node_a.onchain_payment().new_address().unwrap();
1402+
let premine_sat = 1_000_000;
1403+
premine_and_distribute_funds(
1404+
&bitcoind.client,
1405+
&electrsd.client,
1406+
vec![address_a],
1407+
Amount::from_sat(premine_sat),
1408+
);
1409+
node_a.sync_wallets().unwrap();
1410+
node_b.sync_wallets().unwrap();
1411+
open_channel(&node_a, &node_b, 500_000, true, &electrsd);
1412+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
1413+
node_a.sync_wallets().unwrap();
1414+
node_b.sync_wallets().unwrap();
1415+
expect_channel_ready_event!(node_a, node_b.node_id());
1416+
expect_channel_ready_event!(node_b, node_a.node_id());
1417+
1418+
let seed = b"test_payment_preimage";
1419+
let bytes: Sha256Hash = Sha256Hash::hash(seed);
1420+
let custom_bytes = bytes.to_byte_array();
1421+
let custom_preimage = PaymentPreimage(custom_bytes);
1422+
1423+
let amount_msat = 100_000;
1424+
let payment_id = node_a
1425+
.spontaneous_payment()
1426+
.send_with_preimage(amount_msat, node_b.node_id(), custom_preimage.clone().into(), None)
1427+
.unwrap();
1428+
1429+
// check payment status and verify stored preimage
1430+
expect_payment_successful_event!(node_a, Some(payment_id), None);
1431+
let details: PaymentDetails =
1432+
node_a.list_payments_with_filter(|p| p.id == payment_id).first().unwrap().clone();
1433+
assert_eq!(details.status, PaymentStatus::Succeeded);
1434+
if let PaymentKind::Spontaneous { preimage: Some(pi), .. } = details.kind {
1435+
assert_eq!(pi, custom_preimage.into());
1436+
} else {
1437+
panic!("Expected a spontaneous PaymentKind with a preimage");
1438+
}
1439+
1440+
// Verify receiver side (node_b)
1441+
expect_payment_received_event!(node_b, amount_msat);
1442+
let receiver_payments: Vec<PaymentDetails> = node_b.list_payments_with_filter(|p| {
1443+
p.direction == PaymentDirection::Inbound
1444+
&& matches!(p.kind, PaymentKind::Spontaneous { .. })
1445+
});
1446+
1447+
assert_eq!(receiver_payments.len(), 1);
1448+
let receiver_details = &receiver_payments[0];
1449+
assert_eq!(receiver_details.status, PaymentStatus::Succeeded);
1450+
assert_eq!(receiver_details.amount_msat, Some(amount_msat));
1451+
assert_eq!(receiver_details.direction, PaymentDirection::Inbound);
1452+
1453+
// Verify receiver also has the same preimage
1454+
if let PaymentKind::Spontaneous { preimage: Some(pi), .. } = &receiver_details.kind {
1455+
assert_eq!(pi, &custom_preimage.into());
1456+
} else {
1457+
panic!("Expected receiver to have spontaneous PaymentKind with preimage");
1458+
}
1459+
}

0 commit comments

Comments
 (0)