Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Trampoline Packet deserialization for Inbound::Receive #2806

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11867,6 +11867,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
trampoline_packet: None
};
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
// intended amount, we fail the payment.
Expand All @@ -11889,6 +11890,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
trampoline_packet: None
};
let current_height: u32 = node[0].node.best_block.read().unwrap().height();
assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
Expand All @@ -11913,6 +11915,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]), total_msat: 100,
}),
custom_tlvs: Vec::new(),
trampoline_packet: None
}, [0; 32], PaymentHash([0; 32]), 100, 23, None, true, None, current_height,
node[0].node.default_configuration.accept_mpp_keysend);

Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ mod sealed {
ChannelType | SCIDPrivacy,
// Byte 6
ZeroConf | Keysend,
// Byte 7
Trampoline,
]);
define_context!(ChannelContext, []);
define_context!(Bolt11InvoiceContext, [
Expand Down Expand Up @@ -415,6 +417,9 @@ mod sealed {
define_feature!(55, Keysend, [NodeContext],
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
supports_keysend, requires_keysend);
define_feature!(57, Trampoline, [NodeContext],
"Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required,
supports_trampoline_routing, requires_trampoline_routing);
// Note: update the module-level docs when a new feature bit is added!

#[cfg(test)]
Expand Down
139 changes: 135 additions & 4 deletions lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,7 @@ mod fuzzy_internal_msgs {
use crate::prelude::*;
use crate::ln::{PaymentPreimage, PaymentSecret};
use crate::ln::features::BlindedHopFeatures;
use crate::ln::msgs::VariableLengthOnionPacket;

// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
// them from untrusted input):
Expand All @@ -1695,6 +1696,7 @@ mod fuzzy_internal_msgs {
custom_tlvs: Vec<(u64, Vec<u8>)>,
amt_msat: u64,
outgoing_cltv_value: u32,
trampoline_packet: Option<VariableLengthOnionPacket>
},
BlindedForward {
short_channel_id: u64,
Expand Down Expand Up @@ -1727,6 +1729,7 @@ mod fuzzy_internal_msgs {
custom_tlvs: Vec<(u64, Vec<u8>)>,
amt_msat: u64,
outgoing_cltv_value: u32,
trampoline_packet: Option<VariableLengthOnionPacket>
},
BlindedForward {
encrypted_tlvs: Vec<u8>,
Expand Down Expand Up @@ -1787,6 +1790,44 @@ impl fmt::Debug for OnionPacket {
}
}

/// BOLT 4 onion packet including hop data for the next peer.
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct VariableLengthOnionPacket {
/// BOLT 4 version number.
pub version: u8,
/// In order to ensure we always return an error on onion decode in compliance with [BOLT
/// #4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md), we have to
/// deserialize `OnionPacket`s contained in [`UpdateAddHTLC`] messages even if the ephemeral
/// public key (here) is bogus, so we hold a [`Result`] instead of a [`PublicKey`] as we'd
/// like.
pub public_key: Result<PublicKey, secp256k1::Error>,
/// Variable number of bytes encrypted payload for the next hop; 650 by default for Trampoline
pub(crate) hop_data: Vec<u8>,
/// HMAC to verify the integrity of hop_data.
pub hmac: [u8; 32],
}

impl Readable for VariableLengthOnionPacket {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(VariableLengthOnionPacket {
version: Readable::read(r)?,
public_key: {
let mut buf = [0u8;33];
r.read_exact(&mut buf)?;
PublicKey::from_slice(&buf)
},
hop_data: Readable::read(r)?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add a unit test to make sure this works? The current spec doesn't encode the length of the hop_data, so I think there's a chance this might break without using a reader with an a priori specified fixed length.

hmac: Readable::read(r)?,
})
}
}

impl fmt::Debug for VariableLengthOnionPacket {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_fmt(format_args!("VariableLengthOnionPacket version {} with hmac {:?}", self.version, &self.hmac[..]))
}
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub(crate) struct OnionErrorPacket {
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
Expand Down Expand Up @@ -2215,6 +2256,23 @@ impl Readable for OnionPacket {
}
}

// This will be written as the value of a TLV, so when reading, the length of the hop data will be
// inferred from a ReadableArg containing the total length. The standard hop data for Trampoline
// onions will prospectively be 650 bytes.
impl Writeable for VariableLengthOnionPacket {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.version.write(w)?;
match self.public_key {
Ok(pubkey) => pubkey.write(w)?,
Err(_) => [0u8;33].write(w)?,
}
// don't encode the length of hop_data
w.write_all(&self.hop_data)?;
&self.hmac.write(w)?;
Ok(())
}
}

impl_writeable_msg!(UpdateAddHTLC, {
channel_id,
htlc_id,
Expand Down Expand Up @@ -2277,13 +2335,17 @@ impl Writeable for OutboundOnionPayload {
},
Self::Receive {
ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat,
outgoing_cltv_value, ref custom_tlvs,
outgoing_cltv_value, ref trampoline_packet, ref custom_tlvs
} => {
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
// to reject any reserved types in the experimental range if new ones are ever
// standardized.
let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode()));
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
let trampoline_tlv = trampoline_packet.as_ref().map(|trampoline| (66100, trampoline.encode()));
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter()
.chain(keysend_tlv.iter())
.chain(trampoline_tlv.iter())
.collect();
custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
_encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedBigSize(*amt_msat), required),
Expand Down Expand Up @@ -2327,6 +2389,7 @@ impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: Node
let mut total_msat = None;
let mut keysend_preimage: Option<PaymentPreimage> = None;
let mut custom_tlvs = Vec::new();
let mut trampoline_packet: Option<VariableLengthOnionPacket> = None;

let tlv_len = BigSize::read(r)?;
let rd = FixedLengthReader::new(r, tlv_len.0);
Expand All @@ -2339,6 +2402,7 @@ impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: Node
(12, intro_node_blinding_point, option),
(16, payment_metadata, option),
(18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
(66100, trampoline_packet, option),
// See https://github.com/lightning/blips/blob/master/blip-0003.md
(5482373484, keysend_preimage, option)
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
Expand Down Expand Up @@ -2415,6 +2479,7 @@ impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: Node
amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
custom_tlvs,
trampoline_packet
})
}
}
Expand Down Expand Up @@ -2833,10 +2898,10 @@ mod tests {
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::ChannelId;
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket};
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, VariableLengthOnionPacket};
use crate::ln::msgs::SocketAddress;
use crate::routing::gossip::{NodeAlias, NodeId};
use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited};
use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited, BigSize};
use crate::util::test_utils;

