Implement proper `node::Features` type

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-10-04 11:55:41 +02:00
parent 77ee404e83
commit e909e04c6c
No known key found for this signature in database
6 changed files with 156 additions and 8 deletions

View File

@ -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

View File

@ -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)?;

View File

@ -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),

View File

@ -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>,

View File

@ -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";

View File

@ -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
);
}
}