Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement proper `node::Features` type
Alexis Sellier committed 3 years ago
commit e909e04c6c22d37da5c39f86f873d607d372c024
parent 77ee404e838849b7633db6e6e8f29879d1594cc1
6 files changed +156 -8
modified radicle-node/src/service.rs
@@ -28,6 +28,7 @@ use crate::crypto::{Signer, Verified};
use crate::git;
use crate::git::Url;
use crate::identity::{Doc, Id};
+
use crate::node;
use crate::service::config::ProjectTracking;
use crate::service::message::{Address, Announcement, AnnouncementMessage};
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
@@ -40,7 +41,7 @@ pub use crate::service::message::{Envelope, Message};
pub use crate::service::peer::Session;

use self::gossip::Gossip;
-
use self::message::{InventoryAnnouncement, NodeFeatures};
+
use self::message::InventoryAnnouncement;
use self::reactor::Reactor;

pub const DEFAULT_PORT: u16 = 8776;
@@ -985,7 +986,7 @@ mod gossip {
    }

    pub fn node(timestamp: Timestamp, config: &Config) -> NodeAnnouncement {
-
        let features = NodeFeatures::default();
+
        let features = node::Features::SEED;
        let alias = config.alias();
        let addresses = vec![]; // TODO

modified radicle-node/src/service/message.rs
@@ -6,6 +6,7 @@ use thiserror::Error;
use crate::crypto;
use crate::git;
use crate::identity::Id;
+
use crate::node;
use crate::service::filter::Filter;
use crate::service::{NodeId, Timestamp, PROTOCOL_VERSION};
use crate::storage::refs::Refs;
@@ -20,9 +21,6 @@ pub struct Envelope {
    pub msg: Message,
}

-
/// Advertized node feature. Signals what services the node supports.
-
pub type NodeFeatures = [u8; 32];
-

#[derive(Debug, Clone, PartialEq, Eq)]
// TODO: We should check the length and charset when deserializing.
pub struct Hostname(String);
@@ -128,7 +126,7 @@ pub struct Subscribe {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NodeAnnouncement {
    /// Advertized features.
-
    pub features: NodeFeatures,
+
    pub features: node::Features,
    /// Monotonic timestamp.
    pub timestamp: Timestamp,
    /// Non-unique alias. Must be valid UTF-8.
@@ -152,7 +150,7 @@ impl wire::Encode for NodeAnnouncement {

impl wire::Decode for NodeAnnouncement {
    fn decode<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
-
        let features = NodeFeatures::decode(reader)?;
+
        let features = node::Features::decode(reader)?;
        let timestamp = Timestamp::decode(reader)?;
        let alias = wire::Decode::decode(reader)?;
        let addresses = Vec::<Address>::decode(reader)?;
modified radicle-node/src/test/arbitrary.rs
@@ -72,7 +72,7 @@ impl Arbitrary for Message {
            .into(),
            MessageType::NodeAnnouncement => {
                let message = NodeAnnouncement {
-
                    features: ByteArray::<32>::arbitrary(g).into_inner(),
+
                    features: u64::arbitrary(g).into(),
                    timestamp: Timestamp::arbitrary(g),
                    alias: ByteArray::<32>::arbitrary(g).into_inner(),
                    addresses: Arbitrary::arbitrary(g),
modified radicle-node/src/wire.rs
@@ -18,6 +18,7 @@ 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};
@@ -438,6 +439,20 @@ impl Decode for SignedRefs<Unverified> {
    }
}

+
impl Encode for node::Features {
+
    fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
+
        self.deref().encode(writer)
+
    }
+
}
+

+
impl Decode for node::Features {
+
    fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
+
        let features = u64::decode(reader)?;
+

+
        Ok(Self::from(features))
+
    }
+
}
+

#[derive(Debug)]
pub struct Wire<R, S, T, G> {
    inboxes: HashMap<IpAddr, Decoder>,
modified radicle/src/node.rs
@@ -1,3 +1,5 @@
+
mod features;
+

use std::fmt;
use std::io;
use std::io::{BufRead, BufReader, Write};
@@ -6,6 +8,8 @@ use std::path::Path;

use crate::identity::Id;

+
pub use features::Features;
+

/// Default name for control socket file.
pub const DEFAULT_SOCKET_NAME: &str = "radicle.sock";

added radicle/src/node/features.rs
@@ -0,0 +1,130 @@
+
//! Node features advertized on the network.
+
use std::{fmt, ops};
+

+
/// Advertized node features. Signals what services the node supports.
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+
pub struct Features(u64);
+

+
impl Features {
+
    /// `NONE` means no features are supported.
+
    pub const NONE: Features = Features(0b00000000);
+

+
    /// `SEED` is the base feature set all seed nodes must support.
+
    pub const SEED: Features = Features(0b00000001);
+

+
    /// Returns [`Features`] with the other features added.
+
    #[must_use]
+
    pub fn with(self, other: Features) -> Features {
+
        Self(self.0 | other.0)
+
    }
+

+
    /// Returns [`Features`] without the other features.
+
    #[must_use]
+
    pub fn without(self, other: Features) -> Features {
+
        Self(self.0 ^ other.0)
+
    }
+

+
    /// Check whether [`Features`] are included.
+
    pub fn has(self, flags: Features) -> bool {
+
        (self.0 | flags.0) == self.0
+
    }
+
}
+

+
impl Default for Features {
+
    fn default() -> Self {
+
        Self::NONE
+
    }
+
}
+

+
impl ops::Deref for Features {
+
    type Target = u64;
+

+
    fn deref(&self) -> &Self::Target {
+
        &self.0
+
    }
+
}
+

+
impl fmt::LowerHex for Features {
+
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+
        fmt::LowerHex::fmt(&self.0, f)
+
    }
+
}
+

+
impl fmt::UpperHex for Features {
+
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+
        fmt::UpperHex::fmt(&self.0, f)
+
    }
+
}
+

+
impl fmt::Display for Features {
+
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+
        if *self == Features::NONE {
+
            write!(f, "Features(NONE)")
+
        } else {
+
            write!(f, "Features(0x{:x})", self.0)
+
        }
+
    }
+
}
+

+
impl From<u64> for Features {
+
    fn from(f: u64) -> Self {
+
        Features(f)
+
    }
+
}
+

+
impl From<Features> for u64 {
+
    fn from(flags: Features) -> Self {
+
        flags.0
+
    }
+
}
+

+
impl ops::BitOr for Features {
+
    type Output = Self;
+

+
    fn bitor(self, rhs: Self) -> Self {
+
        self.with(rhs)
+
    }
+
}
+

+
impl ops::BitOrAssign for Features {
+
    fn bitor_assign(&mut self, rhs: Self) {
+
        *self = self.with(rhs);
+
    }
+
}
+

+
impl ops::BitXor for Features {
+
    type Output = Self;
+

+
    fn bitxor(self, rhs: Self) -> Self {
+
        self.without(rhs)
+
    }
+
}
+

+
impl ops::BitXorAssign for Features {
+
    fn bitxor_assign(&mut self, rhs: Self) {
+
        *self = self.without(rhs);
+
    }
+
}
+

+
#[cfg(test)]
+
mod test {
+
    use super::*;
+

+
    #[test]
+
    fn test_operations() {
+
        assert_eq!(Features::NONE.with(Features::SEED), Features::SEED);
+

+
        assert!(!Features::from(u64::MAX)
+
            .without(Features::SEED)
+
            .has(Features::SEED));
+

+
        assert!(Features::from(u64::MIN)
+
            .with(Features::SEED)
+
            .has(Features::SEED));
+

+
        assert_eq!(
+
            Features::NONE.with(Features::SEED).without(Features::SEED),
+
            Features::NONE
+
        );
+
    }
+
}