diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index f20f93c789c..39ddbf393b0 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -572,7 +572,7 @@ fn get_payment_secret_hash(dest: &ChanMan, payment_ctr: &mut u64) -> (PaymentSec *payment_ctr += 1; let payment_hash = PaymentHash(Sha256::hash(&[*payment_ctr as u8]).to_byte_array()); let payment_secret = dest - .create_inbound_payment_for_hash(payment_hash, None, 3600, None) + .create_inbound_payment_for_hash(payment_hash, None, 3600, None, None) .expect("create_inbound_payment_for_hash failed"); (payment_secret, payment_hash) } diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index c1d7982e5e4..35894c8d895 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -841,7 +841,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()); // Note that this may fail - our hashes may collide and we'll end up trying to // double-register the same payment_hash. - let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1, None); + let _ = channelmanager.create_inbound_payment_for_hash(payment_hash, None, 1, None, None); }, 9 => { for payment in payments_received.drain(..) { diff --git a/lightning-liquidity/tests/lsps2_integration_tests.rs b/lightning-liquidity/tests/lsps2_integration_tests.rs index fbff2eae4cd..92e6b33ebb6 100644 --- a/lightning-liquidity/tests/lsps2_integration_tests.rs +++ b/lightning-liquidity/tests/lsps2_integration_tests.rs @@ -122,7 +122,7 @@ fn create_jit_invoice( let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2; let (payment_hash, payment_secret) = node .node - .create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta)) + .create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None) .map_err(|e| { log_error!(node.logger, "Failed to register inbound payment: {:?}", e); })?; diff --git a/lightning/src/ln/bolt11_payment_tests.rs b/lightning/src/ln/bolt11_payment_tests.rs index 8c2ac155ce7..733e26d0f1b 100644 --- a/lightning/src/ln/bolt11_payment_tests.rs +++ b/lightning/src/ln/bolt11_payment_tests.rs @@ -31,7 +31,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() { let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42]; let (payment_hash, payment_secret) = - nodes[1].node.create_inbound_payment(None, 7200, None).unwrap(); + nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap(); let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let invoice = InvoiceBuilder::new(Currency::Bitcoin) @@ -98,7 +98,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() { let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42]; let (payment_hash, payment_secret) = - nodes[1].node.create_inbound_payment(None, 7200, None).unwrap(); + nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap(); let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let invoice = InvoiceBuilder::new(Currency::Bitcoin) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 660f61f0f57..47626e60cd5 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8481,6 +8481,7 @@ impl< let verify_res = inbound_payment::verify( payment_hash, &payment_data, + onion_fields.payment_metadata.as_deref(), self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger, @@ -14123,7 +14124,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ ) -> Result> { let Bolt11InvoiceParameters { amount_msats, description, invoice_expiry_delta_secs, min_final_cltv_expiry_delta, - payment_hash, + payment_hash, payment_metadata, } = params; let currency = @@ -14156,6 +14157,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ payment_hash, amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32), min_final_cltv_expiry_delta, + payment_metadata.as_deref(), ) .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; (payment_hash, payment_secret) @@ -14165,6 +14167,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ .create_inbound_payment( amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32), min_final_cltv_expiry_delta, + payment_metadata.as_deref(), ) .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))? }, @@ -14203,7 +14206,11 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ invoice = invoice.private_route(hint); } - let raw_invoice = invoice.build_raw().map_err(|e| SignOrCreationError::CreationError(e))?; + let raw_invoice = if let Some(payment_metadata) = payment_metadata { + invoice.payment_metadata(payment_metadata).build_raw() + } else { + invoice.build_raw() + }.map_err(|e| SignOrCreationError::CreationError(e))?; let signature = self.node_signer.sign_invoice(&raw_invoice, Recipient::Node); raw_invoice @@ -14282,6 +14289,14 @@ pub struct Bolt11InvoiceParameters { /// involving another protocol where the payment hash is also involved outside the scope of /// lightning. pub payment_hash: Option, + + /// The `payment_metadata` to include in the invoice. This is provided back to us in the payment + /// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via + /// [`Event::PaymentClaimable::onion_fields`]. + /// + /// Note that because it is exposed to the sender in the invoice you should consider encrypting + /// it. It is committed to, however, so cannot be modified by the sender. + pub payment_metadata: Option>, } impl Default for Bolt11InvoiceParameters { @@ -14292,6 +14307,7 @@ impl Default for Bolt11InvoiceParameters { invoice_expiry_delta_secs: None, min_final_cltv_expiry_delta: None, payment_hash: None, + payment_metadata: None, } } } @@ -14776,7 +14792,7 @@ impl< refund, self.list_usable_channels(), |amount_msats, relative_expiry| { - self.create_inbound_payment(Some(amount_msats), relative_expiry, None) + self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None) .map_err(|()| Bolt12SemanticError::InvalidAmount) }, )?; @@ -14885,7 +14901,7 @@ impl< /// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash pub fn create_inbound_payment( &self, min_value_msat: Option, invoice_expiry_delta_secs: u32, - min_final_cltv_expiry_delta: Option, + min_final_cltv_expiry_delta: Option, payment_metadata: Option<&[u8]>, ) -> Result<(PaymentHash, PaymentSecret), ()> { inbound_payment::create( &self.inbound_payment_key, @@ -14894,6 +14910,7 @@ impl< &self.entropy_source, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, min_final_cltv_expiry_delta, + payment_metadata, ) } @@ -14946,6 +14963,7 @@ impl< pub fn create_inbound_payment_for_hash( &self, payment_hash: PaymentHash, min_value_msat: Option, invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option, + payment_metadata: Option<&[u8]>, ) -> Result { inbound_payment::create_from_hash( &self.inbound_payment_key, @@ -14954,6 +14972,7 @@ impl< invoice_expiry_delta_secs, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, min_final_cltv_expiry, + payment_metadata, ) } @@ -14963,9 +14982,15 @@ impl< /// [`create_inbound_payment`]: Self::create_inbound_payment pub fn get_payment_preimage( &self, payment_hash: PaymentHash, payment_secret: PaymentSecret, + payment_metadata: Option<&[u8]>, ) -> Result { let expanded_key = &self.inbound_payment_key; - inbound_payment::get_payment_preimage(payment_hash, payment_secret, expanded_key) + inbound_payment::get_payment_preimage( + payment_hash, + payment_secret, + payment_metadata, + expanded_key, + ) } /// [`BlindedMessagePath`]s for an async recipient to communicate with this node and interactively @@ -17017,7 +17042,8 @@ impl< self.create_inbound_payment( Some(amount_msats), relative_expiry, - None + None, + None, ).map_err(|_| Bolt12SemanticError::InvalidAmount) }; @@ -21275,7 +21301,7 @@ mod tests { // payment verification fails as expected. let mut bad_payment_hash = payment_hash.clone(); bad_payment_hash.0[0] += 1; - match inbound_payment::verify(bad_payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) { + match inbound_payment::verify(bad_payment_hash, &payment_data, None, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) { Ok(_) => panic!("Unexpected ok"), Err(()) => { nodes[0].logger.assert_log_contains("lightning::ln::inbound_payment", "Failing HTLC with user-generated payment_hash", 1); @@ -21283,7 +21309,7 @@ mod tests { } // Check that using the original payment hash succeeds. - assert!(inbound_payment::verify(payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok()); + assert!(inbound_payment::verify(payment_hash, &payment_data, None, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok()); } fn check_not_connected_to_peer_error( @@ -21956,7 +21982,7 @@ pub mod bench { payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes()); payment_count += 1; let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()); - let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap(); + let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap(); $node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000), PaymentId(payment_hash.0), diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 84cdf785da5..3e5b7e3296f 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2818,6 +2818,7 @@ pub fn get_payment_preimage_hash( min_value_msat, 7200, min_final_cltv_expiry_delta, + None, ) .unwrap(); (payment_preimage, payment_hash, payment_secret) diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 7ed46922d8a..894497b927d 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -293,8 +293,10 @@ pub fn test_duplicate_htlc_different_direction_onchain() { let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 900_000); let (route, _, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_value_msats); - let node_a_payment_secret = - nodes[0].node.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap(); + let node_a_payment_secret = nodes[0] + .node + .create_inbound_payment_for_hash(payment_hash, None, 7200, None, None) + .unwrap(); send_along_route_with_secret( &nodes[1], route, @@ -4156,8 +4158,10 @@ pub fn test_duplicate_payment_hash_one_failure_one_success() { let (our_payment_preimage, dup_payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3]], 900_000); - let payment_secret = - nodes[4].node.create_inbound_payment_for_hash(dup_payment_hash, None, 7200, None).unwrap(); + let payment_secret = nodes[4] + .node + .create_inbound_payment_for_hash(dup_payment_hash, None, 7200, None, None) + .unwrap(); let payment_params = PaymentParameters::from_node_id(node_e_id, TEST_FINAL_CLTV) .with_bolt11_features(nodes[4].node.bolt11_invoice_features()) .unwrap(); @@ -4424,13 +4428,13 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno // 2nd HTLC (not added - smaller than dust limit + HTLC tx fee): let path_5: &[&[_]] = &[&[&nodes[2], &nodes[3], &nodes[5]]]; let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_1, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_1, None, 7200, None, None).unwrap(); let route = route_to_5.clone(); send_along_route_with_secret(&nodes[1], route, path_5, dust_limit_msat, hash_1, payment_secret); // 3rd HTLC (not added - smaller than dust limit + HTLC tx fee): let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_2, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_2, None, 7200, None, None).unwrap(); let route = route_to_5; send_along_route_with_secret(&nodes[1], route, path_5, dust_limit_msat, hash_2, payment_secret); @@ -4443,12 +4447,12 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno // 6th HTLC: let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_3, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_3, None, 7200, None, None).unwrap(); send_along_route_with_secret(&nodes[1], route.clone(), path_5, 1000000, hash_3, payment_secret); // 7th HTLC: let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_4, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_4, None, 7200, None, None).unwrap(); send_along_route_with_secret(&nodes[1], route, path_5, 1000000, hash_4, payment_secret); // 8th HTLC: @@ -4457,7 +4461,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno // 9th HTLC (not added - smaller than dust limit + HTLC tx fee): let (route, _, _, _) = get_route_and_payment_hash!(nodes[1], nodes[5], dust_limit_msat); let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_5, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_5, None, 7200, None, None).unwrap(); send_along_route_with_secret(&nodes[1], route, path_5, dust_limit_msat, hash_5, payment_secret); // 10th HTLC (not added - smaller than dust limit + HTLC tx fee): @@ -4466,7 +4470,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno // 11th HTLC: let (route, _, _, _) = get_route_and_payment_hash!(nodes[1], nodes[5], 1000000); let payment_secret = - nodes[5].node.create_inbound_payment_for_hash(hash_6, None, 7200, None).unwrap(); + nodes[5].node.create_inbound_payment_for_hash(hash_6, None, 7200, None, None).unwrap(); send_along_route_with_secret(&nodes[1], route, path_5, 1000000, hash_6, payment_secret); // Double-check that six of the new HTLC were added @@ -6061,7 +6065,7 @@ pub fn test_check_htlc_underpaying() { let (_, our_payment_hash, _) = get_payment_preimage_hash(&nodes[0], None, None); let our_payment_secret = nodes[1] .node - .create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None) + .create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None, None) .unwrap(); let onion = RecipientOnionFields::secret_only(our_payment_secret, route.get_total_amount()); let id = PaymentId(our_payment_hash.0); @@ -7229,7 +7233,7 @@ pub fn test_preimage_storage() { { let (payment_hash, payment_secret) = - nodes[1].node.create_inbound_payment(Some(100_000), 7200, None).unwrap(); + nodes[1].node.create_inbound_payment(Some(100_000), 7200, None, None).unwrap(); let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000); let onion = RecipientOnionFields::secret_only(payment_secret, 100_000); let id = PaymentId(payment_hash.0); @@ -7274,7 +7278,7 @@ pub fn test_bad_secret_hash() { let random_hash = PaymentHash([42; 32]); let random_secret = PaymentSecret([43; 32]); let (our_payment_hash, our_payment_secret) = - nodes[1].node.create_inbound_payment(Some(100_000), 2, None).unwrap(); + nodes[1].node.create_inbound_payment(Some(100_000), 2, None, None).unwrap(); let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000); // All the below cases should end up being handled exactly identically, so we macro the @@ -9482,9 +9486,13 @@ fn do_payment_with_custom_min_final_cltv_expiry(valid_delta: bool, use_user_hash } else { let (hash, payment_secret) = nodes[1] .node - .create_inbound_payment(Some(recv_value), 7200, Some(min_cltv_expiry_delta)) + .create_inbound_payment(Some(recv_value), 7200, Some(min_cltv_expiry_delta), None) .unwrap(); - (hash, nodes[1].node.get_payment_preimage(hash, payment_secret).unwrap(), payment_secret) + ( + hash, + nodes[1].node.get_payment_preimage(hash, payment_secret, None).unwrap(), + payment_secret, + ) }; let route = get_route!(nodes[0], payment_parameters, recv_value).unwrap(); let onion = RecipientOnionFields::secret_only(payment_secret, recv_value); diff --git a/lightning/src/ln/inbound_payment.rs b/lightning/src/ln/inbound_payment.rs index d70a20eaf44..ec5ac92c20e 100644 --- a/lightning/src/ln/inbound_payment.rs +++ b/lightning/src/ln/inbound_payment.rs @@ -28,10 +28,10 @@ use crate::util::logger::Logger; use crate::prelude::*; pub(crate) const IV_LEN: usize = 16; -const METADATA_LEN: usize = 16; -const METADATA_KEY_LEN: usize = 32; +const INFO_LEN: usize = 16; +const INFO_KEY_LEN: usize = 32; const AMT_MSAT_LEN: usize = 8; -// Used to shift the payment type bits to take up the top 3 bits of the metadata bytes, or to +// Used to shift the payment type bits to take up the top 3 bits of the info bytes, or to // retrieve said payment type bits. const METHOD_TYPE_OFFSET: usize = 5; @@ -40,20 +40,20 @@ const METHOD_TYPE_OFFSET: usize = 5; /// [`NodeSigner::get_expanded_key`]: crate::sign::NodeSigner::get_expanded_key #[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)] pub struct ExpandedKey { - /// The key used to encrypt the bytes containing the payment metadata (i.e. the amount and + /// The key used to encrypt the bytes containing the payment info (i.e. the amount and /// expiry, included for payment verification on decryption). - metadata_key: [u8; 32], - /// The key used to authenticate an LDK-provided payment hash and metadata as previously + info_key: [u8; 32], + /// The key used to authenticate an LDK-provided payment hash and info as previously /// registered with LDK. ldk_pmt_hash_key: [u8; 32], - /// The key used to authenticate a user-provided payment hash and metadata as previously + /// The key used to authenticate a user-provided payment hash and info as previously /// registered with LDK. user_pmt_hash_key: [u8; 32], /// The base key used to derive signing keys and authenticate messages for BOLT 12 Offers. offers_base_key: [u8; 32], /// The key used to encrypt message metadata for BOLT 12 Offers. offers_encryption_key: [u8; 32], - /// The key used to authenticate spontaneous payments' metadata as previously registered with LDK + /// The key used to authenticate spontaneous payments' info as previously registered with LDK /// for inclusion in a blinded path. spontaneous_pmt_key: [u8; 32], /// The key used to authenticate phantom-node-shared blinded paths as generated by us. Note @@ -68,7 +68,7 @@ impl ExpandedKey { /// It is recommended to cache this value and not regenerate it for each new inbound payment. pub fn new(key_material: [u8; 32]) -> ExpandedKey { let ( - metadata_key, + info_key, ldk_pmt_hash_key, user_pmt_hash_key, offers_base_key, @@ -77,7 +77,7 @@ impl ExpandedKey { phantom_node_blinded_path_key, ) = hkdf_extract_expand_7x(b"LDK Inbound Payment Key Expansion", &key_material); Self { - metadata_key, + info_key, ldk_pmt_hash_key, user_pmt_hash_key, offers_base_key, @@ -128,7 +128,7 @@ impl Method { } } -fn min_final_cltv_expiry_delta_from_metadata(bytes: [u8; METADATA_LEN]) -> u16 { +fn min_final_cltv_expiry_delta_from_info(bytes: [u8; INFO_LEN]) -> u16 { let expiry_bytes = &bytes[AMT_MSAT_LEN..]; u16::from_be_bytes([expiry_bytes[0], expiry_bytes[1]]) } @@ -150,8 +150,9 @@ fn min_final_cltv_expiry_delta_from_metadata(bytes: [u8; METADATA_LEN]) -> u16 { pub fn create( keys: &ExpandedKey, min_value_msat: Option, invoice_expiry_delta_secs: u32, entropy_source: &ES, current_time: u64, min_final_cltv_expiry_delta: Option, + payment_metadata: Option<&[u8]>, ) -> Result<(PaymentHash, PaymentSecret), ()> { - let metadata_bytes = construct_metadata_bytes( + let info_bytes = construct_info_bytes( min_value_msat, if min_final_cltv_expiry_delta.is_some() { Method::LdkPaymentHashCustomFinalCltv @@ -169,11 +170,14 @@ pub fn create( let mut hmac = HmacEngine::::new(&keys.ldk_pmt_hash_key); hmac.input(&iv_bytes); - hmac.input(&metadata_bytes); + hmac.input(&info_bytes); + if let Some(metadata) = payment_metadata { + hmac.input(metadata); + } let payment_preimage_bytes = Hmac::from_engine(hmac).to_byte_array(); let ldk_pmt_hash = PaymentHash(Sha256::hash(&payment_preimage_bytes).to_byte_array()); - let payment_secret = construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key); + let payment_secret = construct_payment_secret(&iv_bytes, &info_bytes, &keys.info_key); Ok((ldk_pmt_hash, payment_secret)) } @@ -190,8 +194,9 @@ pub fn create( pub fn create_from_hash( keys: &ExpandedKey, min_value_msat: Option, payment_hash: PaymentHash, invoice_expiry_delta_secs: u32, current_time: u64, min_final_cltv_expiry_delta: Option, + payment_metadata: Option<&[u8]>, ) -> Result { - let metadata_bytes = construct_metadata_bytes( + let info_bytes = construct_info_bytes( min_value_msat, if min_final_cltv_expiry_delta.is_some() { Method::UserPaymentHashCustomFinalCltv @@ -204,21 +209,24 @@ pub fn create_from_hash( )?; let mut hmac = HmacEngine::::new(&keys.user_pmt_hash_key); - hmac.input(&metadata_bytes); + hmac.input(&info_bytes); hmac.input(&payment_hash.0); + if let Some(metadata) = payment_metadata { + hmac.input(metadata); + } let hmac_bytes = Hmac::from_engine(hmac).to_byte_array(); let mut iv_bytes = [0 as u8; IV_LEN]; iv_bytes.copy_from_slice(&hmac_bytes[..IV_LEN]); - Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key)) + Ok(construct_payment_secret(&iv_bytes, &info_bytes, &keys.info_key)) } pub(crate) fn create_for_spontaneous_payment( keys: &ExpandedKey, min_value_msat: Option, invoice_expiry_delta_secs: u32, current_time: u64, min_final_cltv_expiry_delta: Option, ) -> Result { - let metadata_bytes = construct_metadata_bytes( + let info_bytes = construct_info_bytes( min_value_msat, Method::SpontaneousPayment, invoice_expiry_delta_secs, @@ -227,13 +235,13 @@ pub(crate) fn create_for_spontaneous_payment( )?; let mut hmac = HmacEngine::::new(&keys.spontaneous_pmt_key); - hmac.input(&metadata_bytes); + hmac.input(&info_bytes); let hmac_bytes = Hmac::from_engine(hmac).to_byte_array(); let mut iv_bytes = [0 as u8; IV_LEN]; iv_bytes.copy_from_slice(&hmac_bytes[..IV_LEN]); - Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key)) + Ok(construct_payment_secret(&iv_bytes, &info_bytes, &keys.info_key)) } pub(crate) fn calculate_absolute_expiry( @@ -247,10 +255,10 @@ pub(crate) fn calculate_absolute_expiry( highest_seen_timestamp + invoice_expiry_delta_secs as u64 + 7200 } -fn construct_metadata_bytes( +fn construct_info_bytes( min_value_msat: Option, payment_type: Method, invoice_expiry_delta_secs: u32, highest_seen_timestamp: u64, min_final_cltv_expiry_delta: Option, -) -> Result<[u8; METADATA_LEN], ()> { +) -> Result<[u8; INFO_LEN], ()> { if min_value_msat.is_some() && min_value_msat.unwrap() > MAX_VALUE_MSAT { return Err(()); } @@ -285,41 +293,35 @@ fn construct_metadata_bytes( expiry_bytes[1] |= bytes[1]; } - let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN]; + let mut info_bytes: [u8; INFO_LEN] = [0; INFO_LEN]; - metadata_bytes[..AMT_MSAT_LEN].copy_from_slice(&min_amt_msat_bytes); - metadata_bytes[AMT_MSAT_LEN..].copy_from_slice(&expiry_bytes); + info_bytes[..AMT_MSAT_LEN].copy_from_slice(&min_amt_msat_bytes); + info_bytes[AMT_MSAT_LEN..].copy_from_slice(&expiry_bytes); - Ok(metadata_bytes) + Ok(info_bytes) } fn construct_payment_secret( - iv_bytes: &[u8; IV_LEN], metadata_bytes: &[u8; METADATA_LEN], - metadata_key: &[u8; METADATA_KEY_LEN], + iv_bytes: &[u8; IV_LEN], info_bytes: &[u8; INFO_LEN], info_key: &[u8; INFO_KEY_LEN], ) -> PaymentSecret { let mut payment_secret_bytes: [u8; 32] = [0; 32]; - let (iv_slice, encrypted_metadata_slice) = payment_secret_bytes.split_at_mut(IV_LEN); + let (iv_slice, encrypted_info_slice) = payment_secret_bytes.split_at_mut(IV_LEN); iv_slice.copy_from_slice(iv_bytes); - ChaCha20::encrypt_single_block( - metadata_key, - iv_bytes, - encrypted_metadata_slice, - metadata_bytes, - ); + ChaCha20::encrypt_single_block(info_key, iv_bytes, encrypted_info_slice, info_bytes); PaymentSecret(payment_secret_bytes) } /// Check that an inbound payment's `payment_data` field is sane. /// /// LDK does not store any data for pending inbound payments. Instead, we construct our payment -/// secret (and, if supplied by LDK, our payment preimage) to include encrypted metadata about the -/// payment. +/// secret (and, if supplied by LDK, our payment preimage) to include encrypted information about +/// the payment. /// -/// For payments without a custom `min_final_cltv_expiry_delta`, the metadata is constructed as: +/// For payments without a custom `min_final_cltv_expiry_delta`, the payment info is: /// payment method (3 bits) || payment amount (8 bytes - 3 bits) || expiry (8 bytes) /// -/// For payments including a custom `min_final_cltv_expiry_delta`, the metadata is constructed as: +/// For payments including a custom `min_final_cltv_expiry_delta`, the payment info is: /// payment method (3 bits) || payment amount (8 bytes - 3 bits) || min_final_cltv_expiry_delta (2 bytes) || expiry (6 bytes) /// /// In both cases the result is then encrypted using a key derived from [`NodeSigner::get_expanded_key`]. @@ -332,14 +334,14 @@ fn construct_payment_secret( /// method is called, then the payment method bits mentioned above are represented internally as /// [`Method::LdkPaymentHash`]. If the latter, [`Method::UserPaymentHash`]. /// -/// For the former method, the payment preimage is constructed as an HMAC of payment metadata and -/// random bytes. Because the payment secret is also encoded with these random bytes and metadata -/// (with the metadata encrypted with a block cipher), we're able to authenticate the preimage on +/// For the former method, the payment preimage is constructed as an HMAC of payment info and +/// random bytes. Because the payment secret is also encoded with these random bytes and info +/// (with the info encrypted with a block cipher), we're able to authenticate the preimage on /// payment receipt. /// /// For the latter, the payment secret instead contains an HMAC of the user-provided payment hash -/// and payment metadata (encrypted with a block cipher), allowing us to authenticate the payment -/// hash and metadata on payment receipt. +/// and payment info (encrypted with a block cipher), allowing us to authenticate the payment +/// hash and info on payment receipt. /// /// See [`ExpandedKey`] docs for more info on the individual keys used. /// @@ -347,17 +349,17 @@ fn construct_payment_secret( /// [`create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment /// [`create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash pub(super) fn verify( - payment_hash: PaymentHash, payment_data: &msgs::FinalOnionHopData, highest_seen_timestamp: u64, - keys: &ExpandedKey, logger: &L, + payment_hash: PaymentHash, payment_data: &msgs::FinalOnionHopData, + payment_metadata: Option<&[u8]>, highest_seen_timestamp: u64, keys: &ExpandedKey, + logger: &L, ) -> Result<(Option, Option), ()> { - let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_data.payment_secret, keys); + let (iv_bytes, info_bytes) = decrypt_info(payment_data.payment_secret, keys); - let payment_type_res = - Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET); + let payment_type_res = Method::from_bits((info_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET); let mut amt_msat_bytes = [0; AMT_MSAT_LEN]; - let mut expiry_bytes = [0; METADATA_LEN - AMT_MSAT_LEN]; - amt_msat_bytes.copy_from_slice(&metadata_bytes[..AMT_MSAT_LEN]); - expiry_bytes.copy_from_slice(&metadata_bytes[AMT_MSAT_LEN..]); + let mut expiry_bytes = [0; INFO_LEN - AMT_MSAT_LEN]; + amt_msat_bytes.copy_from_slice(&info_bytes[..AMT_MSAT_LEN]); + expiry_bytes.copy_from_slice(&info_bytes[AMT_MSAT_LEN..]); // Zero out the bits reserved to indicate the payment type. amt_msat_bytes[0] &= 0b00011111; let mut min_final_cltv_expiry_delta = None; @@ -368,8 +370,11 @@ pub(super) fn verify( match payment_type_res { Ok(Method::UserPaymentHash) | Ok(Method::UserPaymentHashCustomFinalCltv) => { let mut hmac = HmacEngine::::new(&keys.user_pmt_hash_key); - hmac.input(&metadata_bytes[..]); + hmac.input(&info_bytes[..]); hmac.input(&payment_hash.0); + if let Some(metadata) = payment_metadata { + hmac.input(metadata); + } if !fixed_time_eq( &iv_bytes, &Hmac::from_engine(hmac).to_byte_array().split_at_mut(IV_LEN).0, @@ -383,7 +388,13 @@ pub(super) fn verify( } }, Ok(Method::LdkPaymentHash) | Ok(Method::LdkPaymentHashCustomFinalCltv) => { - match derive_ldk_payment_preimage(payment_hash, &iv_bytes, &metadata_bytes, keys) { + match derive_ldk_payment_preimage( + payment_hash, + &iv_bytes, + &info_bytes, + payment_metadata, + keys, + ) { Ok(preimage) => payment_preimage = Some(preimage), Err(bad_preimage_bytes) => { log_trace!( @@ -398,7 +409,7 @@ pub(super) fn verify( }, Ok(Method::SpontaneousPayment) => { let mut hmac = HmacEngine::::new(&keys.spontaneous_pmt_key); - hmac.input(&metadata_bytes[..]); + hmac.input(&info_bytes[..]); if !fixed_time_eq( &iv_bytes, &Hmac::from_engine(hmac).to_byte_array().split_at_mut(IV_LEN).0, @@ -420,8 +431,7 @@ pub(super) fn verify( match payment_type_res { Ok(Method::UserPaymentHashCustomFinalCltv) | Ok(Method::LdkPaymentHashCustomFinalCltv) => { - min_final_cltv_expiry_delta = - Some(min_final_cltv_expiry_delta_from_metadata(metadata_bytes)); + min_final_cltv_expiry_delta = Some(min_final_cltv_expiry_delta_from_info(info_bytes)); // Zero out first two bytes of expiry reserved for `min_final_cltv_expiry_delta`. expiry_bytes[0] &= 0; expiry_bytes[1] &= 0; @@ -446,21 +456,27 @@ pub(super) fn verify( } pub(super) fn get_payment_preimage( - payment_hash: PaymentHash, payment_secret: PaymentSecret, keys: &ExpandedKey, + payment_hash: PaymentHash, payment_secret: PaymentSecret, payment_metadata: Option<&[u8]>, + keys: &ExpandedKey, ) -> Result { - let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_secret, keys); + let (iv_bytes, info_bytes) = decrypt_info(payment_secret, keys); - match Method::from_bits((metadata_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET) { + match Method::from_bits((info_bytes[0] & 0b1110_0000) >> METHOD_TYPE_OFFSET) { Ok(Method::LdkPaymentHash) | Ok(Method::LdkPaymentHashCustomFinalCltv) => { - 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 {}", - &payment_hash, - log_bytes!(bad_preimage_bytes) - ), - }, + derive_ldk_payment_preimage( + payment_hash, + &iv_bytes, + &info_bytes, + payment_metadata, + keys, ) + .map_err(|bad_preimage_bytes| APIError::APIMisuseError { + err: format!( + "Payment hash {} did not match decoded preimage {}", + &payment_hash, + log_bytes!(bad_preimage_bytes) + ), + }) }, Ok(Method::UserPaymentHash) | Ok(Method::UserPaymentHashCustomFinalCltv) => { Err(APIError::APIMisuseError { @@ -477,33 +493,36 @@ pub(super) fn get_payment_preimage( } } -fn decrypt_metadata( +fn decrypt_info( payment_secret: PaymentSecret, keys: &ExpandedKey, -) -> ([u8; IV_LEN], [u8; METADATA_LEN]) { +) -> ([u8; IV_LEN], [u8; INFO_LEN]) { let mut iv_bytes = [0; IV_LEN]; - let (iv_slice, encrypted_metadata_bytes) = payment_secret.0.split_at(IV_LEN); + let (iv_slice, encrypted_info_bytes) = payment_secret.0.split_at(IV_LEN); iv_bytes.copy_from_slice(iv_slice); - let mut metadata_bytes: [u8; METADATA_LEN] = [0; METADATA_LEN]; + let mut info_bytes: [u8; INFO_LEN] = [0; INFO_LEN]; ChaCha20::encrypt_single_block( - &keys.metadata_key, + &keys.info_key, &iv_bytes, - &mut metadata_bytes, - encrypted_metadata_bytes, + &mut info_bytes, + encrypted_info_bytes, ); - (iv_bytes, metadata_bytes) + (iv_bytes, info_bytes) } // Errors if the payment preimage doesn't match `payment_hash`. Returns the bad preimage bytes in // this case. fn derive_ldk_payment_preimage( - payment_hash: PaymentHash, iv_bytes: &[u8; IV_LEN], metadata_bytes: &[u8; METADATA_LEN], - keys: &ExpandedKey, + payment_hash: PaymentHash, iv_bytes: &[u8; IV_LEN], info_bytes: &[u8; INFO_LEN], + payment_metadata: Option<&[u8]>, keys: &ExpandedKey, ) -> Result { let mut hmac = HmacEngine::::new(&keys.ldk_pmt_hash_key); hmac.input(iv_bytes); - hmac.input(metadata_bytes); + hmac.input(info_bytes); + if let Some(metadata) = payment_metadata { + hmac.input(metadata); + } let decoded_payment_preimage = Hmac::from_engine(hmac).to_byte_array(); if !fixed_time_eq(&payment_hash.0, &Sha256::hash(&decoded_payment_preimage).to_byte_array()) { return Err(decoded_payment_preimage); diff --git a/lightning/src/ln/invoice_utils.rs b/lightning/src/ln/invoice_utils.rs index 63ad110bba0..564203bf524 100644 --- a/lightning/src/ln/invoice_utils.rs +++ b/lightning/src/ln/invoice_utils.rs @@ -191,6 +191,7 @@ fn _create_phantom_invoice( invoice_expiry_delta_secs, duration_since_epoch.as_secs(), min_final_cltv_expiry_delta, + None, ) .map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; (payment_hash, payment_secret) @@ -202,6 +203,7 @@ fn _create_phantom_invoice( &entropy_source, duration_since_epoch.as_secs(), min_final_cltv_expiry_delta, + None, ) .map_err(|_| SignOrCreationError::CreationError(CreationError::InvalidAmount))? }; @@ -670,7 +672,8 @@ mod test { let (payment_hash, payment_secret) = (invoice.payment_hash(), *invoice.payment_secret()); - let preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap(); + let preimage = + nodes[1].node.get_payment_preimage(payment_hash, payment_secret, None).unwrap(); // Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is // available. @@ -1255,7 +1258,7 @@ mod test { let payment_preimage = if user_generated_pmt_hash { user_payment_preimage } else { - nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap() + nodes[1].node.get_payment_preimage(payment_hash, payment_secret, None).unwrap() }; assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64); @@ -1363,7 +1366,7 @@ mod test { let payment_amt = 20_000; let (payment_hash, _payment_secret) = - nodes[1].node.create_inbound_payment(Some(payment_amt), 3600, None).unwrap(); + nodes[1].node.create_inbound_payment(Some(payment_amt), 3600, None, None).unwrap(); let route_hints = vec![nodes[1].node.get_phantom_route_hints(), nodes[2].node.get_phantom_route_hints()]; diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 45640d3486d..2ede955de7b 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -32,7 +32,7 @@ use crate::routing::router::{ }; use crate::sign::NodeSigner; use crate::types::features::BlindedHopFeatures; -use crate::types::payment::PaymentSecret; +use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::util::errors::APIError; use crate::util::ser::Writeable; use crate::util::test_utils; @@ -80,9 +80,29 @@ fn large_payment_metadata() { - final_payload_len_without_metadata; let mut payment_metadata = vec![42; max_metadata_len]; + let mut counter = 42; + macro_rules! get_payment_hash { + ($node: expr, $metadata: expr) => { { + let payment_preimage = PaymentPreimage([counter; 32]); + counter += 1; + let payment_hash: PaymentHash = payment_preimage.into(); + let payment_secret = $node + .node + .create_inbound_payment_for_hash( + payment_hash, + Some(amt_msat), + 7200, + None, + Some($metadata), + ) + .unwrap(); + (payment_hash, payment_preimage, payment_secret) + } }; + } + // Check that the maximum-size metadata is sendable. - let (mut route_0_1, payment_hash, payment_preimage, payment_secret) = - get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat); + let (payment_hash, payment_preimage, payment_secret) = get_payment_hash!(nodes[1], &payment_metadata); + let (mut route_0_1, ..) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat); let mut max_sized_onion = RecipientOnionFields { payment_secret: Some(payment_secret), payment_metadata: Some(payment_metadata.clone()), @@ -112,14 +132,17 @@ fn large_payment_metadata() { // Check that the payment parameter for max path length will prevent us from routing past our // next-hop peer given the payment_metadata size. - let (mut route_0_2, payment_hash_2, payment_preimage_2, payment_secret_2) = - get_route_and_payment_hash!(&nodes[0], &nodes[2], amt_msat); + + let (payment_hash_2, payment_preimage_2, payment_secret_2) = + get_payment_hash!(nodes[2], &max_sized_onion.payment_metadata.as_ref().unwrap()); + let (mut route_0_2, ..) = get_route_and_payment_hash!(&nodes[0], &nodes[2], amt_msat); let mut route_params_0_2 = route_0_2.route_params.clone().unwrap(); route_params_0_2.payment_params.max_path_length = 1; nodes[0].router.expect_find_route_query(route_params_0_2); + max_sized_onion.payment_secret = Some(payment_secret_2); let id = PaymentId(payment_hash_2.0); - let route_params = route_0_2.route_params.clone().unwrap(); + let mut route_params = route_0_2.route_params.clone().unwrap(); let err = nodes[0] .node .send_payment(payment_hash_2, max_sized_onion.clone(), id, route_params, Retry::Attempts(0)) @@ -130,6 +153,9 @@ fn large_payment_metadata() { let mut too_large_onion = max_sized_onion.clone(); too_large_onion.payment_metadata.as_mut().map(|mut md| md.push(42)); too_large_onion.total_mpp_amount_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; + let (payment_hash_2, payment_preimage_2, payment_secret_2) = + get_payment_hash!(nodes[2], &too_large_onion.payment_metadata.as_ref().unwrap()); + too_large_onion.payment_secret = Some(payment_secret_2); // First confirm we'll fail to create the onion packet directly. let secp_ctx = Secp256k1::signing_only(); @@ -164,6 +190,8 @@ fn large_payment_metadata() { // If we remove enough payment_metadata bytes to allow for 2 hops, we're now able to send to // nodes[2]. let two_hop_metadata = vec![42; max_metadata_len - INTERMED_PAYLOAD_LEN_ESTIMATE]; + let (payment_hash_2, payment_preimage_2, payment_secret_2) = + get_payment_hash!(nodes[2], &two_hop_metadata); let mut onion_allowing_2_hops = RecipientOnionFields { payment_secret: Some(payment_secret_2), payment_metadata: Some(two_hop_metadata.clone()), diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index be52459a872..d7bef35ed06 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -1506,7 +1506,7 @@ fn get_ldk_payment_preimage() { 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, None).unwrap(); + nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs, None, None).unwrap(); let payment_params = PaymentParameters::from_node_id(node_b_id, TEST_FINAL_CLTV) .with_bolt11_features(nodes[1].node.bolt11_invoice_features()) @@ -1519,7 +1519,8 @@ fn get_ldk_payment_preimage() { check_added_monitors(&nodes[0], 1); // Make sure to use `get_payment_preimage` - let preimage = Some(nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap()); + let preimage = + Some(nodes[1].node.get_payment_preimage(payment_hash, payment_secret, None).unwrap()); let mut events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); let event = events.pop().unwrap(); @@ -2240,7 +2241,7 @@ fn do_test_intercepted_payment(test: InterceptTest) { let route = get_route(&nodes[0], &route_params).unwrap(); let (hash, payment_secret) = - nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap(); + nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None, None).unwrap(); let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(hash.0); nodes[0].node.send_payment_with_route(route.clone(), hash, onion, id).unwrap(); @@ -2349,7 +2350,8 @@ fn do_test_intercepted_payment(test: InterceptTest) { do_commitment_signed_dance(&nodes[2], &nodes[1], commitment, false, true); expect_and_process_pending_htlcs(&nodes[2], false); - let preimage = Some(nodes[2].node.get_payment_preimage(hash, payment_secret).unwrap()); + let preimage = + Some(nodes[2].node.get_payment_preimage(hash, payment_secret, None).unwrap()); expect_payment_claimable!(&nodes[2], hash, payment_secret, amt_msat, preimage, node_c_id); let path: &[&[_]] = &[&[&nodes[1], &nodes[2]]]; @@ -2475,7 +2477,7 @@ fn do_accept_underpaying_htlcs_config(num_mpp_parts: usize) { .unwrap(); let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat); let (payment_hash, payment_secret) = - nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap(); + nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None, None).unwrap(); let onion = RecipientOnionFields::secret_only(payment_secret, amt_msat); let id = PaymentId(payment_hash.0); @@ -2531,7 +2533,7 @@ fn do_accept_underpaying_htlcs_config(num_mpp_parts: usize) { // Claim the payment and check that the skimmed fee is as expected. let payment_preimage = - nodes[2].node.get_payment_preimage(payment_hash, payment_secret).unwrap(); + nodes[2].node.get_payment_preimage(payment_hash, payment_secret, None).unwrap(); let events = nodes[2].node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { @@ -4807,10 +4809,20 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) { // Pay more than half of each channel's max, requiring MPP let amt_msat = 750_000_000; - let (payment_preimage, payment_hash, payment_secret) = - get_payment_preimage_hash(&nodes[3], Some(amt_msat), None); - let payment_id = PaymentId(payment_hash.0); let payment_metadata = vec![44, 49, 52, 142]; + let payment_preimage = PaymentPreimage([42; 32]); + let payment_hash: PaymentHash = payment_preimage.into(); + let payment_secret = nodes[3] + .node + .create_inbound_payment_for_hash( + payment_hash, + Some(amt_msat), + 7200, + None, + Some(&payment_metadata), + ) + .unwrap(); + let payment_id = PaymentId(payment_hash.0); let payment_params = PaymentParameters::from_node_id(node_d_id, TEST_FINAL_CLTV) .with_bolt11_features(nodes[1].node.bolt11_invoice_features()) diff --git a/pending_changelog/matt-commit-to-metadata.txt b/pending_changelog/matt-commit-to-metadata.txt new file mode 100644 index 00000000000..400af0fe952 --- /dev/null +++ b/pending_changelog/matt-commit-to-metadata.txt @@ -0,0 +1,6 @@ +# Backwards compat + * Payment metadata is now comitted to in the HMAC used to build payment secrets. + As such, any existing BOLT 11 invoices issued with payment metadata will be + implicitly invalidated on upgrade and any BOLT 11 invoices issued with payment + metadata will be invalidated on downgrade. If this is problematic for you + please reach out.