Skip to content

Commit d02d276

Browse files
committed
public onion utils, scid_utils, and utils for creating/peeling payment onions
1 parent be8797e commit d02d276

File tree

6 files changed

+169
-9
lines changed

6 files changed

+169
-9
lines changed

lightning/src/ln/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(crate) mod onion_utils;
3939
mod outbound_payment;
4040
pub mod wire;
4141

42+
pub use onion_utils::{ForwardedPayment, PeeledPayment, ReceivedPayment, create_payment_onion, peel_payment_onion};
4243
// Older rustc (which we support) refuses to let us call the get_payment_preimage_hash!() macro
4344
// without the node parameter being mut. This is incorrect, and thus newer rustcs will complain
4445
// about an unnecessary mut. Thus, we silence the unused_mut warning in two test modules below.

lightning/src/ln/msgs.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,17 +1674,21 @@ pub use self::fuzzy_internal_msgs::*;
16741674
#[cfg(not(fuzzing))]
16751675
pub(crate) use self::fuzzy_internal_msgs::*;
16761676

1677+
/// Bolt04 OnionPacket including hop data for the next peer
16771678
#[derive(Clone)]
1678-
pub(crate) struct OnionPacket {
1679-
pub(crate) version: u8,
1679+
pub struct OnionPacket {
1680+
/// Bolt 04 version number
1681+
pub version: u8,
16801682
/// In order to ensure we always return an error on onion decode in compliance with [BOLT
16811683
/// #4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md), we have to
16821684
/// deserialize `OnionPacket`s contained in [`UpdateAddHTLC`] messages even if the ephemeral
16831685
/// public key (here) is bogus, so we hold a [`Result`] instead of a [`PublicKey`] as we'd
16841686
/// like.
1685-
pub(crate) public_key: Result<PublicKey, secp256k1::Error>,
1686-
pub(crate) hop_data: [u8; 20*65],
1687-
pub(crate) hmac: [u8; 32],
1687+
pub public_key: Result<PublicKey, secp256k1::Error>,
1688+
/// 1300 bytes encrypted payload for the next hop
1689+
pub hop_data: [u8; 20*65],
1690+
/// HMAC to verify the integrity of hop_data
1691+
pub hmac: [u8; 32],
16881692
}
16891693

16901694
impl onion_utils::Packet for OnionPacket {

lightning/src/ln/onion_utils.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,152 @@ pub(crate) fn decode_next_payment_hop<NS: Deref>(
935935
}
936936
}
937937

938+
/// Build a payment onion, returning the first hop msat and cltv values as well.
939+
pub fn create_payment_onion<T>(
940+
secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64,
941+
recipient_onion: RecipientOnionFields, starting_htlc_offset: u32, payment_hash: PaymentHash,
942+
keysend_preimage: Option<PaymentPreimage>, prng_seed: [u8; 32]) -> Result<(u64, u32, msgs::OnionPacket), ()>
943+
where
944+
T: secp256k1::Signing
945+
{
946+
let onion_keys = construct_onion_keys(&secp_ctx, &path, &session_priv).map_err(|_| ())?;
947+
let (onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads(
948+
&path,
949+
total_msat,
950+
recipient_onion,
951+
starting_htlc_offset,
952+
&keysend_preimage,
953+
).map_err(|_| ())?;
954+
let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, &payment_hash)?;
955+
Ok((htlc_msat, htlc_cltv, onion_packet))
956+
}
957+
958+
/// Forwarded Payment, including next hop details
959+
#[derive(Debug)]
960+
pub struct ForwardedPayment {
961+
/// short channel id of the next hop
962+
pub short_channel_id: u64,
963+
/// The value, in msat, of the payment after this hop's fee is deducted.
964+
pub amt_to_forward: u64,
965+
/// outgoing CLTV for the next hop
966+
pub outgoing_cltv_value: u32,
967+
/// onion packet for the next hop
968+
pub onion_packet: msgs::OnionPacket,
969+
}
970+
971+
/// Received payment, of either regular or blinded type
972+
#[derive(Debug)]
973+
pub enum ReceivedPayment {
974+
/// Regular (unblinded) payment
975+
Regular {
976+
/// payment_secret to authenticate sender to the receiver
977+
payment_secret: Option<[u8; 32]>,
978+
/// The total value, in msat, of the payment as received by the ultimate recipient
979+
total_msat: Option<u64>,
980+
/// custom payment metadata included in the payment
981+
payment_metadata: Option<Vec<u8>>,
982+
/// preimage used in spontaneous payment
983+
keysend_preimage: Option<[u8; 32]>,
984+
/// custom TLV records included in the payment
985+
custom_tlvs: Vec<(u64, Vec<u8>)>,
986+
/// amount received
987+
amt_msat: u64,
988+
/// outgoing ctlv
989+
outgoing_cltv_value: u32,
990+
},
991+
/// Blinded payment
992+
Blinded {
993+
/// amount received
994+
amt_msat: u64,
995+
/// amount received plus fees paid
996+
total_msat: u64,
997+
/// outgoing cltv
998+
outgoing_cltv_value: u32,
999+
/// Payment secret
1000+
payment_secret: [u8; 32],
1001+
/// The maximum total CLTV that is acceptable when relaying a payment over this hop
1002+
max_cltv_expiry: u32,
1003+
/// The minimum value, in msat, that may be accepted by the node corresponding to this hop
1004+
htlc_minimum_msat: u64,
1005+
/// Blinding point from intro node
1006+
intro_node_blinding_point: PublicKey,
1007+
}
1008+
}
1009+
1010+
impl std::convert::TryFrom<msgs::InboundOnionPayload> for ReceivedPayment {
1011+
type Error = ();
1012+
fn try_from(pld: msgs::InboundOnionPayload) -> Result<Self, Self::Error> {
1013+
match pld {
1014+
msgs::InboundOnionPayload::Forward { short_channel_id: _, amt_to_forward: _, outgoing_cltv_value: _ } => {
1015+
Err(())
1016+
},
1017+
msgs::InboundOnionPayload::Receive { payment_data, payment_metadata, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value } => {
1018+
let (payment_secret, total_msat) = match payment_data {
1019+
Some(p) => (Some(p.payment_secret.0), Some(p.total_msat)),
1020+
None => (None, None),
1021+
};
1022+
let keysend_preimage = keysend_preimage.map(|p| p.0);
1023+
Ok(Self::Regular { payment_secret, total_msat, payment_metadata, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value })
1024+
},
1025+
msgs::InboundOnionPayload::BlindedReceive { amt_msat, total_msat, outgoing_cltv_value, payment_secret, payment_constraints, intro_node_blinding_point } => {
1026+
let payment_secret = payment_secret.0;
1027+
let max_cltv_expiry = payment_constraints.max_cltv_expiry;
1028+
let htlc_minimum_msat = payment_constraints.htlc_minimum_msat;
1029+
Ok(Self::Blinded { amt_msat, total_msat, outgoing_cltv_value, payment_secret, max_cltv_expiry, htlc_minimum_msat, intro_node_blinding_point })
1030+
}
1031+
}
1032+
}
1033+
}
1034+
1035+
/// Received and decrypted onion payment, either of type Receive (for us), or Forward.
1036+
#[derive(Debug)]
1037+
pub enum PeeledPayment {
1038+
/// This onion payload was for us, not for forwarding to a next-hop.
1039+
Receive(ReceivedPayment),
1040+
/// This onion payload to be forwarded to next peer.
1041+
Forward(ForwardedPayment),
1042+
}
1043+
1044+
/// Unwrap one layer of an incoming HTLC, returning either or a received payment, or a another
1045+
/// onion to forward.
1046+
pub fn peel_payment_onion<NS: Deref>(
1047+
shared_secret: [u8; 32], onion: &msgs::OnionPacket, payment_hash: PaymentHash,
1048+
node_signer: &NS, secp_ctx: &Secp256k1<secp256k1::All>
1049+
) -> Result<PeeledPayment, ()>
1050+
where
1051+
NS::Target: NodeSigner,
1052+
{
1053+
let hop = decode_next_payment_hop(shared_secret, &onion.hop_data[..], onion.hmac, payment_hash, node_signer).map_err(|_| ())?;
1054+
let peeled = match hop {
1055+
Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
1056+
if let msgs::InboundOnionPayload::Forward{short_channel_id, amt_to_forward, outgoing_cltv_value} = next_hop_data {
1057+
1058+
let next_packet_pk = next_hop_pubkey(secp_ctx, onion.public_key.unwrap(), &shared_secret);
1059+
1060+
let onion_packet = msgs::OnionPacket {
1061+
version: 0,
1062+
public_key: next_packet_pk,
1063+
hop_data: new_packet_bytes,
1064+
hmac: next_hop_hmac,
1065+
};
1066+
PeeledPayment::Forward(ForwardedPayment{
1067+
short_channel_id,
1068+
amt_to_forward,
1069+
outgoing_cltv_value,
1070+
onion_packet,
1071+
})
1072+
} else {
1073+
return Err(());
1074+
}
1075+
},
1076+
Hop::Receive(inbound) => {
1077+
let payload: ReceivedPayment = inbound.try_into().map_err(|_| ())?;
1078+
PeeledPayment::Receive(payload)
1079+
},
1080+
};
1081+
Ok(peeled)
1082+
}
1083+
9381084
pub(crate) fn decode_next_untagged_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], read_args: T) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> {
9391085
decode_next_hop(shared_secret, hop_data, hmac_bytes, None, read_args)
9401086
}

lightning/src/onion_message/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod functional_tests;
2828

2929
// Re-export structs so they can be imported with just the `onion_message::` module prefix.
3030
pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, PendingOnionMessage, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
31+
pub use self::messenger::{create_onion_message, peel_onion_message};
3132
pub use self::offers::{OffersMessage, OffersMessageHandler};
3233
pub use self::packet::{Packet, ParsedOnionMessageContents};
3334
pub(crate) use self::packet::ControlTlvs;

lightning/src/util/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod ser;
2020
pub mod message_signing;
2121
pub mod invoice;
2222
pub mod persist;
23+
pub mod scid_utils;
2324
pub mod string;
2425
pub mod wakers;
2526
#[cfg(fuzzing)]
@@ -34,7 +35,6 @@ pub(crate) mod chacha20;
3435
pub(crate) mod poly1305;
3536
pub(crate) mod chacha20poly1305rfc;
3637
pub(crate) mod transaction_utils;
37-
pub(crate) mod scid_utils;
3838
pub(crate) mod time;
3939

4040
pub mod indexed_map;

lightning/src/util/scid_utils.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// You may not use this file except in accordance with one or both of these
88
// licenses.
99

10+
//! Utils for constructing and parse short chan ids
11+
1012
/// Maximum block height that can be used in a `short_channel_id`. This
1113
/// value is based on the 3-bytes available for block height.
1214
pub const MAX_SCID_BLOCK: u64 = 0x00ffffff;
@@ -22,8 +24,11 @@ pub const MAX_SCID_VOUT_INDEX: u64 = 0xffff;
2224
/// A `short_channel_id` construction error
2325
#[derive(Debug, PartialEq, Eq)]
2426
pub enum ShortChannelIdError {
27+
/// Block too high
2528
BlockOverflow,
29+
/// TxIndex too high
2630
TxIndexOverflow,
31+
/// VoutIndex too high
2732
VoutIndexOverflow,
2833
}
2934