use bitcoin::hashes::hex::FromHex;
Expand Down Expand Up @@ -4002,6 +4067,7 @@ mod tests {
amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
custom_tlvs: vec![],
trampoline_packet: None
};
let encoded_value = outbound_msg.encode();
let target_value = <Vec<u8>>::from_hex("1002080badf00d010203040404ffffffff").unwrap();
Expand Down Expand Up @@ -4030,6 +4096,7 @@ mod tests {
amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
custom_tlvs: vec![],
trampoline_packet: None
};
let encoded_value = outbound_msg.encode();
let target_value = <Vec<u8>>::from_hex("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
Expand All @@ -4046,6 +4113,7 @@ mod tests {
payment_metadata: None,
keysend_preimage: None,
custom_tlvs,
..
} = inbound_msg {
assert_eq!(payment_secret, expected_payment_secret);
assert_eq!(amt_msat, 0x0badf00d01020304);
Expand All @@ -4069,6 +4137,7 @@ mod tests {
custom_tlvs: bad_type_range_tlvs,
amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
trampoline_packet: None
};
let encoded_value = msg.encode();
let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
Expand Down Expand Up @@ -4101,6 +4170,7 @@ mod tests {
custom_tlvs: expected_custom_tlvs.clone(),
amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
trampoline_packet: None
};
let encoded_value = msg.encode();
let target_value = <Vec<u8>>::from_hex("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
Expand All @@ -4122,6 +4192,67 @@ mod tests {
} else { panic!(); }
}

#[test]
fn encoding_final_onion_hop_data_with_trampoline_packet() {
let secp_ctx = Secp256k1::new();
let (private_key, public_key) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);

let compressed_public_key = public_key.serialize();
assert_eq!(compressed_public_key.len(), 33);

let trampoline_packet = VariableLengthOnionPacket {
version: 0,
public_key: Ok(public_key),
hop_data: vec![1; 650], // this should be the standard encoded length
hmac: [2; 32],
};
let encoded_trampoline_packet = trampoline_packet.encode();
assert_eq!(encoded_trampoline_packet.len(), 716);

let msg = msgs::OutboundOnionPayload::Receive {
payment_data: None,
payment_metadata: None,
keysend_preimage: None,
custom_tlvs: Vec::new(),
amt_msat: 0x0badf00d01020304,
outgoing_cltv_value: 0xffffffff,
trampoline_packet: Some(trampoline_packet)
};
let encoded_payload = msg.encode();

let trampoline_type_bytes = &encoded_payload[19..=23];
let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes);
let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap();
assert_eq!(trampoline_type_big_size.0, 66100);

let trampoline_length_bytes = &encoded_payload[24..=26];
let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes);
let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap();
assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64);
}

#[test]
fn encoding_final_onion_hop_data_with_eclair_trampoline_packet() {
let public_key = PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap();
let hop_data = <Vec<u8>>::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap();
let hmac_vector = <Vec<u8>>::from_hex("bb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
let mut hmac = [0; 32];
hmac.copy_from_slice(&hmac_vector);

let compressed_public_key = public_key.serialize();
assert_eq!(compressed_public_key.len(), 33);

let trampoline_packet = VariableLengthOnionPacket {
version: 0,
public_key: Ok(public_key),
hop_data,
hmac
};
let encoded_trampoline_packet = trampoline_packet.encode();
let expected_eclair_trampoline_packet = <Vec<u8>>::from_hex("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4cbb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet);
}

#[test]
fn query_channel_range_end_blocknum() {
let tests: Vec<(u32, u32, u32)> = vec![
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/onion_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
custom_tlvs: recipient_onion.custom_tlvs.clone(),
amt_msat: value_msat,
outgoing_cltv_value: cltv,
trampoline_packet: None
});
}
} else {
Expand Down