Address custom HTLC TLV fixups
[rust-lightning] / lightning / src / ln / msgs.rs
index 590b26632fbe9216b632d33844a1e5121b5cef64..515dcbba2d2e136846697dc990d582270e07e1d4 100644 (file)
@@ -43,7 +43,7 @@ use crate::io_extras::read_to_end;
 
 use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
 use crate::util::logger;
-use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited};
+use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
 
 use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
 
@@ -973,7 +973,11 @@ pub struct UnsignedChannelAnnouncement {
        pub bitcoin_key_1: NodeId,
        /// The funding key for the second node
        pub bitcoin_key_2: NodeId,
-       pub(crate) excess_data: Vec<u8>,
+       /// Excess data which was signed as a part of the message which we do not (yet) understand how
+       /// to decode.
+       ///
+       /// This is stored to ensure forward-compatibility as new fields are added to the lightning gossip protocol.
+       pub excess_data: Vec<u8>,
 }
 /// A [`channel_announcement`] message to be sent to or received from a peer.
 ///
@@ -1437,29 +1441,29 @@ mod fuzzy_internal_msgs {
                        payment_data: Option<FinalOnionHopData>,
                        payment_metadata: Option<Vec<u8>>,
                        keysend_preimage: Option<PaymentPreimage>,
+                       custom_tlvs: Vec<(u64, Vec<u8>)>,
                        amt_msat: u64,
                        outgoing_cltv_value: u32,
                },
        }
 
