Implement proper `node::Features` type
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
77ee404e83
commit
e909e04c6c
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue