Skip to content

Commit

Permalink
Trampoline payload construction method
Browse files Browse the repository at this point in the history
  • Loading branch information
arik-so committed Nov 21, 2024
1 parent a85aca9 commit 33804c3
Showing 1 changed file with 117 additions and 1 deletion.
118 changes: 117 additions & 1 deletion lightning/src/ln/onion_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
use crate::ln::msgs;
use crate::offers::invoice_request::InvoiceRequest;
use crate::routing::gossip::NetworkUpdate;
use crate::routing::router::{Path, RouteHop, RouteParameters};
use crate::routing::router::{Path, RouteHop, RouteParameters, TrampolineHop};
use crate::sign::NodeSigner;
use crate::types::features::{ChannelFeatures, NodeFeatures};
use crate::types::payment::{PaymentHash, PaymentPreimage};
Expand Down Expand Up @@ -176,6 +176,122 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
Ok(res)
}

fn build_trampoline_onion_payloads<'a>(
path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,
starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>,
) -> Result<(Vec<msgs::OutboundTrampolinePayload<'a>>, u64, u32), APIError> {
let mut res: Vec<msgs::OutboundTrampolinePayload> = Vec::with_capacity(
path.trampoline_hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()),
);
let blinded_tail = path.blinded_tail.as_ref().ok_or(APIError::InvalidRoute {
err: "Routes using Trampoline must terminate blindly.".to_string(),
})?;
let blinded_tail_with_hop_iter = BlindedTailHopIter {
hops: blinded_tail.hops.iter(),
blinding_point: blinded_tail.blinding_point,
final_value_msat: blinded_tail.final_value_msat,
excess_final_cltv_expiry_delta: blinded_tail.excess_final_cltv_expiry_delta,
};

let (value_msat, cltv) = build_trampoline_onion_payloads_callback(
path.trampoline_hops.iter(),
blinded_tail_with_hop_iter,
total_msat,
recipient_onion,
starting_htlc_offset,
keysend_preimage,
|action, payload| match action {
PayloadCallbackAction::PushBack => res.push(payload),
PayloadCallbackAction::PushFront => res.insert(0, payload),
},
)?;
Ok((res, value_msat, cltv))
}

fn build_trampoline_onion_payloads_callback<'a, H, B, F>(
hops: H, mut blinded_tail: BlindedTailHopIter<'a, B>, total_msat: u64,
recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32,
keysend_preimage: &Option<PaymentPreimage>, mut callback: F,
) -> Result<(u64, u32), APIError>
where
H: DoubleEndedIterator<Item = &'a TrampolineHop>,
B: ExactSizeIterator<Item = &'a BlindedHop>,
F: FnMut(PayloadCallbackAction, msgs::OutboundTrampolinePayload<'a>),
{
let mut cur_value_msat = 0u64;
let mut cur_cltv = starting_htlc_offset;
let mut last_node_id = None;

// appeasing the borrow checker
let mut blinded_tail_option = Some(blinded_tail);

for (idx, hop) in hops.rev().enumerate() {
// First hop gets special values so that it can check, on receipt, that everything is
// exactly as it should be (and the next hop isn't trying to probe to find out if we're
// 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
};
if idx == 0 {
if let Some(BlindedTailHopIter {
blinding_point,
hops,
final_value_msat,
excess_final_cltv_expiry_delta,
}) = blinded_tail_option.take()
{
let mut blinding_point = Some(blinding_point);
let hops_len = hops.len();
for (i, blinded_hop) in hops.enumerate() {
if i == hops_len - 1 {
cur_value_msat += final_value_msat;
callback(
PayloadCallbackAction::PushBack,
msgs::OutboundTrampolinePayload::BlindedReceive {
sender_intended_htlc_amt_msat: final_value_msat,
total_msat,
cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
encrypted_tlvs: &blinded_hop.encrypted_payload,
intro_node_blinding_point: blinding_point.take(),
keysend_preimage: *keysend_preimage,
custom_tlvs: &recipient_onion.custom_tlvs,
},
);
} else {
callback(
PayloadCallbackAction::PushBack,
msgs::OutboundTrampolinePayload::BlindedForward {
encrypted_tlvs: &blinded_hop.encrypted_payload,
intro_node_blinding_point: blinding_point.take(),
},
);
}
}
}
} else {
let payload = msgs::OutboundTrampolinePayload::Forward {
outgoing_node_id: last_node_id.unwrap(),
amt_to_forward: value_msat,
outgoing_cltv_value: cltv,
};
callback(PayloadCallbackAction::PushFront, payload);
}
cur_value_msat += hop.fee_msat;
if cur_value_msat >= 21000000 * 100000000 * 1000 {
return Err(APIError::InvalidRoute { err: "Channel fees overflowed?".to_owned() });
}
cur_cltv += hop.cltv_expiry_delta as u32;
if cur_cltv >= 500000000 {
return Err(APIError::InvalidRoute { err: "Channel CLTV overflowed?".to_owned() });
}
last_node_id = Some(hop.pubkey);
}
Ok((cur_value_msat, cur_cltv))
}

/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
pub(super) fn build_onion_payloads<'a>(
path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,
Expand Down

0 comments on commit 33804c3

Please sign in to comment.