From: Valentine Wallace Date: Mon, 13 Dec 2021 23:40:16 +0000 (-0500) Subject: inbound_payment: Add utility to get payment preimage given hash/secret X-Git-Tag: v0.0.104~1^2 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=41cfd833f1337c890d3fec0fe18bfa2c6bea75f2;p=rust-lightning inbound_payment: Add utility to get payment preimage given hash/secret User-requested feature --- diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index b727f7874..c51d17f67 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -73,12 +73,14 @@ use core::ops::Deref; use std::time::Instant; mod inbound_payment { + use alloc::string::ToString; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::cmp::fixed_time_eq; use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use chain::keysinterface::{KeyMaterial, KeysInterface, Sign}; use ln::{PaymentHash, PaymentPreimage, PaymentSecret}; + use ln::channelmanager::APIError; use ln::msgs; use ln::msgs::MAX_VALUE_MSAT; use util::chacha20::ChaCha20; @@ -288,6 +290,23 @@ mod inbound_payment { Ok(payment_preimage) } + pub(super) fn get_payment_preimage(payment_hash: PaymentHash, payment_secret: PaymentSecret, keys: &ExpandedKey) -> Result { + let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_secret, keys); + + match Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET) { + Ok(Method::LdkPaymentHash) => { + derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys) + .map_err(|bad_preimage_bytes| APIError::APIMisuseError { + err: format!("Payment hash {} did not match decoded preimage {}", log_bytes!(payment_hash.0), log_bytes!(bad_preimage_bytes)) + }) + }, + Ok(Method::UserPaymentHash) => Err(APIError::APIMisuseError { + err: "Expected payment type to be LdkPaymentHash, instead got UserPaymentHash".to_string() + }), + Err(other) => Err(APIError::APIMisuseError { err: format!("Unknown payment type: {}", other) }), + } + } + fn decrypt_metadata(payment_secret: PaymentSecret, keys: &ExpandedKey) -> ([u8; IV_LEN], [u8; METADATA_LEN]) { let mut iv_bytes = [0; IV_LEN]; let (iv_slice, encrypted_metadata_bytes) = payment_secret.0.split_at(IV_LEN); @@ -5097,6 +5116,14 @@ impl ChannelMana self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs) } + /// Gets an LDK-generated payment preimage from a payment hash and payment secret that were + /// previously returned from [`create_inbound_payment`]. + /// + /// [`create_inbound_payment`]: Self::create_inbound_payment + pub fn get_payment_preimage(&self, payment_hash: PaymentHash, payment_secret: PaymentSecret) -> Result { + inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key) + } + #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] pub fn get_and_clear_pending_events(&self) -> Vec { let events = core::cell::RefCell::new(Vec::new()); diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 82c14645f..960a255e9 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -15,9 +15,10 @@ use chain::{ChannelMonitorUpdateErr, Confirm, Listen, Watch}; use chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS}; use chain::transaction::OutPoint; use ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, PaymentId, PaymentSendFailure}; -use ln::features::InitFeatures; +use ln::features::{InitFeatures, InvoiceFeatures}; use ln::msgs; use ln::msgs::ChannelMessageHandler; +use routing::router::{Payee, get_route}; use util::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider}; use util::test_utils; use util::errors::APIError; @@ -706,3 +707,34 @@ fn test_fulfill_restart_failure() { // it had already considered the payment fulfilled, and now they just got free money. assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); } + +#[test] +fn get_ldk_payment_preimage() { + // Ensure that `ChannelManager::get_payment_preimage` can successfully be used to claim a payment. + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()); + + let amt_msat = 60_000; + let expiry_secs = 60 * 60; + let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs).unwrap(); + + let payee = Payee::from_node_id(nodes[1].node.get_our_node_id()) + .with_features(InvoiceFeatures::known()); + let scorer = test_utils::TestScorer::with_fixed_penalty(0); + let route = get_route( + &nodes[0].node.get_our_node_id(), &payee, &nodes[0].network_graph, + Some(&nodes[0].node.list_usable_channels().iter().collect::>()), + amt_msat, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap(); + let _payment_id = nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap(); + check_added_monitors!(nodes[0], 1); + + // Make sure to use `get_payment_preimage` + let payment_preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap(); + let mut events = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), 1); + pass_along_path(&nodes[0], &[&nodes[1]], amt_msat, payment_hash, Some(payment_secret), events.pop().unwrap(), true, Some(payment_preimage)); + claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, payment_preimage); +}