X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fmsgs.rs;h=cd93e236a00a5d662c2ebf564a439eb60142fea4;hb=9ed4f521f3950980a1a4680b708576890e8c0bcf;hp=483d69e7a39ee9b3b0a0aac35a9dad46882f150f;hpb=c9916437468a130718ab83baa2f346b301b674ba;p=rust-lightning diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 483d69e7..cd93e236 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -29,17 +29,20 @@ use std::io::Read; use std::result::Result; use util::events; -use util::ser::{Readable, Writeable, Writer}; +use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt}; use ln::channelmanager::{PaymentPreimage, PaymentHash}; +/// 21 million * 10^8 * 1000 +pub(crate) const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000; + /// An error in decoding a message or struct. #[derive(Debug)] pub enum DecodeError { /// A version byte specified something we don't know how to handle. /// Includes unknown realm byte in an OnionHopData packet UnknownVersion, - /// Unknown feature mandating we fail to parse message + /// Unknown feature mandating we fail to parse message (eg TLV with an even, unknown type) UnknownRequiredFeature, /// Value was invalid, eg a byte which was supposed to be a bool was something other than a 0 /// or 1, a public key/private key/signature was invalid, text wasn't UTF-8, etc @@ -305,6 +308,58 @@ impl NetAddress { } } +/// A "set" of addresses which enforces that there can be only up to one of each net address type. +pub struct NetAddressSet { + v4: Option, + v6: Option, + onion2: Option, + onion3: Option, +} +impl NetAddressSet { + /// Creates a new, empty, NetAddressSet + pub fn new() -> Self { + NetAddressSet { v4: None, v6: None, onion2: None, onion3: None } + } + + /// Sets the IPv4 socket address in this set, overwriting any previous IPv4 socket addresses + /// (if any). + pub fn set_v4(&mut self, addr: [u8; 4], port: u16) { + self.v4 = Some(NetAddress::IPv4 { addr, port }); + } + /// Sets the IPv6 socket address in this set, overwriting any previous IPv4 socket addresses + /// (if any). + pub fn set_v6(&mut self, addr: [u8; 16], port: u16) { + self.v6 = Some(NetAddress::IPv6 { addr, port }); + } + /// Sets the Tor Onion v2 socket address in this set, overwriting any previous IPv4 socket + /// address (if any). + pub fn set_onion_v2(&mut self, addr: [u8; 10], port: u16) { + self.onion2 = Some(NetAddress::OnionV2 { addr, port }); + } + /// Sets the Tor Onion v3 socket address in this set, overwriting any previous IPv4 socket + /// address (if any). + pub fn set_onion_v3(&mut self, ed25519_pubkey: [u8; 32], checksum: u16, version: u8, port: u16) { + self.onion3 = Some(NetAddress::OnionV3 { ed25519_pubkey, checksum, version, port }); + } + + pub(crate) fn to_vec(mut self) -> Vec { + let mut res = Vec::new(); + if let Some(addr) = self.v4.take() { + res.push(addr); + } + if let Some(addr) = self.v6.take() { + res.push(addr); + } + if let Some(addr) = self.onion2.take() { + res.push(addr); + } + if let Some(addr) = self.onion3.take() { + res.push(addr); + } + res + } +} + impl Writeable for NetAddress { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { match self { @@ -606,22 +661,32 @@ pub trait RoutingMessageHandler : Send + Sync { fn get_next_node_announcements(&self, starting_point: Option<&PublicKey>, batch_amount: u8) -> Vec; } -pub(crate) struct OnionRealm0HopData { - pub(crate) short_channel_id: u64, - pub(crate) amt_to_forward: u64, - pub(crate) outgoing_cltv_value: u32, - // 12 bytes of 0-padding -} - mod fuzzy_internal_msgs { // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize // them from untrusted input): + #[derive(Clone)] + pub(crate) struct FinalOnionHopData { + pub(crate) payment_secret: [u8; 32], + pub(crate) total_msat: u64, + } + + pub(crate) enum OnionHopDataFormat { + Legacy { // aka Realm-0 + short_channel_id: u64, + }, + NonFinalNode { + short_channel_id: u64, + }, + FinalNode { + payment_data: Option, + }, + } - use super::OnionRealm0HopData; pub struct OnionHopData { - pub(crate) realm: u8, - pub(crate) data: OnionRealm0HopData, - pub(crate) hmac: [u8; 32], + pub(crate) format: OnionHopDataFormat, + pub(crate) amt_to_forward: u64, + pub(crate) outgoing_cltv_value: u32, + // 12 bytes of 0-padding for Legacy format } pub struct DecodedOnionErrorPacket { @@ -958,53 +1023,108 @@ impl_writeable!(UpdateAddHTLC, 32+8+8+32+4+1366, { onion_routing_packet }); -impl Writeable for OnionRealm0HopData { - fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - w.size_hint(32); - self.short_channel_id.write(w)?; - self.amt_to_forward.write(w)?; - self.outgoing_cltv_value.write(w)?; - w.write_all(&[0;12])?; - Ok(()) - } -} - -impl Readable for OnionRealm0HopData { - fn read(r: &mut R) -> Result { - Ok(OnionRealm0HopData { - short_channel_id: Readable::read(r)?, - amt_to_forward: Readable::read(r)?, - outgoing_cltv_value: { - let v: u32 = Readable::read(r)?; - r.read_exact(&mut [0; 12])?; - v - } - }) - } -} +impl_writeable!(FinalOnionHopData, 32+8, { + payment_secret, + total_msat +}); impl Writeable for OnionHopData { fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { - w.size_hint(65); - self.realm.write(w)?; - self.data.write(w)?; - self.hmac.write(w)?; + w.size_hint(33); + match self.format { + OnionHopDataFormat::Legacy { short_channel_id } => { + 0u8.write(w)?; + short_channel_id.write(w)?; + self.amt_to_forward.write(w)?; + self.outgoing_cltv_value.write(w)?; + }, + OnionHopDataFormat::NonFinalNode { short_channel_id } => { + encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedVarInt(self.amt_to_forward)), + (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)), + (6, short_channel_id) + }); + }, + OnionHopDataFormat::FinalNode { ref payment_data } => { + if let &Some(ref final_data) = payment_data { + encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedVarInt(self.amt_to_forward)), + (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)), + (8, final_data) + }); + } else { + encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedVarInt(self.amt_to_forward)), + (4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value)) + }); + } + }, + } + match self.format { + OnionHopDataFormat::Legacy { .. } => { + w.write_all(&[0;12])?; + }, + _ => {}, + } Ok(()) } } impl Readable for OnionHopData { - fn read(r: &mut R) -> Result { - Ok(OnionHopData { - realm: { - let r: u8 = Readable::read(r)?; - if r != 0 { - return Err(DecodeError::UnknownVersion); + 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 + })?; + let (format, amt, cltv_value) = if v.0 != 0 { + let mut rd = FixedLengthReader::new(r, v.0); + let mut amt = HighZeroBytesDroppedVarInt(0u64); + let mut cltv_value = HighZeroBytesDroppedVarInt(0u32); + let mut short_id: Option = None; + let mut payment_data: Option = None; + decode_tlv!(&mut rd, { + (2, amt), + (4, cltv_value) + }, { + (6, short_id), + (8, payment_data) + }); + rd.eat_remaining().map_err(|_| DecodeError::ShortRead)?; + let format = if let Some(short_channel_id) = short_id { + if payment_data.is_some() { return Err(DecodeError::InvalidValue); } + OnionHopDataFormat::NonFinalNode { + short_channel_id, } - r - }, - data: Readable::read(r)?, - hmac: Readable::read(r)?, + } else { + if let &Some(ref data) = &payment_data { + if data.total_msat > MAX_VALUE_MSAT { + return Err(DecodeError::InvalidValue); + } + } + OnionHopDataFormat::FinalNode { + payment_data + } + }; + (format, amt.0, cltv_value.0) + } else { + let format = OnionHopDataFormat::Legacy { + short_channel_id: Readable::read(r)?, + }; + let amt: u64 = Readable::read(r)?; + let cltv_value: u32 = Readable::read(r)?; + if amt > MAX_VALUE_MSAT { + return Err(DecodeError::InvalidValue); + } + r.read_exact(&mut [0; 12])?; + (format, amt, cltv_value) + }; + + Ok(OnionHopData { + format, + amt_to_forward: amt, + outgoing_cltv_value: cltv_value, }) } } @@ -1290,9 +1410,9 @@ impl_writeable_len_match!(NodeAnnouncement, { mod tests { use hex; use ln::msgs; - use ln::msgs::{ChannelFeatures, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket}; + use ln::msgs::{ChannelFeatures, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat}; use ln::channelmanager::{PaymentPreimage, PaymentHash}; - use util::ser::Writeable; + use util::ser::{Writeable, Readable}; use bitcoin_hashes::sha256d::Hash as Sha256dHash; use bitcoin_hashes::hex::FromHex; @@ -1304,6 +1424,8 @@ mod tests { use secp256k1::key::{PublicKey,SecretKey}; use secp256k1::{Secp256k1, Message}; + use std::io::Cursor; + #[test] fn encoding_channel_reestablish_no_secret() { let cr = msgs::ChannelReestablish { @@ -1943,4 +2065,56 @@ mod tests { let target_value = hex::decode("004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(encoded_value, target_value); } + + #[test] + fn encoding_legacy_onion_hop_data() { + let msg = msgs::OnionHopData { + format: OnionHopDataFormat::Legacy { + short_channel_id: 0xdeadbeef1bad1dea, + }, + amt_to_forward: 0x0badf00d01020304, + outgoing_cltv_value: 0xffffffff, + }; + let encoded_value = msg.encode(); + let target_value = hex::decode("00deadbeef1bad1dea0badf00d01020304ffffffff000000000000000000000000").unwrap(); + assert_eq!(encoded_value, target_value); + } + + #[test] + fn encoding_nonfinal_onion_hop_data() { + let mut msg = msgs::OnionHopData { + format: OnionHopDataFormat::NonFinalNode { + short_channel_id: 0xdeadbeef1bad1dea, + }, + amt_to_forward: 0x0badf00d01020304, + outgoing_cltv_value: 0xffffffff, + }; + let encoded_value = msg.encode(); + let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap(); + assert_eq!(encoded_value, target_value); + msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); + if let OnionHopDataFormat::NonFinalNode { short_channel_id } = msg.format { + assert_eq!(short_channel_id, 0xdeadbeef1bad1dea); + } else { panic!(); } + assert_eq!(msg.amt_to_forward, 0x0badf00d01020304); + assert_eq!(msg.outgoing_cltv_value, 0xffffffff); + } + + #[test] + fn encoding_final_onion_hop_data() { + let mut msg = msgs::OnionHopData { + format: OnionHopDataFormat::FinalNode { + payment_data: None, + }, + amt_to_forward: 0x0badf00d01020304, + outgoing_cltv_value: 0xffffffff, + }; + let encoded_value = msg.encode(); + let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap(); + assert_eq!(encoded_value, target_value); + msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap(); + if let OnionHopDataFormat::FinalNode { payment_data: None } = msg.format { } else { panic!(); } + assert_eq!(msg.amt_to_forward, 0x0badf00d01020304); + assert_eq!(msg.outgoing_cltv_value, 0xffffffff); + } }