From: Antoine Riard Date: Sun, 16 Dec 2018 03:10:47 +0000 (-0500) Subject: Implement serialize/deserialize for Router. X-Git-Tag: v0.0.12~239^2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=c7b8c312ce72e8ce3e1d898b64265721eff0be8f;p=rust-lightning Implement serialize/deserialize for Router. Extend route_test to check if serialize/deserialize of NetworkMap works. Add PartialEq traits on some Router's structs. Modify also UnsignedNodeAnnouncement serialization --- diff --git a/src/ln/msgs.rs b/src/ln/msgs.rs index 494abf19..4c6524af 100644 --- a/src/ln/msgs.rs +++ b/src/ln/msgs.rs @@ -26,7 +26,7 @@ use std::{cmp, fmt}; use std::io::Read; use std::result::Result; -use util::{byte_utils, events}; +use util::events; use util::ser::{Readable, Writeable, Writer}; use ln::channelmanager::{PaymentPreimage, PaymentHash}; @@ -47,7 +47,6 @@ pub enum DecodeError { /// node_announcement included more than one address of a given type! ExtraAddressesPerType, /// A length descriptor in the packet didn't describe the later data correctly - /// (currently only generated in node_announcement) BadLengthDescriptor, /// Error from std::io Io(::std::io::Error), @@ -336,7 +335,7 @@ pub struct AnnouncementSignatures { } /// An address which can be used to connect to a remote peer -#[derive(Clone)] +#[derive(PartialEq, Clone)] pub enum NetAddress { /// An IPv4 address/port on which the peer is listenting. IPv4 { @@ -382,9 +381,84 @@ impl NetAddress { &NetAddress::OnionV3 {..} => { 4 }, } } + + /// Strict byte-length of address descriptor, 1-byte type not recorded + fn len(&self) -> u16 { + match self { + &NetAddress::IPv4 { .. } => { 6 }, + &NetAddress::IPv6 { .. } => { 18 }, + &NetAddress::OnionV2 { .. } => { 12 }, + &NetAddress::OnionV3 { .. } => { 37 }, + } + } } -#[derive(Clone)] +impl Writeable for NetAddress { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + match self { + &NetAddress::IPv4 { ref addr, ref port } => { + 1u8.write(writer)?; + addr.write(writer)?; + port.write(writer)?; + }, + &NetAddress::IPv6 { ref addr, ref port } => { + 2u8.write(writer)?; + addr.write(writer)?; + port.write(writer)?; + }, + &NetAddress::OnionV2 { ref addr, ref port } => { + 3u8.write(writer)?; + addr.write(writer)?; + port.write(writer)?; + }, + &NetAddress::OnionV3 { ref ed25519_pubkey, ref checksum, ref version, ref port } => { + 4u8.write(writer)?; + ed25519_pubkey.write(writer)?; + checksum.write(writer)?; + version.write(writer)?; + port.write(writer)?; + } + } + Ok(()) + } +} + +impl Readable for Result { + fn read(reader: &mut R) -> Result, DecodeError> { + let byte = >::read(reader)?; + match byte { + 1 => { + Ok(Ok(NetAddress::IPv4 { + addr: Readable::read(reader)?, + port: Readable::read(reader)?, + })) + }, + 2 => { + Ok(Ok(NetAddress::IPv6 { + addr: Readable::read(reader)?, + port: Readable::read(reader)?, + })) + }, + 3 => { + Ok(Ok(NetAddress::OnionV2 { + addr: Readable::read(reader)?, + port: Readable::read(reader)?, + })) + }, + 4 => { + Ok(Ok(NetAddress::OnionV3 { + ed25519_pubkey: Readable::read(reader)?, + checksum: Readable::read(reader)?, + version: Readable::read(reader)?, + port: Readable::read(reader)?, + })) + }, + _ => return Ok(Err(byte)), + } + } +} + +#[derive(PartialEq, Clone)] // Only exposed as broadcast of node_announcement should be filtered by node_id /// The unsigned part of a node_announcement pub struct UnsignedNodeAnnouncement { @@ -401,7 +475,7 @@ pub struct UnsignedNodeAnnouncement { pub(crate) excess_address_data: Vec, pub(crate) excess_data: Vec, } -#[derive(Clone)] +#[derive(PartialEq, Clone)] /// A node_announcement message to be sent or received from a peer pub struct NodeAnnouncement { pub(crate) signature: Signature, @@ -1192,38 +1266,17 @@ impl Writeable for UnsignedNodeAnnouncement { w.write_all(&self.rgb)?; self.alias.write(w)?; - let mut addr_slice = Vec::with_capacity(self.addresses.len() * 18); let mut addrs_to_encode = self.addresses.clone(); addrs_to_encode.sort_unstable_by(|a, b| { a.get_id().cmp(&b.get_id()) }); addrs_to_encode.dedup_by(|a, b| { a.get_id() == b.get_id() }); - for addr in addrs_to_encode.iter() { - match addr { - &NetAddress::IPv4{addr, port} => { - addr_slice.push(1); - addr_slice.extend_from_slice(&addr); - addr_slice.extend_from_slice(&byte_utils::be16_to_array(port)); - }, - &NetAddress::IPv6{addr, port} => { - addr_slice.push(2); - addr_slice.extend_from_slice(&addr); - addr_slice.extend_from_slice(&byte_utils::be16_to_array(port)); - }, - &NetAddress::OnionV2{addr, port} => { - addr_slice.push(3); - addr_slice.extend_from_slice(&addr); - addr_slice.extend_from_slice(&byte_utils::be16_to_array(port)); - }, - &NetAddress::OnionV3{ed25519_pubkey, checksum, version, port} => { - addr_slice.push(4); - addr_slice.extend_from_slice(&ed25519_pubkey); - addr_slice.extend_from_slice(&byte_utils::be16_to_array(checksum)); - addr_slice.push(version); - addr_slice.extend_from_slice(&byte_utils::be16_to_array(port)); - }, - } + let mut addr_len = 0; + for addr in &addrs_to_encode { + addr_len += 1 + addr.len(); + } + (addr_len + self.excess_address_data.len() as u16).write(w)?; + for addr in addrs_to_encode { + addr.write(w)?; } - ((addr_slice.len() + self.excess_address_data.len()) as u16).write(w)?; - w.write_all(&addr_slice[..])?; w.write_all(&self.excess_address_data[..])?; w.write_all(&self.excess_data[..])?; Ok(()) @@ -1242,112 +1295,77 @@ impl Readable for UnsignedNodeAnnouncement { r.read_exact(&mut rgb)?; let alias: [u8; 32] = Readable::read(r)?; - let addrlen: u16 = Readable::read(r)?; + let addr_len: u16 = Readable::read(r)?; + let mut addresses: Vec = Vec::with_capacity(4); let mut addr_readpos = 0; - let mut addresses = Vec::with_capacity(4); - let mut f: u8 = 0; - let mut excess = 0; + let mut excess = false; + let mut excess_byte = 0; loop { - if addrlen <= addr_readpos { break; } - f = Readable::read(r)?; - match f { - 1 => { - if addresses.len() > 0 { - return Err(DecodeError::ExtraAddressesPerType); - } - if addrlen < addr_readpos + 1 + 6 { - return Err(DecodeError::BadLengthDescriptor); - } - addresses.push(NetAddress::IPv4 { - addr: { - let mut addr = [0; 4]; - r.read_exact(&mut addr)?; - addr + if addr_len <= addr_readpos { break; } + match Readable::read(r) { + Ok(Ok(addr)) => { + match addr { + NetAddress::IPv4 { .. } => { + if addresses.len() > 0 { + return Err(DecodeError::ExtraAddressesPerType); + } }, - port: Readable::read(r)?, - }); - addr_readpos += 1 + 6 - }, - 2 => { - if addresses.len() > 1 || (addresses.len() == 1 && addresses[0].get_id() != 1) { - return Err(DecodeError::ExtraAddressesPerType); - } - if addrlen < addr_readpos + 1 + 18 { - return Err(DecodeError::BadLengthDescriptor); - } - addresses.push(NetAddress::IPv6 { - addr: { - let mut addr = [0; 16]; - r.read_exact(&mut addr)?; - addr + NetAddress::IPv6 { .. } => { + if addresses.len() > 1 || (addresses.len() == 1 && addresses[0].get_id() != 1) { + return Err(DecodeError::ExtraAddressesPerType); + } }, - port: Readable::read(r)?, - }); - addr_readpos += 1 + 18 - }, - 3 => { - if addresses.len() > 2 || (addresses.len() > 0 && addresses.last().unwrap().get_id() > 2) { - return Err(DecodeError::ExtraAddressesPerType); - } - if addrlen < addr_readpos + 1 + 12 { - return Err(DecodeError::BadLengthDescriptor); - } - addresses.push(NetAddress::OnionV2 { - addr: { - let mut addr = [0; 10]; - r.read_exact(&mut addr)?; - addr + NetAddress::OnionV2 { .. } => { + if addresses.len() > 2 || (addresses.len() > 0 && addresses.last().unwrap().get_id() > 2) { + return Err(DecodeError::ExtraAddressesPerType); + } + }, + NetAddress::OnionV3 { .. } => { + if addresses.len() > 3 || (addresses.len() > 0 && addresses.last().unwrap().get_id() > 3) { + return Err(DecodeError::ExtraAddressesPerType); + } }, - port: Readable::read(r)?, - }); - addr_readpos += 1 + 12 - }, - 4 => { - if addresses.len() > 3 || (addresses.len() > 0 && addresses.last().unwrap().get_id() > 3) { - return Err(DecodeError::ExtraAddressesPerType); } - if addrlen < addr_readpos + 1 + 37 { + if addr_len < addr_readpos + 1 + addr.len() { return Err(DecodeError::BadLengthDescriptor); } - addresses.push(NetAddress::OnionV3 { - ed25519_pubkey: Readable::read(r)?, - checksum: Readable::read(r)?, - version: Readable::read(r)?, - port: Readable::read(r)?, - }); - addr_readpos += 1 + 37 + addr_readpos += (1 + addr.len()) as u16; + addresses.push(addr); }, - _ => { excess = 1; break; } + Ok(Err(unknown_descriptor)) => { + excess = true; + excess_byte = unknown_descriptor; + break; + }, + Err(DecodeError::ShortRead) => return Err(DecodeError::BadLengthDescriptor), + Err(e) => return Err(e), } } let mut excess_data = vec![]; - let excess_address_data = if addr_readpos < addrlen { - let mut excess_address_data = vec![0; (addrlen - addr_readpos) as usize]; - r.read_exact(&mut excess_address_data[excess..])?; - if excess == 1 { - excess_address_data[0] = f; + let excess_address_data = if addr_readpos < addr_len { + let mut excess_address_data = vec![0; (addr_len - addr_readpos) as usize]; + r.read_exact(&mut excess_address_data[if excess { 1 } else { 0 }..])?; + if excess { + excess_address_data[0] = excess_byte; } excess_address_data } else { - if excess == 1 { - excess_data.push(f); + if excess { + excess_data.push(excess_byte); } Vec::new() }; - + r.read_to_end(&mut excess_data)?; Ok(UnsignedNodeAnnouncement { - features: features, - timestamp: timestamp, - node_id: node_id, - rgb: rgb, - alias: alias, - addresses: addresses, - excess_address_data: excess_address_data, - excess_data: { - r.read_to_end(&mut excess_data)?; - excess_data - }, + features, + timestamp, + node_id, + rgb, + alias, + addresses, + excess_address_data, + excess_data, }) } } diff --git a/src/ln/peer_handler.rs b/src/ln/peer_handler.rs index 1ea82547..4edec6ec 100644 --- a/src/ln/peer_handler.rs +++ b/src/ln/peer_handler.rs @@ -478,8 +478,14 @@ impl PeerManager { log_debug!(self, "Got a channel/node announcement with an known required feature flag, you may want to udpate!"); continue; }, - msgs::DecodeError::InvalidValue => return Err(PeerHandleError{ no_connection_possible: false }), - msgs::DecodeError::ShortRead => return Err(PeerHandleError{ no_connection_possible: false }), + msgs::DecodeError::InvalidValue => { + log_debug!(self, "Got an invalid value while deserializing message"); + return Err(PeerHandleError{ no_connection_possible: false }); + }, + msgs::DecodeError::ShortRead => { + log_debug!(self, "Deserialization failed due to shortness of message"); + return Err(PeerHandleError{ no_connection_possible: false }); + }, msgs::DecodeError::ExtraAddressesPerType => { log_debug!(self, "Error decoding message, ignoring due to lnd spec incompatibility. See https://github.com/lightningnetwork/lnd/issues/1407"); continue; diff --git a/src/ln/router.rs b/src/ln/router.rs index 3920d44f..52e03b40 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -15,7 +15,7 @@ use chain::chaininterface::{ChainError, ChainWatchInterface}; use ln::channelmanager; use ln::msgs::{DecodeError,ErrorAction,HandleError,RoutingMessageHandler,NetAddress,GlobalFeatures}; use ln::msgs; -use util::ser::{Writeable, Readable}; +use util::ser::{Writeable, Readable, Writer, ReadableArgs}; use util::logger::Logger; use std::cmp; @@ -78,6 +78,7 @@ impl Readable for Route { } } +#[derive(PartialEq)] struct DirectionalChannelInfo { src_node_id: PublicKey, last_update: u32, @@ -96,6 +97,18 @@ impl std::fmt::Display for DirectionalChannelInfo { } } +impl_writeable!(DirectionalChannelInfo, 0, { + src_node_id, + last_update, + enabled, + cltv_expiry_delta, + htlc_minimum_msat, + fee_base_msat, + fee_proportional_millionths, + last_update_message +}); + +#[derive(PartialEq)] struct ChannelInfo { features: GlobalFeatures, one_to_two: DirectionalChannelInfo, @@ -112,6 +125,14 @@ impl std::fmt::Display for ChannelInfo { } } +impl_writeable!(ChannelInfo, 0, { + features, + one_to_two, + two_to_one, + announcement_message +}); + +#[derive(PartialEq)] struct NodeInfo { #[cfg(feature = "non_bitcoin_chain_hash_routing")] channels: Vec<(u64, Sha256dHash)>, @@ -138,6 +159,68 @@ impl std::fmt::Display for NodeInfo { } } +impl Writeable for NodeInfo { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + (self.channels.len() as u64).write(writer)?; + for ref chan in self.channels.iter() { + chan.write(writer)?; + } + self.lowest_inbound_channel_fee_base_msat.write(writer)?; + self.lowest_inbound_channel_fee_proportional_millionths.write(writer)?; + self.features.write(writer)?; + self.last_update.write(writer)?; + self.rgb.write(writer)?; + self.alias.write(writer)?; + (self.addresses.len() as u64).write(writer)?; + for ref addr in &self.addresses { + addr.write(writer)?; + } + self.announcement_message.write(writer)?; + Ok(()) + } +} + +const MAX_ALLOC_SIZE: u64 = 64*1024; + +impl Readable for NodeInfo { + fn read(reader: &mut R) -> Result { + let channels_count: u64 = Readable::read(reader)?; + let mut channels = Vec::with_capacity(cmp::min(channels_count, MAX_ALLOC_SIZE / 8) as usize); + for _ in 0..channels_count { + channels.push(Readable::read(reader)?); + } + let lowest_inbound_channel_fee_base_msat = Readable::read(reader)?; + let lowest_inbound_channel_fee_proportional_millionths = Readable::read(reader)?; + let features = Readable::read(reader)?; + let last_update = Readable::read(reader)?; + let rgb = Readable::read(reader)?; + let alias = Readable::read(reader)?; + let addresses_count: u64 = Readable::read(reader)?; + let mut addresses = Vec::with_capacity(cmp::min(addresses_count, MAX_ALLOC_SIZE / 40) as usize); + for _ in 0..addresses_count { + match Readable::read(reader) { + Ok(Ok(addr)) => { addresses.push(addr); }, + Ok(Err(_)) => return Err(DecodeError::InvalidValue), + Err(DecodeError::ShortRead) => return Err(DecodeError::BadLengthDescriptor), + _ => unreachable!(), + } + } + let announcement_message = Readable::read(reader)?; + Ok(NodeInfo { + channels, + lowest_inbound_channel_fee_base_msat, + lowest_inbound_channel_fee_proportional_millionths, + features, + last_update, + rgb, + alias, + addresses, + announcement_message + }) + } +} + +#[derive(PartialEq)] struct NetworkMap { #[cfg(feature = "non_bitcoin_chain_hash_routing")] channels: BTreeMap<(u64, Sha256dHash), ChannelInfo>, @@ -147,6 +230,49 @@ struct NetworkMap { our_node_id: PublicKey, nodes: BTreeMap, } + +impl Writeable for NetworkMap { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + (self.channels.len() as u64).write(writer)?; + for (ref chan_id, ref chan_info) in self.channels.iter() { + (*chan_id).write(writer)?; + chan_info.write(writer)?; + } + self.our_node_id.write(writer)?; + (self.nodes.len() as u64).write(writer)?; + for (ref node_id, ref node_info) in self.nodes.iter() { + node_id.write(writer)?; + node_info.write(writer)?; + } + Ok(()) + } +} + +impl Readable for NetworkMap { + fn read(reader: &mut R) -> Result { + let channels_count: u64 = Readable::read(reader)?; + let mut channels = BTreeMap::new(); + for _ in 0..channels_count { + let chan_id: u64 = Readable::read(reader)?; + let chan_info = Readable::read(reader)?; + channels.insert(chan_id, chan_info); + } + let our_node_id = Readable::read(reader)?; + let nodes_count: u64 = Readable::read(reader)?; + let mut nodes = BTreeMap::new(); + for _ in 0..nodes_count { + let node_id = Readable::read(reader)?; + let node_info = Readable::read(reader)?; + nodes.insert(node_id, node_info); + } + Ok(NetworkMap { + channels, + our_node_id, + nodes, + }) + } +} + struct MutNetworkMap<'a> { #[cfg(feature = "non_bitcoin_chain_hash_routing")] channels: &'a mut BTreeMap<(u64, Sha256dHash), ChannelInfo>, @@ -228,6 +354,51 @@ pub struct Router { logger: Arc, } +const SERIALIZATION_VERSION: u8 = 1; +const MIN_SERIALIZATION_VERSION: u8 = 1; + +impl Writeable for Router { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + writer.write_all(&[SERIALIZATION_VERSION; 1])?; + writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?; + + let network = self.network_map.read().unwrap(); + network.write(writer)?; + Ok(()) + } +} + +/// Arguments for the creation of a Router that are not deserialized. +/// At a high-level, the process for deserializing a Router and resuming normal operation is: +/// 1) Deserialize the Router by filling in this struct and calling ::read(reaser, args). +/// 2) Register the new Router with your ChainWatchInterface +pub struct RouterReadArgs { + /// The ChainWatchInterface for use in the Router in the future. + /// + /// No calls to the ChainWatchInterface will be made during deserialization. + pub chain_monitor: Arc, + /// The Logger for use in the ChannelManager and which may be used to log information during + /// deserialization. + pub logger: Arc, +} + +impl ReadableArgs for Router { + fn read(reader: &mut R, args: RouterReadArgs) -> Result { + let _ver: u8 = Readable::read(reader)?; + let min_ver: u8 = Readable::read(reader)?; + if min_ver > SERIALIZATION_VERSION { + return Err(DecodeError::UnknownVersion); + } + let network_map = Readable::read(reader)?; + Ok(Router { + secp_ctx: Secp256k1::verification_only(), + network_map: RwLock::new(network_map), + chain_monitor: args.chain_monitor, + logger: args.logger, + }) + } +} + macro_rules! secp_verify_sig { ( $secp_ctx: expr, $msg: expr, $sig: expr, $pubkey: expr ) => { match $secp_ctx.verify($msg, $sig, $pubkey) { @@ -844,7 +1015,9 @@ mod tests { use ln::router::{Router,NodeInfo,NetworkMap,ChannelInfo,DirectionalChannelInfo,RouteHint}; use ln::msgs::GlobalFeatures; use util::test_utils; + use util::test_utils::TestVecWriter; use util::logger::Logger; + use util::ser::{Writeable, Readable}; use bitcoin::util::hash::Sha256dHash; use bitcoin::network::constants::Network; @@ -1438,5 +1611,14 @@ mod tests { assert_eq!(route.hops[4].fee_msat, 2000); assert_eq!(route.hops[4].cltv_expiry_delta, 42); } + + { // Test Router serialization/deserialization + let mut w = TestVecWriter(Vec::new()); + let network = router.network_map.read().unwrap(); + assert!(!network.channels.is_empty()); + assert!(!network.nodes.is_empty()); + network.write(&mut w).unwrap(); + assert!(::read(&mut ::std::io::Cursor::new(&w.0)).unwrap() == *network); + } } } diff --git a/src/util/ser.rs b/src/util/ser.rs index 78fd4c1c..575d4668 100644 --- a/src/util/ser.rs +++ b/src/util/ser.rs @@ -203,6 +203,10 @@ macro_rules! impl_array { } //TODO: performance issue with [u8; size] with impl_array!() +impl_array!(3); // for rgb +impl_array!(4); // for IPv4 +impl_array!(10); // for OnionV2 +impl_array!(16); // for IPv6 impl_array!(32); // for channel id & hmac impl_array!(33); // for PublicKey impl_array!(64); // for Signature diff --git a/src/util/test_utils.rs b/src/util/test_utils.rs index b04f728b..b889e254 100644 --- a/src/util/test_utils.rs +++ b/src/util/test_utils.rs @@ -20,8 +20,8 @@ use secp256k1::{SecretKey, PublicKey}; use std::sync::{Arc,Mutex}; use std::{mem}; -struct VecWriter(Vec); -impl Writer for VecWriter { +pub struct TestVecWriter(pub Vec); +impl Writer for TestVecWriter { fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { self.0.extend_from_slice(buf); Ok(()) @@ -58,7 +58,7 @@ impl channelmonitor::ManyChannelMonitor for TestChannelMonitor { fn add_update_monitor(&self, funding_txo: OutPoint, monitor: channelmonitor::ChannelMonitor) -> Result<(), channelmonitor::ChannelMonitorUpdateErr> { // At every point where we get a monitor update, we should be able to send a useful monitor // to a watchtower and disk... - let mut w = VecWriter(Vec::new()); + let mut w = TestVecWriter(Vec::new()); monitor.write_for_disk(&mut w).unwrap(); assert!(<(Sha256dHash, channelmonitor::ChannelMonitor)>::read( &mut ::std::io::Cursor::new(&w.0), Arc::new(TestLogger::new())).unwrap().1 == monitor);