@@ -65,7 +70,7 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64
6570
/// 2) phantom node payments, to get an scid for the phantom node's phantom channel
6671
/// 3) payments intended to be intercepted will route using a fake scid (this is typically used so
6772
/// the forwarding node can open a JIT channel to the next hop)
68-
pub(crate) mod fake_scid {
73+
pub mod fake_scid {
6974
use bitcoin::blockdata::constants::ChainHash;
7075
use bitcoin::network::constants::Network;
7176
use crate::sign::EntropySource;
@@ -90,9 +95,12 @@ pub(crate) mod fake_scid {
9095
/// receipt, and handle the HTLC accordingly. The namespace identifier is encrypted when encoded
9196
/// into the fake scid.
9297
#[derive(Copy, Clone)]
93-
pub(crate) enum Namespace {
98+
pub enum Namespace {
99+
/// Phantom short channel ids
94100
Phantom,
101+
/// Aliases for outbound channels
95102
OutboundAlias,
103+
/// Namespace for intercepted payments
96104
Intercept
97105
}
98106

@@ -101,7 +109,7 @@ pub(crate) mod fake_scid {
101109
/// between segwit activation and the current best known height, and the tx index and output
102110
/// index are also selected from a "reasonable" range. We add this logic because it makes it
103111
/// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
104-
pub(crate) fn get_fake_scid<ES: Deref>(&self, highest_seen_blockheight: u32, chain_hash: &ChainHash, fake_scid_rand_bytes: &[u8; 32], entropy_source: &ES) -> u64
112+
pub fn get_fake_scid<ES: Deref>(&self, highest_seen_blockheight: u32, chain_hash: &ChainHash, fake_scid_rand_bytes: &[u8; 32], entropy_source: &ES) -> u64
105113
where ES::Target: EntropySource,
106114
{
107115
// Ensure we haven't created a namespace that doesn't fit into the 3 bits we've allocated for

0 commit comments

Comments
 (0)