From cb4939fd752544fb962dcc7dab5843ee97d1f51b Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 8 Sep 2022 22:16:36 +0200 Subject: [PATCH] node: More work on encoding Signed-off-by: Alexis Sellier --- node/src/crypto.rs | 4 +- node/src/protocol/message.rs | 80 +++++++++++++++++++++++++++--------- node/src/protocol/wire.rs | 8 ++++ node/src/storage.rs | 4 +- node/src/storage/refs.rs | 6 +-- node/src/test/arbitrary.rs | 33 ++++++++++++--- 6 files changed, 101 insertions(+), 34 deletions(-) diff --git a/node/src/crypto.rs b/node/src/crypto.rs index 40bab804..ec42b3eb 100644 --- a/node/src/crypto.rs +++ b/node/src/crypto.rs @@ -9,10 +9,10 @@ pub use ed25519::Error; pub use ed25519::Signature; /// Verified (used as type witness). -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Verified; /// Unverified (used as type witness). -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Unverified; pub trait Signer: Send + Sync { diff --git a/node/src/protocol/message.rs b/node/src/protocol/message.rs index 0fc5b38c..85bb04a6 100644 --- a/node/src/protocol/message.rs +++ b/node/src/protocol/message.rs @@ -1,7 +1,6 @@ -use std::net; +use std::{io, net}; use byteorder::{NetworkEndian, ReadBytesExt}; -use serde::{Deserialize, Serialize}; use crate::crypto; use crate::git; @@ -12,7 +11,7 @@ use crate::storage; use crate::storage::refs::SignedRefs; /// Message envelope. All messages sent over the network are wrapped in this type. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Envelope { /// Network magic constant. Used to differentiate networks. pub magic: u32, @@ -23,7 +22,7 @@ pub struct Envelope { /// Advertized node feature. Signals what services the node supports. pub type NodeFeatures = [u8; 32]; -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] // TODO: We should check the length and charset when deserializing. pub struct Hostname(String); @@ -90,7 +89,7 @@ impl TryFrom for AddressType { } /// Peer public protocol address. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Address { Ipv4 { ip: net::Ipv4Addr, @@ -196,32 +195,63 @@ impl wire::Decode for Address { } } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeAnnouncement { /// Node identifier. - id: NodeId, + pub id: NodeId, /// Advertized features. - features: NodeFeatures, + pub features: NodeFeatures, /// Monotonic timestamp. - timestamp: Timestamp, + pub timestamp: Timestamp, /// Non-unique alias. Must be valid UTF-8. - alias: [u8; 32], + pub alias: [u8; 32], /// Announced addresses. - addresses: Vec
, + pub addresses: Vec
, } impl NodeAnnouncement { /// Verify a signature on this message. pub fn verify(&self, signature: &crypto::Signature) -> bool { - // TODO: Use binary serialization. - let msg = serde_json::to_vec(self).unwrap(); + let msg = wire::serialize(self); self.id.verify(signature, &msg).is_ok() } } +impl wire::Encode for NodeAnnouncement { + fn encode(&self, writer: &mut W) -> Result { + let mut n = 0; + + n += self.id.encode(writer)?; + n += self.features.encode(writer)?; + n += self.timestamp.encode(writer)?; + n += self.alias.encode(writer)?; + n += self.addresses.as_slice().encode(writer)?; + + Ok(n) + } +} + +impl wire::Decode for NodeAnnouncement { + fn decode(reader: &mut R) -> Result { + let id = NodeId::decode(reader)?; + let features = NodeFeatures::decode(reader)?; + let timestamp = Timestamp::decode(reader)?; + let alias = wire::Decode::decode(reader)?; + let addresses = Vec::
::decode(reader)?; + + Ok(Self { + id, + features, + timestamp, + alias, + addresses, + }) + } +} + /// Message payload. /// These are the messages peers send to each other. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Message { /// Say hello to a peer. This is the first message sent to a peer after connection. Hello { @@ -233,10 +263,10 @@ pub enum Message { git: git::Url, }, Node { - /// Signature over the announcement, by the node being announced. - signature: crypto::Signature, /// Unsigned node announcement. announcement: NodeAnnouncement, + /// Signature over the announcement, by the node being announced. + signature: crypto::Signature, }, /// Get a peer's inventory. GetInventory { ids: Vec }, @@ -271,7 +301,7 @@ impl Message { } pub fn node(announcement: NodeAnnouncement, signer: S) -> Self { - let msg = serde_json::to_vec(&announcement).unwrap(); + let msg = wire::serialize(&announcement); let signature = signer.sign(&msg); Self::Node { @@ -346,8 +376,12 @@ impl wire::Encode for Message { n += inv.as_slice().encode(writer)?; n += timestamp.encode(writer)?; } - Self::Node { .. } => { - todo!(); + Self::Node { + announcement, + signature, + } => { + n += announcement.encode(writer)?; + n += signature.encode(writer)?; } } Ok(n) @@ -375,7 +409,13 @@ impl wire::Decode for Message { }) } Ok(MessageType::Node) => { - todo!(); + let announcement = NodeAnnouncement::decode(reader)?; + let signature = crypto::Signature::decode(reader)?; + + Ok(Self::Node { + announcement, + signature, + }) } Ok(MessageType::GetInventory) => { let ids = Vec::::decode(reader)?; diff --git a/node/src/protocol/wire.rs b/node/src/protocol/wire.rs index 63e55281..139c0835 100644 --- a/node/src/protocol/wire.rs +++ b/node/src/protocol/wire.rs @@ -421,6 +421,14 @@ mod tests { 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); diff --git a/node/src/storage.rs b/node/src/storage.rs index 0193b707..9410155b 100644 --- a/node/src/storage.rs +++ b/node/src/storage.rs @@ -8,7 +8,6 @@ use std::ops::{Deref, DerefMut}; use std::path::Path; use radicle_git_ext as git_ext; -use serde::{Deserialize, Serialize}; use thiserror::Error; pub use radicle_git_ext::Oid; @@ -110,7 +109,7 @@ impl From> for HashMap { } /// A project remote. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Remote { /// ID of remote. pub id: PublicKey, @@ -119,7 +118,6 @@ pub struct Remote { /// Whether this remote is of a project delegate. pub delegate: bool, /// Whether the remote is verified or not, ie. whether its signed refs were checked. - #[serde(skip)] verified: PhantomData, } diff --git a/node/src/storage/refs.rs b/node/src/storage/refs.rs index 5fe228a5..54a39475 100644 --- a/node/src/storage/refs.rs +++ b/node/src/storage/refs.rs @@ -9,7 +9,6 @@ use std::str::FromStr; use once_cell::sync::Lazy; use radicle_git_ext as git_ext; -use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::crypto; @@ -52,7 +51,7 @@ pub enum Error { } /// The published state of a local repository. -#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct Refs(BTreeMap); impl Refs { @@ -162,11 +161,10 @@ impl DerefMut for Refs { /// /// The type parameter keeps track of whether the signature was [`Verified`] or /// [`Unverified`]. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SignedRefs { refs: Refs, signature: Signature, - #[serde(skip)] _verified: PhantomData, } diff --git a/node/src/test/arbitrary.rs b/node/src/test/arbitrary.rs index 77e87d2f..2f05d1a5 100644 --- a/node/src/test/arbitrary.rs +++ b/node/src/test/arbitrary.rs @@ -13,7 +13,7 @@ use crate::crypto::{PublicKey, SecretKey}; use crate::git; use crate::hash; use crate::identity::{Delegate, Did, Doc, Id, Project}; -use crate::protocol::message::{Address, Envelope, Message}; +use crate::protocol::message::{Address, Envelope, Message, MessageType, NodeAnnouncement}; use crate::protocol::{NodeId, Timestamp}; use crate::storage; use crate::storage::refs::{Refs, SignedRefs}; @@ -68,22 +68,45 @@ impl Arbitrary for Envelope { impl Arbitrary for Message { fn arbitrary(g: &mut quickcheck::Gen) -> Self { - let type_id = g.choose(&[4, 6, 8]).unwrap(); + let type_id = g + .choose(&[ + MessageType::GetInventory, + MessageType::Inventory, + MessageType::Node, + MessageType::RefsUpdate, + ]) + .unwrap(); match type_id { - 4 => Self::GetInventory { + MessageType::GetInventory => Self::GetInventory { ids: Vec::::arbitrary(g), }, - 6 => Self::Inventory { + MessageType::Inventory => Self::Inventory { node: NodeId::arbitrary(g), inv: Vec::::arbitrary(g), timestamp: Timestamp::arbitrary(g), }, - 8 => Self::RefsUpdate { + MessageType::RefsUpdate => Self::RefsUpdate { id: Id::arbitrary(g), signer: PublicKey::arbitrary(g), refs: SignedRefs::::arbitrary(g), }, + MessageType::Node => { + let announcement = NodeAnnouncement { + id: NodeId::arbitrary(g), + features: ByteArray::<32>::arbitrary(g).into_inner(), + timestamp: Timestamp::arbitrary(g), + alias: ByteArray::<32>::arbitrary(g).into_inner(), + addresses: Arbitrary::arbitrary(g), + }; + let bytes: ByteArray<64> = Arbitrary::arbitrary(g); + let signature = crypto::Signature::from(bytes.into_inner()); + + Self::Node { + signature, + announcement, + } + } _ => unreachable!(), } }