Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: More work on encoding
Alexis Sellier committed 3 years ago
commit cb4939fd752544fb962dcc7dab5843ee97d1f51b
parent 1dec499e3d33fb4d80d4cc6d359c458f0c42f1d1
6 files changed +101 -34
modified 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 {
modified 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<u8> 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<Address>,
+
    pub addresses: Vec<Address>,
}

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<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
+
        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<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
+
        let id = NodeId::decode(reader)?;
+
        let features = NodeFeatures::decode(reader)?;
+
        let timestamp = Timestamp::decode(reader)?;
+
        let alias = wire::Decode::decode(reader)?;
+
        let addresses = Vec::<Address>::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<Id> },
@@ -271,7 +301,7 @@ impl Message {
    }

    pub fn node<S: crypto::Signer>(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::<Id>::decode(reader)?;
modified node/src/protocol/wire.rs
@@ -422,6 +422,14 @@ mod tests {
    }

    #[quickcheck]
+
    fn prop_vec(input: Vec<String>) {
+
        assert_eq!(
+
            deserialize::<Vec<String>>(&serialize(&input.as_slice())).unwrap(),
+
            input
+
        );
+
    }
+

+
    #[quickcheck]
    fn prop_pubkey(input: PublicKey) {
        assert_eq!(deserialize::<PublicKey>(&serialize(&input)).unwrap(), input);
    }
modified 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<V> From<Remotes<V>> for HashMap<RemoteId, Refs> {
}

/// A project remote.
-
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Remote<V> {
    /// ID of remote.
    pub id: PublicKey,
@@ -119,7 +118,6 @@ pub struct Remote<V> {
    /// 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<V>,
}

modified 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<git::RefString, Oid>);

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<V> {
    refs: Refs,
    signature: Signature,
-
    #[serde(skip)]
    _verified: PhantomData<V>,
}

modified 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::<Id>::arbitrary(g),
            },
-
            6 => Self::Inventory {
+
            MessageType::Inventory => Self::Inventory {
                node: NodeId::arbitrary(g),
                inv: Vec::<Id>::arbitrary(g),
                timestamp: Timestamp::arbitrary(g),
            },
-
            8 => Self::RefsUpdate {
+
            MessageType::RefsUpdate => Self::RefsUpdate {
                id: Id::arbitrary(g),
                signer: PublicKey::arbitrary(g),
                refs: SignedRefs::<Unverified>::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!(),
        }
    }