-       pub(crate) enum OnionHopDataFormat {
-               NonFinalNode {
+       pub(crate) enum OutboundOnionPayload {
+               Forward {
                        short_channel_id: u64,
+                       /// The value, in msat, of the payment after this hop's fee is deducted.
+                       amt_to_forward: u64,
+                       outgoing_cltv_value: u32,
                },
-               FinalNode {
+               Receive {
                        payment_data: Option<FinalOnionHopData>,
                        payment_metadata: Option<Vec<u8>>,
                        keysend_preimage: Option<PaymentPreimage>,
+                       custom_tlvs: Vec<(u64, Vec<u8>)>,
+                       amt_msat: u64,
+                       outgoing_cltv_value: u32,
                },
        }
 
-       pub struct OnionHopData {
-               pub(crate) format: OnionHopDataFormat,
-               /// The value, in msat, of the payment after this hop's fee is deducted.
-               pub(crate) amt_to_forward: u64,
-               pub(crate) outgoing_cltv_value: u32,
-       }
-
        pub struct DecodedOnionErrorPacket {
                pub(crate) hmac: [u8; 32],
                pub(crate) failuremsg: Vec<u8>,
@@ -1966,24 +1970,32 @@ impl Readable for FinalOnionHopData {
        }
 }
 
-impl Writeable for OnionHopData {
+impl Writeable for OutboundOnionPayload {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               match self.format {
-                       OnionHopDataFormat::NonFinalNode { short_channel_id } => {
+               match self {
+                       Self::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } => {
                                _encode_varint_length_prefixed_tlv!(w, {
-                                       (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
-                                       (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+                                       (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+                                       (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
                                        (6, short_channel_id, required)
                                });
                        },
-                       OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
+                       Self::Receive {
+                               ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat,
+                               outgoing_cltv_value, 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();
+                               custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
                                _encode_varint_length_prefixed_tlv!(w, {
-                                       (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
-                                       (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
+                                       (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
+                                       (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
                                        (8, payment_data, option),
-                                       (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
-                                       (5482373484, keysend_preimage, option)
-                               });
+                                       (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
+                               }, custom_tlvs.iter());
                        },
                }
                Ok(())
@@ -1998,7 +2010,11 @@ impl Readable for InboundOnionPayload {
                let mut payment_data: Option<FinalOnionHopData> = None;
                let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
                let mut keysend_preimage: Option<PaymentPreimage> = None;
-               read_tlv_fields!(r, {
+               let mut custom_tlvs = Vec::new();
+
+               let tlv_len = BigSize::read(r)?;
+               let rd = FixedLengthReader::new(r, tlv_len.0);
+               decode_tlv_stream_with_custom_tlv_decode!(rd, {
                        (2, amt, required),
                        (4, cltv_value, required),
                        (6, short_id, option),
@@ -2006,6 +2022,12 @@ impl Readable for InboundOnionPayload {
                        (16, payment_metadata, 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> {
+                       if msg_type < 1 << 16 { return Ok(false) }
+                       let mut value = Vec::new();
+                       msg_reader.read_to_end(&mut value)?;
+                       custom_tlvs.push((msg_type, value));
+                       Ok(true)
                });
 
                if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
@@ -2029,6 +2051,7 @@ impl Readable for InboundOnionPayload {
                                keysend_preimage,
                                amt_msat: amt.0,
                                outgoing_cltv_value: cltv_value.0,
+                               custom_tlvs,
                        })
                }
        }
@@ -2454,7 +2477,7 @@ mod tests {
        use hex;
        use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
        use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
-       use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
+       use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket};
        use crate::routing::gossip::{NodeAlias, NodeId};
        use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited};
 
@@ -3537,14 +3560,12 @@ mod tests {
 
        #[test]
        fn encoding_nonfinal_onion_hop_data() {
-               let msg = msgs::OnionHopData {
-                       format: OnionHopDataFormat::NonFinalNode {
-                               short_channel_id: 0xdeadbeef1bad1dea,
-                       },
+               let outbound_msg = msgs::OutboundOnionPayload::Forward {
+                       short_channel_id: 0xdeadbeef1bad1dea,
                        amt_to_forward: 0x0badf00d01020304,
                        outgoing_cltv_value: 0xffffffff,
                };
-               let encoded_value = msg.encode();
+               let encoded_value = outbound_msg.encode();
                let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap();
                assert_eq!(encoded_value, target_value);
 
@@ -3558,16 +3579,15 @@ mod tests {
 
        #[test]
        fn encoding_final_onion_hop_data() {
-               let msg = msgs::OnionHopData {
-                       format: OnionHopDataFormat::FinalNode {
-                               payment_data: None,
-                               payment_metadata: None,
-                               keysend_preimage: None,
-                       },
-                       amt_to_forward: 0x0badf00d01020304,
+               let outbound_msg = msgs::OutboundOnionPayload::Receive {
+                       payment_data: None,
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       amt_msat: 0x0badf00d01020304,
                        outgoing_cltv_value: 0xffffffff,
+                       custom_tlvs: vec![],
                };
-               let encoded_value = msg.encode();
+               let encoded_value = outbound_msg.encode();
                let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
                assert_eq!(encoded_value, target_value);
 
@@ -3581,19 +3601,18 @@ mod tests {
        #[test]
        fn encoding_final_onion_hop_data_with_secret() {
                let expected_payment_secret = PaymentSecret([0x42u8; 32]);
-               let msg = msgs::OnionHopData {
-                       format: OnionHopDataFormat::FinalNode {
-                               payment_data: Some(FinalOnionHopData {
-                                       payment_secret: expected_payment_secret,
-                                       total_msat: 0x1badca1f
-                               }),
-                               payment_metadata: None,
-                               keysend_preimage: None,
-                       },
-                       amt_to_forward: 0x0badf00d01020304,
+               let outbound_msg = msgs::OutboundOnionPayload::Receive {
+                       payment_data: Some(FinalOnionHopData {
+                               payment_secret: expected_payment_secret,
+                               total_msat: 0x1badca1f
+                       }),
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       amt_msat: 0x0badf00d01020304,
                        outgoing_cltv_value: 0xffffffff,
+                       custom_tlvs: vec![],
                };
-               let encoded_value = msg.encode();
+               let encoded_value = outbound_msg.encode();
                let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
                assert_eq!(encoded_value, target_value);
 
@@ -3606,10 +3625,78 @@ mod tests {
                        amt_msat, outgoing_cltv_value,
                        payment_metadata: None,
                        keysend_preimage: None,
+                       custom_tlvs,
                } = inbound_msg  {
                        assert_eq!(payment_secret, expected_payment_secret);
                        assert_eq!(amt_msat, 0x0badf00d01020304);
                        assert_eq!(outgoing_cltv_value, 0xffffffff);
+                       assert_eq!(custom_tlvs, vec![]);
+               } else { panic!(); }
+       }
+
+       #[test]
+       fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
+               // If custom TLVs have type number within the range reserved for protocol, treat them as if
+               // they're unknown
+               let bad_type_range_tlvs = vec![
+                       ((1 << 16) - 4, vec![42]),
+                       ((1 << 16) - 2, vec![42; 32]),
+               ];
+               let mut msg = msgs::OutboundOnionPayload::Receive {
+                       payment_data: None,
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       custom_tlvs: bad_type_range_tlvs,
+                       amt_msat: 0x0badf00d01020304,
+                       outgoing_cltv_value: 0xffffffff,
+               };
+               let encoded_value = msg.encode();
+               assert!(msgs::InboundOnionPayload::read(&mut Cursor::new(&encoded_value[..])).is_err());
+               let good_type_range_tlvs = vec![
+                       ((1 << 16) - 3, vec![42]),
+                       ((1 << 16) - 1, vec![42; 32]),
+               ];
+               if let msgs::OutboundOnionPayload::Receive { ref mut custom_tlvs, .. } = msg {
+                       *custom_tlvs = good_type_range_tlvs.clone();
+               }
+               let encoded_value = msg.encode();
+               let inbound_msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
+               match inbound_msg {
+                       msgs::InboundOnionPayload::Receive { custom_tlvs, .. } => assert!(custom_tlvs.is_empty()),
+                       _ => panic!(),
+               }
+       }
+
+       #[test]
+       fn encoding_final_onion_hop_data_with_custom_tlvs() {
+               let expected_custom_tlvs = vec![
+                       (5482373483, vec![0x12, 0x34]),
+                       (5482373487, vec![0x42u8; 8]),
+               ];
+               let msg = msgs::OutboundOnionPayload::Receive {
+                       payment_data: None,
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       custom_tlvs: expected_custom_tlvs.clone(),
+                       amt_msat: 0x0badf00d01020304,
+                       outgoing_cltv_value: 0xffffffff,
+               };
+               let encoded_value = msg.encode();
+               let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
+               assert_eq!(encoded_value, target_value);
+               let inbound_msg: msgs::InboundOnionPayload = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
+               if let msgs::InboundOnionPayload::Receive {
+                       payment_data: None,
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       custom_tlvs,
+                       amt_msat,
+                       outgoing_cltv_value,
+                       ..
+               } = inbound_msg {
+                       assert_eq!(custom_tlvs, expected_custom_tlvs);
+                       assert_eq!(amt_msat, 0x0badf00d01020304);
+                       assert_eq!(outgoing_cltv_value, 0xffffffff);
                } else { panic!(); }
        }
 
@@ -3765,20 +3852,18 @@ mod tests {
        // see above test, needs to be a separate method for use of the serialization macros.
        fn encode_big_payload() -> Result<Vec<u8>, io::Error> {
                use crate::util::ser::HighZeroBytesDroppedBigSize;
-               let payload = msgs::OnionHopData {
-                       format: OnionHopDataFormat::NonFinalNode {
-                               short_channel_id: 0xdeadbeef1bad1dea,
-                       },
+               let payload = msgs::OutboundOnionPayload::Forward {
+                       short_channel_id: 0xdeadbeef1bad1dea,
                        amt_to_forward: 1000,
                        outgoing_cltv_value: 0xffffffff,
                };
                let mut encoded_payload = Vec::new();
                let test_bytes = vec![42u8; 1000];
-               if let OnionHopDataFormat::NonFinalNode { short_channel_id } = payload.format {
+               if let msgs::OutboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = payload {
                        _encode_varint_length_prefixed_tlv!(&mut encoded_payload, {
                                (1, test_bytes, required_vec),
-                               (2, HighZeroBytesDroppedBigSize(payload.amt_to_forward), required),
-                               (4, HighZeroBytesDroppedBigSize(payload.outgoing_cltv_value), required),
+                               (2, HighZeroBytesDroppedBigSize(amt_to_forward), required),
+                               (4, HighZeroBytesDroppedBigSize(outgoing_cltv_value), required),
                                (6, short_channel_id, required)
                        });
                }