@@ -22,11 +22,12 @@ use bitcoin::blockdata::transaction::Transaction;
22
22
use bitcoin::blockdata::constants::{genesis_block, ChainHash};
23
23
use bitcoin::network::constants::Network;
24
24
25
- use bitcoin::hashes::Hash;
25
+ use bitcoin::hashes::{Hash, HashEngine};
26
+ use bitcoin::hashes::hmac::{Hmac, HmacEngine};
26
27
use bitcoin::hashes::sha256::Hash as Sha256;
27
28
use bitcoin::hash_types::{BlockHash, Txid};
28
29
29
- use bitcoin::secp256k1::{SecretKey, PublicKey};
30
+ use bitcoin::secp256k1::{PublicKey, Scalar, SecretKey };
30
31
use bitcoin::secp256k1::Secp256k1;
31
32
use bitcoin::{LockTime, secp256k1, Sequence};
32
33
@@ -49,7 +50,7 @@ use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Pa
49
50
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
50
51
use crate::ln::msgs;
51
52
use crate::ln::onion_utils;
52
- use crate::ln::onion_utils::HTLCFailReason;
53
+ use crate::ln::onion_utils::{ HTLCFailReason, INVALID_ONION_BLINDING} ;
53
54
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
54
55
#[cfg(test)]
55
56
use crate::ln::outbound_payment;
@@ -112,6 +113,9 @@ pub(super) enum PendingHTLCRouting {
112
113
phantom_shared_secret: Option<[u8; 32]>,
113
114
/// See [`RecipientOnionFields::custom_tlvs`] for more info.
114
115
custom_tlvs: Vec<(u64, Vec<u8>)>,
116
+ /// `Some` if this corresponds to a blinded HTLC. Used for failing backwards properly. We use an
117
+ /// option to maintain compatibility with LDK versions prior to 0.0.117.
118
+ blinded: Option<()>,
115
119
},
116
120
ReceiveKeysend {
117
121
/// This was added in 0.0.116 and will break deserialization on downgrades.
@@ -121,6 +125,8 @@ pub(super) enum PendingHTLCRouting {
121
125
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
122
126
/// See [`RecipientOnionFields::custom_tlvs`] for more info.
123
127
custom_tlvs: Vec<(u64, Vec<u8>)>,
128
+ /// See [`Self::Receive::blinded`] docs.
129
+ blinded: Option<()>,
124
130
},
125
131
}
126
132
@@ -2758,18 +2764,34 @@ where
2758
2764
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
2759
2765
counterparty_skimmed_fee_msat: Option<u64>,
2760
2766
) -> Result<PendingHTLCInfo, InboundOnionErr> {
2761
- let (payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2767
+ let (
2768
+ payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata, blinded
2769
+ ) = match hop_data {
2762
2770
msgs::InboundOnionPayload::Receive {
2763
2771
payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
2764
2772
} =>
2765
- (payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
2766
- msgs::InboundOnionPayload::Forward { .. } =>
2773
+ (payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, None),
2774
+ msgs::InboundOnionPayload::BlindedReceive {
2775
+ amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, ..
2776
+ } => {
2777
+ let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
2778
+ let blinded = if intro_node_blinding_point.is_none() { Some(()) } else { None };
2779
+ (Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None, blinded)
2780
+ }
2781
+ msgs::InboundOnionPayload::Forward { .. } => {
2767
2782
return Err(InboundOnionErr {
2768
2783
err_code: 0x4000|22,
2769
2784
err_data: Vec::new(),
2770
2785
msg: "Got non final data with an HMAC of 0",
2771
- }),
2772
- _ => todo!()
2786
+ })
2787
+ },
2788
+ msgs::InboundOnionPayload::BlindedForward { .. } => {
2789
+ return Err(InboundOnionErr {
2790
+ msg: "Got blinded non final data with an HMAC of 0",
2791
+ err_code: INVALID_ONION_BLINDING,
2792
+ err_data: Vec::new(), // filled in in construct_pending_htlc_status
2793
+ })
2794
+ },
2773
2795
};
2774
2796
// final_incorrect_cltv_expiry
2775
2797
if outgoing_cltv_value > cltv_expiry {
@@ -2834,6 +2856,7 @@ where
2834
2856
payment_metadata,
2835
2857
incoming_cltv_expiry: outgoing_cltv_value,
2836
2858
custom_tlvs,
2859
+ blinded,
2837
2860
}
2838
2861
} else if let Some(data) = payment_data {
2839
2862
PendingHTLCRouting::Receive {
@@ -2842,6 +2865,7 @@ where
2842
2865
incoming_cltv_expiry: outgoing_cltv_value,
2843
2866
phantom_shared_secret,
2844
2867
custom_tlvs,
2868
+ blinded,
2845
2869
}
2846
2870
} else {
2847
2871
return Err(InboundOnionErr {
@@ -2882,8 +2906,15 @@ where
2882
2906
return_malformed_err!("invalid ephemeral pubkey", 0x8000 | 0x4000 | 6);
2883
2907
}
2884
2908
2909
+ let blinded_node_id_tweak = msg.blinding_point.map(|bp| {
2910
+ let blinded_tlvs_ss = self.node_signer.ecdh(
2911
+ Recipient::Node, &bp, None).unwrap().secret_bytes();
2912
+ let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
2913
+ hmac.input(blinded_tlvs_ss.as_ref());
2914
+ Scalar::from_be_bytes(Hmac::from_engine(hmac).into_inner()).unwrap()
2915
+ });
2885
2916
let shared_secret = self.node_signer.ecdh(
2886
- Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), None
2917
+ Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), blinded_node_id_tweak.as_ref()
2887
2918
).unwrap().secret_bytes();
2888
2919
2889
2920
if msg.onion_routing_packet.version != 0 {
@@ -4042,14 +4073,18 @@ where
4042
4073
}
4043
4074
}) => {
4044
4075
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
4045
- PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
4076
+ PendingHTLCRouting::Receive {
4077
+ payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs, ..
4078
+ } => {
4046
4079
let _legacy_hop_data = Some(payment_data.clone());
4047
4080
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
4048
4081
payment_metadata, custom_tlvs };
4049
4082
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
4050
4083
Some(payment_data), phantom_shared_secret, onion_fields)
4051
4084
},
4052
- PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
4085
+ PendingHTLCRouting::ReceiveKeysend {
4086
+ payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs, ..
4087
+ } => {
4053
4088
let onion_fields = RecipientOnionFields {
4054
4089
payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
4055
4090
payment_metadata,
@@ -7917,6 +7952,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
7917
7952
(1, phantom_shared_secret, option),
7918
7953
(2, incoming_cltv_expiry, required),
7919
7954
(3, payment_metadata, option),
7955
+ (4, blinded, option),
7920
7956
(5, custom_tlvs, optional_vec),
7921
7957
},
7922
7958
(2, ReceiveKeysend) => {
@@ -7925,6 +7961,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
7925
7961
(3, payment_metadata, option),
7926
7962
(4, payment_data, option), // Added in 0.0.116
7927
7963
(5, custom_tlvs, optional_vec),
7964
+ (6, blinded, option),
7928
7965
},
7929
7966
;);
7930
7967
0 commit comments