Skip to content

Commit da29cac

Browse files
committed
Add invoice constructor with custom payment hash
1 parent 5e577cb commit da29cac

File tree

1 file changed

+97
-1
lines changed

1 file changed

+97
-1
lines changed

lightning-invoice/src/utils.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,82 @@ where
332332
)
333333
}
334334

335+
pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
336+
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
337+
network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
338+
invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, payment_secret: PaymentSecret
339+
) -> Result<Invoice, SignOrCreationError<()>>
340+
where
341+
M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
342+
T::Target: BroadcasterInterface,
343+
K::Target: KeysInterface,
344+
F::Target: FeeEstimator,
345+
L::Target: Logger,
346+
{
347+
_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
348+
channelmanager, keys_manager, logger, network, amt_msat,
349+
InvoiceDescription::Direct(
350+
&Description::new(description).map_err(SignOrCreationError::CreationError)?,
351+
),
352+
duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret
353+
)
354+
}
355+
356+
fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
357+
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
358+
network: Currency, amt_msat: Option<u64>, description: InvoiceDescription, duration_since_epoch: Duration,
359+
invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, payment_secret: PaymentSecret
360+
) -> Result<Invoice, SignOrCreationError<()>>
361+
where
362+
M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
363+
T::Target: BroadcasterInterface,
364+
K::Target: KeysInterface,
365+
F::Target: FeeEstimator,
366+
L::Target: Logger,
367+
{
368+
let our_node_pubkey = channelmanager.get_our_node_id();
369+
let channels = channelmanager.list_channels();
370+
371+
log_trace!(logger, "Creating invoice with payment hash {}", log_bytes!(payment_hash.0));
372+
373+
let invoice = match description {
374+
InvoiceDescription::Direct(description) => {
375+
InvoiceBuilder::new(network).description(description.0.clone())
376+
}
377+
InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
378+
};
379+
380+
let mut invoice = invoice
381+
.duration_since_epoch(duration_since_epoch)
382+
.payee_pub_key(our_node_pubkey)
383+
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
384+
.payment_secret(payment_secret)
385+
.basic_mpp()
386+
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into())
387+
.expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
388+
if let Some(amt) = amt_msat {
389+
invoice = invoice.amount_milli_satoshis(amt);
390+
}
391+
392+
let route_hints = filter_channels(channels, amt_msat, &logger);
393+
for hint in route_hints {
394+
invoice = invoice.private_route(hint);
395+
}
396+
397+
let raw_invoice = match invoice.build_raw() {
398+
Ok(inv) => inv,
399+
Err(e) => return Err(SignOrCreationError::CreationError(e))
400+
};
401+
let hrp_str = raw_invoice.hrp.to_string();
402+
let hrp_bytes = hrp_str.as_bytes();
403+
let data_without_signature = raw_invoice.data.to_base32();
404+
let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node));
405+
match signed_raw_invoice {
406+
Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
407+
Err(e) => Err(SignOrCreationError::SignError(e))
408+
}
409+
}
410+
335411
fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
336412
channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
337413
network: Currency, amt_msat: Option<u64>, description: InvoiceDescription,
@@ -567,7 +643,7 @@ where
567643
mod test {
568644
use core::time::Duration;
569645
use crate::{Currency, Description, InvoiceDescription};
570-
use bitcoin_hashes::Hash;
646+
use bitcoin_hashes::{Hash, sha256};
571647
use bitcoin_hashes::sha256::Hash as Sha256;
572648
use lightning::chain::keysinterface::PhantomKeysManager;
573649
use lightning::ln::{PaymentPreimage, PaymentHash};
@@ -665,6 +741,26 @@ mod test {
665741
assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
666742
}
667743

744+
745+
#[test]
746+
fn test_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash() {
747+
let chanmon_cfgs = create_chanmon_cfgs(2);
748+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
749+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
750+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
751+
let payment_hash = PaymentHash([0; 32]);
752+
let payment_secret = &nodes[1].node.create_inbound_payment_for_hash(payment_hash, Some(10_000), 3600);
753+
let invoice = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
754+
&nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
755+
Some(10_000), "test".to_string(), Duration::from_secs(1234567), 3600,
756+
payment_hash, payment_secret.unwrap()
757+
).unwrap();
758+
assert_eq!(invoice.amount_pico_btc(), Some(100_000));
759+
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
760+
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
761+
assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&payment_hash.0[..]).unwrap());
762+
}
763+
668764
#[test]
669765
fn test_hints_includes_single_channels_to_nodes() {
670766
let chanmon_cfgs = create_chanmon_cfgs(3);

0 commit comments

Comments
 (0)