X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmsgs.rs;h=a32b17b9f0d331c50780bec4963ff4e17c3d4306;hb=fa1a0d8531612d16cedc9c1bdcea3e3989649c7c;hp=c2925d7354de4142c109ee79bf7b8c33fb0819ba;hpb=4905df889498ec520932ea6684b0ffabbd226641;p=rust-lightning diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index c2925d73..a32b17b9 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -31,6 +31,8 @@ use bitcoin::blockdata::script::Script; use bitcoin::hash_types::{Txid, BlockHash}; use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; +use ln::onion_utils; +use onion_message; use prelude::*; use core::fmt; @@ -38,9 +40,9 @@ use core::fmt::Debug; use io::{self, Read}; use io_extras::read_to_end; -use util::events::MessageSendEventsProvider; +use util::events::{MessageSendEventsProvider, OnionMessageProvider}; use util::logger; -use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname}; +use util::ser::{BigSize, LengthReadable, Readable, ReadableArgs, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname}; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; @@ -304,6 +306,14 @@ pub struct UpdateAddHTLC { pub(crate) onion_routing_packet: OnionPacket, } + /// An onion message to be sent or received from a peer +#[derive(Clone, Debug, PartialEq)] +pub struct OnionMessage { + /// Used in decrypting the onion packet's payload. + pub blinding_point: PublicKey, + pub(crate) onion_routing_packet: onion_message::Packet, +} + /// An update_fulfill_htlc message to be sent or received from a peer #[derive(Clone, Debug, PartialEq)] pub struct UpdateFulfillHTLC { @@ -905,15 +915,15 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { /// Handle an incoming channel_update message, returning true if it should be forwarded on, /// false or returning an Err otherwise. fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result; - /// Gets a subset of the channel announcements and updates required to dump our routing table - /// to a remote node, starting at the short_channel_id indicated by starting_point and - /// including the batch_amount entries immediately higher in numerical value than starting_point. - fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)>; - /// Gets a subset of the node announcements required to dump our routing table to a remote node, - /// starting at the node *after* the provided publickey and including batch_amount entries - /// immediately higher (as defined by ::cmp) than starting_point. + /// Gets channel announcements and updates required to dump our routing table to a remote node, + /// starting at the short_channel_id indicated by starting_point and including announcements + /// for a single channel. + fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(ChannelAnnouncement, Option, Option)>; + /// Gets a node announcement required to dump our routing table to a remote node, starting at + /// the node *after* the provided pubkey and including up to one announcement immediately + /// higher (as defined by ::cmp) than starting_point. /// If None is provided for starting_point, we start at the first node. - fn get_next_node_announcements(&self, starting_point: Option<&PublicKey>, batch_amount: u8) -> Vec; + fn get_next_node_announcement(&self, starting_point: Option<&PublicKey>) -> Option; /// Called when a connection is established with a peer. This can be used to /// perform routing table synchronization using a strategy defined by the /// implementor. @@ -935,6 +945,18 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider { fn handle_query_short_channel_ids(&self, their_node_id: &PublicKey, msg: QueryShortChannelIds) -> Result<(), LightningError>; } +/// A trait to describe an object that can receive onion messages. +pub trait OnionMessageHandler : OnionMessageProvider { + /// Handle an incoming onion_message message from the given peer. + fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage); + /// Called when a connection is established with a peer. Can be used to track which peers + /// advertise onion message support and are online. + fn peer_connected(&self, their_node_id: &PublicKey, init: &Init); + /// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to + /// drop and refuse to forward onion messages to this peer. + fn peer_disconnected(&self, their_node_id: &PublicKey, no_connection_possible: bool); +} + mod fuzzy_internal_msgs { use prelude::*; use ln::{PaymentPreimage, PaymentSecret}; @@ -993,6 +1015,18 @@ pub(crate) struct OnionPacket { pub(crate) hmac: [u8; 32], } +impl onion_utils::Packet for OnionPacket { + type Data = onion_utils::FixedSizeOnionPacket; + fn new(pubkey: PublicKey, hop_data: onion_utils::FixedSizeOnionPacket, hmac: [u8; 32]) -> Self { + Self { + version: 0, + public_key: Ok(pubkey), + hop_data: hop_data.0, + hmac, + } + } +} + impl PartialEq for OnionPacket { fn eq(&self, other: &OnionPacket) -> bool { for (i, j) in self.hop_data.iter().zip(other.hop_data.iter()) { @@ -1327,17 +1361,40 @@ impl_writeable_msg!(UpdateAddHTLC, { onion_routing_packet }, {}); +impl Readable for OnionMessage { + fn read(r: &mut R) -> Result { + let blinding_point: PublicKey = Readable::read(r)?; + let len: u16 = Readable::read(r)?; + let mut packet_reader = FixedLengthReader::new(r, len as u64); + let onion_routing_packet: onion_message::Packet = ::read(&mut packet_reader)?; + Ok(Self { + blinding_point, + onion_routing_packet, + }) + } +} + +impl Writeable for OnionMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.blinding_point.write(w)?; + let onion_packet_len = self.onion_routing_packet.serialized_length(); + (onion_packet_len as u16).write(w)?; + self.onion_routing_packet.write(w)?; + Ok(()) + } +} + impl Writeable for FinalOnionHopData { fn write(&self, w: &mut W) -> Result<(), io::Error> { self.payment_secret.0.write(w)?; - HighZeroBytesDroppedVarInt(self.total_msat).write(w) + HighZeroBytesDroppedBigSize(self.total_msat).write(w) } } impl Readable for FinalOnionHopData { fn read(r: &mut R) -> Result { let secret: [u8; 32] = Readable::read(r)?; - let amt: HighZeroBytesDroppedVarInt = Readable::read(r)?; + let amt: HighZeroBytesDroppedBigSize = Readable::read(r)?; Ok(Self { payment_secret: PaymentSecret(secret), total_msat: amt.0 }) } } @@ -1354,15 +1411,15 @@ impl Writeable for OnionHopData { }, OnionHopDataFormat::NonFinalNode { short_channel_id } => { encode_varint_length_prefixed_tlv!(w, { - (2, HighZeroBytesDroppedVarInt(self.amt_to_forward), required), - (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value), required), + (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required), (6, short_channel_id, required) }); }, OnionHopDataFormat::FinalNode { ref payment_data, ref keysend_preimage } => { encode_varint_length_prefixed_tlv!(w, { - (2, HighZeroBytesDroppedVarInt(self.amt_to_forward), required), - (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value), required), + (2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required), (8, payment_data, option), (5482373484, keysend_preimage, option) }); @@ -1373,27 +1430,22 @@ impl Writeable for OnionHopData { } impl Readable for OnionHopData { - fn read(mut r: &mut R) -> Result { - use bitcoin::consensus::encode::{Decodable, Error, VarInt}; - let v: VarInt = Decodable::consensus_decode(&mut r) - .map_err(|e| match e { - Error::Io(ioe) => DecodeError::from(ioe), - _ => DecodeError::InvalidValue - })?; + fn read(r: &mut R) -> Result { + let b: BigSize = Readable::read(r)?; const LEGACY_ONION_HOP_FLAG: u64 = 0; - let (format, amt, cltv_value) = if v.0 != LEGACY_ONION_HOP_FLAG { - let mut rd = FixedLengthReader::new(r, v.0); - let mut amt = HighZeroBytesDroppedVarInt(0u64); - let mut cltv_value = HighZeroBytesDroppedVarInt(0u32); + let (format, amt, cltv_value) = if b.0 != LEGACY_ONION_HOP_FLAG { + let mut rd = FixedLengthReader::new(r, b.0); + let mut amt = HighZeroBytesDroppedBigSize(0u64); + let mut cltv_value = HighZeroBytesDroppedBigSize(0u32); let mut short_id: Option = None; let mut payment_data: Option = None; let mut keysend_preimage: Option = None; - // The TLV type is chosen to be compatible with lnd and c-lightning. decode_tlv_stream!(&mut rd, { (2, amt, required), (4, cltv_value, required), (6, short_id, option), (8, payment_data, option), + // See https://github.com/lightning/blips/blob/master/blip-0003.md (5482373484, keysend_preimage, option) }); rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; @@ -1435,6 +1487,14 @@ impl Readable for OnionHopData { } } +// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and +// onion message packets. +impl ReadableArgs<()> for OnionHopData { + fn read(r: &mut R, _arg: ()) -> Result { + ::read(r) + } +} + impl Writeable for Ping { fn write(&self, w: &mut W) -> Result<(), io::Error> { self.ponglen.write(w)?; @@ -1860,7 +1920,7 @@ mod tests { use bitcoin::secp256k1::{PublicKey,SecretKey}; use bitcoin::secp256k1::{Secp256k1, Message}; - use io::Cursor; + use io::{self, Cursor}; use prelude::*; use core::convert::TryFrom; @@ -2771,4 +2831,40 @@ mod tests { assert_eq!(gossip_timestamp_filter.first_timestamp, 1590000000); assert_eq!(gossip_timestamp_filter.timestamp_range, 0xffff_ffff); } + + #[test] + fn decode_onion_hop_data_len_as_bigsize() { + // Tests that we can decode an onion payload that is >253 bytes. + // Previously, receiving a payload of this size could've caused us to fail to decode a valid + // payload, because we were decoding the length (a BigSize, big-endian) as a VarInt + // (little-endian). + + // Encode a test onion payload with a big custom TLV such that it's >253 bytes, forcing the + // payload length to be encoded over multiple bytes rather than a single u8. + let big_payload = encode_big_payload().unwrap(); + let mut rd = Cursor::new(&big_payload[..]); + ::read(&mut rd).unwrap(); + } + // see above test, needs to be a separate method for use of the serialization macros. + fn encode_big_payload() -> Result, io::Error> { + use util::ser::HighZeroBytesDroppedBigSize; + let payload = msgs::OnionHopData { + format: OnionHopDataFormat::NonFinalNode { + 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 { + encode_varint_length_prefixed_tlv!(&mut encoded_payload, { + (1, test_bytes, vec_type), + (2, HighZeroBytesDroppedBigSize(payload.amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(payload.outgoing_cltv_value), required), + (6, short_channel_id, required) + }); + } + Ok(encoded_payload) + } }