Skip to content

Commit 2807540

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

File tree

3 files changed

+96
-6
lines changed

3 files changed

+96
-6
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/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

tests/integration_tests_rust.rs

Lines changed: 70 additions & 2 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;
@@ -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)