pub mod message; pub mod transcode; use std::collections::{BTreeMap, HashMap, VecDeque}; use std::convert::TryFrom; use std::net; use std::ops::Deref; use std::string::FromUtf8Error; use std::{io, mem}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use nakamoto_net as nakamoto; use nakamoto_net::{Link, LocalTime}; use crate::address; use crate::crypto::{PublicKey, Signature, Signer, Unverified}; use crate::deserializer::Deserializer; use crate::git; use crate::git::fmt; use crate::hash::Digest; use crate::identity::Id; use crate::node; use crate::service; use crate::service::reactor::Io; use crate::service::{filter, routing, session}; use crate::storage::refs::Refs; use crate::storage::refs::SignedRefs; use crate::storage::WriteStorage; use crate::wire::transcode::{Framer, Handshake, HandshakeResult, MuxMsg, Transcode}; /// The default type we use to represent sizes on the wire. /// /// Since wire messages are limited to 64KB by the transport layer, /// two bytes is enough to represent any message. /// /// Note that in certain cases, we may use a smaller type. pub type Size = u16; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("i/o: {0}")] Io(#[from] io::Error), #[error("UTF-8 error: {0}")] FromUtf8(#[from] FromUtf8Error), #[error("invalid size: expected {expected}, got {actual}")] InvalidSize { expected: usize, actual: usize }, #[error("invalid filter size: {0}")] InvalidFilterSize(usize), #[error(transparent)] InvalidRefName(#[from] fmt::Error), #[error("unknown address type `{0}`")] UnknownAddressType(u8), #[error("unknown message type `{0}`")] UnknownMessageType(u16), } impl Error { /// Whether we've reached the end of file. This will be true when we fail to decode /// a message because there's not enough data in the stream. pub fn is_eof(&self) -> bool { matches!(self, Self::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof) } } /// Things that can be encoded as binary. pub trait Encode { fn encode(&self, writer: &mut W) -> Result; } /// Things that can be decoded from binary. pub trait Decode: Sized { fn decode(reader: &mut R) -> Result; } /// Encode an object into a vector. pub fn serialize(data: &T) -> Vec { let mut buffer = Vec::new(); let len = data .encode(&mut buffer) .expect("in-memory writes don't error"); debug_assert_eq!(len, buffer.len()); buffer } /// Decode an object from a vector. pub fn deserialize(data: &[u8]) -> Result { let mut cursor = io::Cursor::new(data); T::decode(&mut cursor) } impl Encode for u8 { fn encode(&self, writer: &mut W) -> Result { writer.write_u8(*self)?; Ok(mem::size_of::()) } } impl Encode for u16 { fn encode(&self, writer: &mut W) -> Result { writer.write_u16::(*self)?; Ok(mem::size_of::()) } } impl Encode for u32 { fn encode(&self, writer: &mut W) -> Result { writer.write_u32::(*self)?; Ok(mem::size_of::()) } } impl Encode for u64 { fn encode(&self, writer: &mut W) -> Result { writer.write_u64::(*self)?; Ok(mem::size_of::()) } } impl Encode for PublicKey { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for &[u8; T] { fn encode(&self, writer: &mut W) -> Result { // TODO: This can be removed when the clippy bugs are fixed #[allow(clippy::explicit_auto_deref)] writer.write_all(*self)?; Ok(mem::size_of::()) } } impl Encode for [u8; T] { fn encode(&self, writer: &mut W) -> Result { writer.write_all(self)?; Ok(mem::size_of::()) } } impl Encode for &[T] where T: Encode, { fn encode(&self, writer: &mut W) -> Result { let mut n = (self.len() as Size).encode(writer)?; for item in self.iter() { n += item.encode(writer)?; } Ok(n) } } impl Encode for &str { fn encode(&self, writer: &mut W) -> Result { assert!(self.len() <= u8::MAX as usize); let n = (self.len() as u8).encode(writer)?; let bytes = self.as_bytes(); // Nb. Don't use the [`Encode`] instance here for &[u8], because we are prefixing the // length ourselves. writer.write_all(bytes)?; Ok(n + bytes.len()) } } impl Encode for String { fn encode(&self, writer: &mut W) -> Result { self.as_str().encode(writer) } } impl Encode for git::Url { fn encode(&self, writer: &mut W) -> Result { self.to_string().encode(writer) } } impl Encode for Digest { fn encode(&self, writer: &mut W) -> Result { self.as_ref().encode(writer) } } impl Encode for Id { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for Refs { fn encode(&self, writer: &mut W) -> Result { let len: Size = self .len() .try_into() .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; let mut n = len.encode(writer)?; for (name, oid) in self.iter() { n += name.as_str().encode(writer)?; n += oid.encode(writer)?; } Ok(n) } } impl Encode for Signature { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for git::Oid { fn encode(&self, writer: &mut W) -> Result { // Nb. We use length-encoding here to support future SHA-2 object ids. self.as_bytes().encode(writer) } } //////////////////////////////////////////////////////////////////////////////// impl Decode for PublicKey { fn decode(reader: &mut R) -> Result { let buf: [u8; 32] = Decode::decode(reader)?; PublicKey::try_from(buf) .map_err(|e| Error::Io(io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))) } } impl Decode for Refs { fn decode(reader: &mut R) -> Result { let len = Size::decode(reader)?; let mut refs = BTreeMap::new(); for _ in 0..len { let name = String::decode(reader)?; let name = git::RefString::try_from(name).map_err(Error::from)?; let oid = git::Oid::decode(reader)?; refs.insert(name, oid); } Ok(refs.into()) } } impl Decode for git::Oid { fn decode(reader: &mut R) -> Result { let len = Size::decode(reader)? as usize; #[allow(non_upper_case_globals)] const expected: usize = mem::size_of::(); if len != expected { return Err(Error::InvalidSize { expected, actual: len, }); } let buf: [u8; expected] = Decode::decode(reader)?; let oid = git::raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size"); let oid = git::Oid::from(oid); Ok(oid) } } impl Decode for Signature { fn decode(reader: &mut R) -> Result { let bytes: [u8; 64] = Decode::decode(reader)?; Ok(Signature::from(bytes)) } } impl Decode for u8 { fn decode(reader: &mut R) -> Result { reader.read_u8().map_err(Error::from) } } impl Decode for u16 { fn decode(reader: &mut R) -> Result { reader.read_u16::().map_err(Error::from) } } impl Decode for u32 { fn decode(reader: &mut R) -> Result { reader.read_u32::().map_err(Error::from) } } impl Decode for u64 { fn decode(reader: &mut R) -> Result { reader.read_u64::().map_err(Error::from) } } impl Decode for [u8; N] { fn decode(reader: &mut R) -> Result { let mut ary = [0; N]; reader.read_exact(&mut ary)?; Ok(ary) } } impl Decode for Vec where T: Decode, { fn decode(reader: &mut R) -> Result { let len: Size = Size::decode(reader)?; let mut vec = Vec::with_capacity(len as usize); for _ in 0..len { let item = T::decode(reader)?; vec.push(item); } Ok(vec) } } impl Decode for String { fn decode(reader: &mut R) -> Result { let len = u8::decode(reader)?; let mut bytes = vec![0; len as usize]; reader.read_exact(&mut bytes)?; let string = String::from_utf8(bytes)?; Ok(string) } } impl Decode for Id { fn decode(reader: &mut R) -> Result { let oid: git::Oid = Decode::decode(reader)?; Ok(Self::from(oid)) } } impl Decode for Digest { fn decode(reader: &mut R) -> Result { let bytes: [u8; 32] = Decode::decode(reader)?; Ok(Self::from(bytes)) } } impl Encode for filter::Filter { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.deref().as_bytes().encode(writer)?; Ok(n) } } impl Decode for filter::Filter { fn decode(reader: &mut R) -> Result { let size: usize = Size::decode(reader)? as usize; if !filter::FILTER_SIZES.contains(&size) { return Err(Error::InvalidFilterSize(size)); } let mut bytes = vec![0; size]; reader.read_exact(&mut bytes[..])?; let f = filter::BloomFilter::from(bytes); debug_assert_eq!(f.hashes(), filter::FILTER_HASHES); Ok(Self::from(f)) } } impl Encode for SignedRefs { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.refs.encode(writer)?; n += self.signature.encode(writer)?; Ok(n) } } impl Decode for SignedRefs { fn decode(reader: &mut R) -> Result { let refs = Refs::decode(reader)?; let signature = Signature::decode(reader)?; Ok(Self::new(refs, signature)) } } impl Encode for node::Features { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Decode for node::Features { fn decode(reader: &mut R) -> Result { let features = u64::decode(reader)?; Ok(Self::from(features)) } } #[derive(Debug)] pub struct Inbox { pub pipeline: Framer, pub deserializer: Deserializer, } #[derive(Debug)] pub struct Wire { handshakes: HashMap, inner_queue: VecDeque>, inboxes: HashMap>, inner: service::Service, } impl Wire { pub fn new(inner: service::Service) -> Self { Self { handshakes: HashMap::new(), inner_queue: Default::default(), inboxes: HashMap::new(), inner, } } } impl nakamoto::Protocol for Wire where R: routing::Store, S: address::Store, W: WriteStorage + 'static, G: Signer, H: Handshake, { type Event = service::Event; type Command = service::Command; type DisconnectReason = service::DisconnectReason; fn initialize(&mut self, time: LocalTime) { self.inner.initialize(time) } fn tick(&mut self, now: nakamoto::LocalTime) { self.inner.tick(now) } fn wake(&mut self) { self.inner.wake() } fn command(&mut self, cmd: Self::Command) { self.inner.command(cmd) } fn attempted(&mut self, addr: &std::net::SocketAddr) { self.inner.attempted(addr) } fn connected(&mut self, addr: net::SocketAddr, local_addr: &net::SocketAddr, link: Link) { self.handshakes.insert(addr, H::new(link)); self.inner.connecting(addr, local_addr, link) } fn disconnected( &mut self, addr: &net::SocketAddr, reason: nakamoto::DisconnectReason, ) { self.handshakes.remove(addr); self.inboxes.remove(addr); self.inner.disconnected(addr, &reason) } fn received_bytes(&mut self, addr: &net::SocketAddr, raw_bytes: &[u8]) { if let Some(handshake) = self.handshakes.remove(addr) { debug_assert!(!self.inboxes.contains_key(addr)); match handshake.step(raw_bytes) { HandshakeResult::Next(handshake, reply) => { self.handshakes.insert(*addr, handshake); if !reply.is_empty() { self.inner_queue .push_back(nakamoto::Io::Write(*addr, reply)); } return; } HandshakeResult::Complete(transcoder, reply, link) => { log::debug!("handshake with peer {} is complete", addr); if !reply.is_empty() { self.inner_queue .push_back(nakamoto::Io::Write(*addr, reply)); } let pipeline = Framer::new(transcoder); self.inboxes.insert( *addr, Inbox { pipeline, deserializer: Deserializer::new(256), }, ); self.inner.connected(*addr, link); } HandshakeResult::Error(err) => { log::error!("invalid handshake input. Details: {}", err); self.inner_queue.push_back(nakamoto::Io::Disconnect( *addr, service::DisconnectReason::Error(session::Error::Handshake( err.to_string(), )), )); return; } } } if let Some(Inbox { pipeline, deserializer, }) = self.inboxes.get_mut(addr) { pipeline.input(raw_bytes); for frame in pipeline { let Ok(msg) = MuxMsg::try_from(frame) else { // TODO: Disconnect peer. log::error!("Message frame with invalid channel structure from {}", addr); return; }; match msg.channel { 0 => deserializer.input(&msg.data), 1 => { /* TODO: Send to git worker */ } wrong_channel => { // TODO: Disconnect peer. log::error!("Wrong message channel {} from peer {}", wrong_channel, addr); return; } }; } for message in deserializer { match message { Ok(msg) => self.inner.received_message(addr, msg), Err(err) => { // TODO: Disconnect peer. log::error!("Invalid message received from {}: {}", addr, err); return; } } } } else { log::debug!("Received message from unknown peer {}", addr); } } } impl Iterator for Wire { type Item = nakamoto::Io; fn next(&mut self) -> Option { if let Some(event) = self.inner_queue.pop_front() { return Some(event); } match self.inner.next() { Some(Io::Write(addr, msgs)) => { let mut buf = Vec::new(); for msg in msgs { log::debug!("Write {:?} to {}", &msg, addr.ip()); msg.encode(&mut buf) .expect("writing to an in-memory buffer doesn't fail"); } let Inbox { pipeline, .. } = self.inboxes.get_mut(&addr).expect( "broken handshake implementation: data sent before handshake was complete", ); let data = pipeline.frame(buf).expect("oversized data for a frame"); let msg = MuxMsg { channel: 0, data }; Some(nakamoto::Io::Write(addr, msg.into())) } Some(Io::Event(e)) => Some(nakamoto::Io::Event(e)), Some(Io::Connect(a)) => Some(nakamoto::Io::Connect(a)), Some(Io::Disconnect(a, r)) => Some(nakamoto::Io::Disconnect(a, r)), Some(Io::Wakeup(d)) => Some(nakamoto::Io::Wakeup(d)), None => None, } } } #[cfg(test)] mod tests { use super::*; use quickcheck_macros::quickcheck; use crate::crypto::Unverified; use crate::storage::refs::SignedRefs; use crate::test::{arbitrary, assert_matches}; #[quickcheck] fn prop_u8(input: u8) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u16(input: u16) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u32(input: u32) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u64(input: u64) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_string(input: String) -> quickcheck::TestResult { if input.len() > u8::MAX as usize { return quickcheck::TestResult::discard(); } assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); quickcheck::TestResult::passed() } #[quickcheck] fn prop_vec(input: Vec) { assert_eq!( deserialize::>(&serialize(&input.as_slice())).unwrap(), input ); } #[quickcheck] fn prop_pubkey(input: PublicKey) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_filter(input: filter::Filter) { assert_eq!( deserialize::(&serialize(&input)).unwrap(), input ); } #[quickcheck] fn prop_id(input: Id) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_digest(input: Digest) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_refs(input: Refs) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_signature(input: arbitrary::ByteArray<64>) { let signature = Signature::from(input.into_inner()); assert_eq!( deserialize::(&serialize(&signature)).unwrap(), signature ); } #[quickcheck] fn prop_oid(input: arbitrary::ByteArray<20>) { let oid = git::Oid::try_from(input.into_inner().as_slice()).unwrap(); assert_eq!(deserialize::(&serialize(&oid)).unwrap(), oid); } #[quickcheck] fn prop_signed_refs(input: SignedRefs) { assert_eq!( deserialize::>(&serialize(&input)).unwrap(), input ); } #[test] fn test_string() { assert_eq!( serialize(&String::from("hello")), vec![5, b'h', b'e', b'l', b'l', b'o'] ); } #[test] fn test_filter_invalid() { let b = bloomy::BloomFilter::with_size(filter::FILTER_SIZE_M / 3); let f = filter::Filter::from(b); let bytes = serialize(&f); assert_matches!( deserialize::(&bytes).unwrap_err(), Error::InvalidFilterSize(_) ); } }