X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmsgs.rs;h=19c92e5746e51696f658d220cb2ab4fa4964cbfd;hb=c39c398f0618835201fb741b1976952c3f9180fa;hp=8dfefb665ff1dfe5c60b65eee693bcbd3c3fb824;hpb=8354e0c9ba383d4f4df14c9735847a0b19a37f91;p=rust-lightning diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 8dfefb66..19c92e57 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1666,7 +1666,7 @@ mod fuzzy_internal_msgs { use crate::prelude::*; use crate::ln::{PaymentPreimage, PaymentSecret}; use crate::ln::features::BlindedHopFeatures; - use super::FinalOnionHopData; + use super::{FinalOnionHopData, TrampolineOnionPacket}; // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize // them from untrusted input): @@ -1700,6 +1700,7 @@ mod fuzzy_internal_msgs { payment_secret: PaymentSecret, payment_constraints: PaymentConstraints, intro_node_blinding_point: Option, + keysend_preimage: Option, } } @@ -1710,6 +1711,13 @@ mod fuzzy_internal_msgs { amt_to_forward: u64, outgoing_cltv_value: u32, }, + #[allow(unused)] + TrampolineEntrypoint { + amt_to_forward: u64, + outgoing_cltv_value: u32, + multipath_trampoline_data: Option, + trampoline_packet: TrampolineOnionPacket, + }, Receive { payment_data: Option, payment_metadata: Option>, @@ -1728,6 +1736,18 @@ mod fuzzy_internal_msgs { cltv_expiry_height: u32, encrypted_tlvs: Vec, intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node + keysend_preimage: Option, + } + } + + pub(crate) enum OutboundTrampolinePayload { + #[allow(unused)] + Forward { + /// The value, in msat, of the payment after this hop's fee is deducted. + amt_to_forward: u64, + outgoing_cltv_value: u32, + /// The node id to which the trampoline node must find a route + outgoing_node_id: PublicKey, } } @@ -1777,6 +1797,52 @@ impl fmt::Debug for OnionPacket { } } +/// BOLT 4 onion packet including hop data for the next peer. +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct TrampolineOnionPacket { + /// Bolt 04 version number + pub version: u8, + /// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data + pub public_key: PublicKey, + /// Encrypted payload for the next hop + // + // Unlike the onion packets used for payments, Trampoline onion packets have to be shorter than + // 1300 bytes. The expected default is 650 bytes. + // TODO: if 650 ends up being the most common size, optimize this to be: + // enum { SixFifty([u8; 650]), VarLen(Vec) } + pub hop_data: Vec, + /// HMAC to verify the integrity of hop_data + pub hmac: [u8; 32], +} + +impl onion_utils::Packet for TrampolineOnionPacket { + type Data = Vec; + fn new(public_key: PublicKey, hop_data: Vec, hmac: [u8; 32]) -> Self { + Self { + version: 0, + public_key, + hop_data, + hmac, + } + } +} + +impl Writeable for TrampolineOnionPacket { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.version.write(w)?; + self.public_key.write(w)?; + w.write_all(&self.hop_data)?; + self.hmac.write(w)?; + Ok(()) + } +} + +impl Debug for TrampolineOnionPacket { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("TrampolineOnionPacket 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? @@ -2490,6 +2556,17 @@ impl Writeable for OutboundOnionPayload { (6, short_channel_id, required) }); }, + Self::TrampolineEntrypoint { + amt_to_forward, outgoing_cltv_value, ref multipath_trampoline_data, + ref trampoline_packet + } => { + _encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required), + (8, multipath_trampoline_data, option), + (20, trampoline_packet, required) + }); + }, Self::Receive { ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat, cltv_expiry_height, ref custom_tlvs, @@ -2515,14 +2592,15 @@ impl Writeable for OutboundOnionPayload { }, Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, - intro_node_blinding_point, + intro_node_blinding_point, keysend_preimage, } => { _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), (10, *encrypted_tlvs, required_vec), (12, intro_node_blinding_point, option), - (18, HighZeroBytesDroppedBigSize(*total_msat), required) + (18, HighZeroBytesDroppedBigSize(*total_msat), required), + (5482373484, keysend_preimage, option) }); }, } @@ -2530,6 +2608,22 @@ impl Writeable for OutboundOnionPayload { } } +impl Writeable for OutboundTrampolinePayload { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + match self { + Self::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => { + _encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required), + (14, outgoing_node_id, required) + }); + } + } + Ok(()) + } +} + + impl ReadableArgs<(Option, &NS)> for InboundOnionPayload where NS::Target: NodeSigner { fn read(r: &mut R, args: (Option, &NS)) -> Result { let (update_add_blinding_point, node_signer) = args; @@ -2572,9 +2666,7 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w } if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) { - if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() || - keysend_preimage.is_some() - { + if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() { return Err(DecodeError::InvalidValue) } let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0; @@ -2587,7 +2679,9 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs { short_channel_id, payment_relay, payment_constraints, features })} => { - if amt.is_some() || cltv_value.is_some() || total_msat.is_some() { + if amt.is_some() || cltv_value.is_some() || total_msat.is_some() || + keysend_preimage.is_some() + { return Err(DecodeError::InvalidValue) } Ok(Self::BlindedForward { @@ -2609,6 +2703,7 @@ impl ReadableArgs<(Option, &NS)> for InboundOnionPayload w payment_secret, payment_constraints, intro_node_blinding_point, + keysend_preimage, }) }, } @@ -3055,10 +3150,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, CommonOpenChannelFields, CommonAcceptChannelFields}; + use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket}; use crate::ln::msgs::SocketAddress; use crate::routing::gossip::{NodeAlias, NodeId}; - use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited}; + use crate::util::ser::{BigSize, Hostname, Readable, ReadableArgs, TransactionU16LenLimited, Writeable}; use crate::util::test_utils; use bitcoin::hashes::hex::FromHex; @@ -4350,6 +4445,64 @@ 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 = TrampolineOnionPacket { + version: 0, + 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::TrampolineEntrypoint { + multipath_trampoline_data: None, + amt_to_forward: 0x0badf00d01020304, + outgoing_cltv_value: 0xffffffff, + trampoline_packet, + }; + let encoded_payload = msg.encode(); + + let trampoline_type_bytes = &encoded_payload[19..=19]; + 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, 20); + + let trampoline_length_bytes = &encoded_payload[20..=22]; + 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(&>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap(); + let hop_data = >::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap(); + let hmac_vector = >::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 = TrampolineOnionPacket { + version: 0, + public_key, + hop_data, + hmac, + }; + let encoded_trampoline_packet = trampoline_packet.encode(); + let expected_eclair_trampoline_packet = >::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![