use crate::ln::msgs;
use crate::ln::wire::Encode;
use crate::routing::gossip::NetworkUpdate;
-use crate::routing::router::{Path, RouteHop};
+use crate::routing::router::{BlindedTail, Path, RouteHop};
+use crate::sign::NodeSigner;
use crate::util::chacha20::{ChaCha20, ChaChaReader};
use crate::util::errors::{self, APIError};
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter};
let mut cur_value_msat = 0u64;
let mut cur_cltv = starting_htlc_offset;
let mut last_short_channel_id = 0;
- let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(path.hops.len());
+ let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(
+ path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len())
+ );
for (idx, hop) in path.hops.iter().rev().enumerate() {
// First hop gets special values so that it can check, on receipt, that everything is
// the intended recipient).
let value_msat = if cur_value_msat == 0 { hop.fee_msat } else { cur_value_msat };
let cltv = if cur_cltv == starting_htlc_offset { hop.cltv_expiry_delta + starting_htlc_offset } else { cur_cltv };
- res.insert(0, if idx == 0 {
- msgs::OutboundOnionPayload::Receive {
- payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
- Some(msgs::FinalOnionHopData {
- payment_secret: secret,
- total_msat,
- })
- } else { None },
- payment_metadata: recipient_onion.payment_metadata.take(),
- keysend_preimage: *keysend_preimage,
- custom_tlvs: recipient_onion.custom_tlvs.clone(),
- amt_msat: value_msat,
- outgoing_cltv_value: cltv,
+ if idx == 0 {
+ if let Some(BlindedTail {
+ blinding_point, hops, final_value_msat, excess_final_cltv_expiry_delta, ..
+ }) = &path.blinded_tail {
+ let mut blinding_point = Some(*blinding_point);
+ for (i, blinded_hop) in hops.iter().enumerate() {
+ if i == hops.len() - 1 {
+ cur_value_msat += final_value_msat;
+ cur_cltv += excess_final_cltv_expiry_delta;
+ res.push(msgs::OutboundOnionPayload::BlindedReceive {
+ amt_msat: *final_value_msat,
+ total_msat,
+ outgoing_cltv_value: cltv,
+ encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
+ intro_node_blinding_point: blinding_point.take(),
+ });
+ } else {
+ res.push(msgs::OutboundOnionPayload::BlindedForward {
+ encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
+ intro_node_blinding_point: blinding_point.take(),
+ });
+ }
+ }
+ } else {
+ res.push(msgs::OutboundOnionPayload::Receive {
+ payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
+ Some(msgs::FinalOnionHopData {
+ payment_secret: secret,
+ total_msat,
+ })
+ } else { None },
+ payment_metadata: recipient_onion.payment_metadata.take(),
+ keysend_preimage: *keysend_preimage,
+ custom_tlvs: recipient_onion.custom_tlvs.clone(),
+ amt_msat: value_msat,
+ outgoing_cltv_value: cltv,
+ });
}
} else {
- msgs::OutboundOnionPayload::Forward {
+ res.insert(0, msgs::OutboundOnionPayload::Forward {
short_channel_id: last_short_channel_id,
amt_to_forward: value_msat,
outgoing_cltv_value: cltv,
- }
- });
+ });
+ }
cur_value_msat += hop.fee_msat;
if cur_value_msat >= 21000000 * 100000000 * 1000 {
return Err(APIError::InvalidRoute{err: "Channel fees overflowed?".to_owned()});
},
}
-pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
- match decode_next_hop(shared_secret, hop_data, hmac_bytes, Some(payment_hash), ()) {
+pub(crate) fn decode_next_payment_hop<NS: Deref>(
+ shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash,
+ node_signer: &NS,
+) -> Result<Hop, OnionDecodeErr> where NS::Target: NodeSigner {
+ match decode_next_hop(shared_secret, hop_data, hmac_bytes, Some(payment_hash), node_signer) {
Ok((next_hop_data, None)) => Ok(Hop::Receive(next_hop_data)),
Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => {
Ok(Hop::Forward {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
- short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
+ short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
- short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
+ short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
- short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
+ short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
- short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
+ short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
- short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
+ short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0, maybe_announced_channel: true, // We fill in the payloads manually instead of generating them from RouteHops.
},
], blinded_tail: None }],
route_params: None,