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;
|
||||||
use crate::git::Url;
|
use crate::git::Url;
|
||||||
use crate::identity::{Doc, Id};
|
use crate::identity::{Doc, Id};
|
||||||
|
use crate::node;
|
||||||
use crate::service::config::ProjectTracking;
|
use crate::service::config::ProjectTracking;
|
||||||
use crate::service::message::{Address, Announcement, AnnouncementMessage};
|
use crate::service::message::{Address, Announcement, AnnouncementMessage};
|
||||||
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
|
use crate::service::message::{NodeAnnouncement, RefsAnnouncement};
|
||||||
|
|
@ -40,7 +41,7 @@ pub use crate::service::message::{Envelope, Message};
|
||||||
pub use crate::service::peer::Session;
|
pub use crate::service::peer::Session;
|
||||||
|
|
||||||
use self::gossip::Gossip;
|
use self::gossip::Gossip;
|
||||||
use self::message::{InventoryAnnouncement, NodeFeatures};
|
use self::message::InventoryAnnouncement;
|
||||||
use self::reactor::Reactor;
|
use self::reactor::Reactor;
|
||||||
|
|
||||||
pub const DEFAULT_PORT: u16 = 8776;
|
pub const DEFAULT_PORT: u16 = 8776;
|
||||||
|
|
@ -985,7 +986,7 @@ mod gossip {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(timestamp: Timestamp, config: &Config) -> NodeAnnouncement {
|
pub fn node(timestamp: Timestamp, config: &Config) -> NodeAnnouncement {
|
||||||
let features = NodeFeatures::default();
|
let features = node::Features::SEED;
|
||||||
let alias = config.alias();
|
let alias = config.alias();
|
||||||
let addresses = vec![]; // TODO
|
let addresses = vec![]; // TODO
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use thiserror::Error;
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
|
use crate::node;
|
||||||
use crate::service::filter::Filter;
|
use crate::service::filter::Filter;
|
||||||
use crate::service::{NodeId, Timestamp, PROTOCOL_VERSION};
|
use crate::service::{NodeId, Timestamp, PROTOCOL_VERSION};
|
||||||
use crate::storage::refs::Refs;
|
use crate::storage::refs::Refs;
|
||||||
|
|
@ -20,9 +21,6 @@ pub struct Envelope {
|
||||||
pub msg: Message,
|
pub msg: Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advertized node feature. Signals what services the node supports.
|
|
||||||
pub type NodeFeatures = [u8; 32];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
// TODO: We should check the length and charset when deserializing.
|
// TODO: We should check the length and charset when deserializing.
|
||||||
pub struct Hostname(String);
|
pub struct Hostname(String);
|
||||||
|
|
@ -128,7 +126,7 @@ pub struct Subscribe {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NodeAnnouncement {
|
pub struct NodeAnnouncement {
|
||||||
/// Advertized features.
|
/// Advertized features.
|
||||||
pub features: NodeFeatures,
|
pub features: node::Features,
|
||||||
/// Monotonic timestamp.
|
/// Monotonic timestamp.
|
||||||
pub timestamp: Timestamp,
|
pub timestamp: Timestamp,
|
||||||
/// Non-unique alias. Must be valid UTF-8.
|
/// Non-unique alias. Must be valid UTF-8.
|
||||||
|
|
@ -152,7 +150,7 @@ impl wire::Encode for NodeAnnouncement {
|
||||||
|
|
||||||
impl wire::Decode for NodeAnnouncement {
|
impl wire::Decode for NodeAnnouncement {
|
||||||
fn decode<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
|
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 timestamp = Timestamp::decode(reader)?;
|
||||||
let alias = wire::Decode::decode(reader)?;
|
let alias = wire::Decode::decode(reader)?;
|
||||||
let addresses = Vec::<Address>::decode(reader)?;
|
let addresses = Vec::<Address>::decode(reader)?;
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ impl Arbitrary for Message {
|
||||||
.into(),
|
.into(),
|
||||||
MessageType::NodeAnnouncement => {
|
MessageType::NodeAnnouncement => {
|
||||||
let message = NodeAnnouncement {
|
let message = NodeAnnouncement {
|
||||||
features: ByteArray::<32>::arbitrary(g).into_inner(),
|
features: u64::arbitrary(g).into(),
|
||||||
timestamp: Timestamp::arbitrary(g),
|
timestamp: Timestamp::arbitrary(g),
|
||||||
alias: ByteArray::<32>::arbitrary(g).into_inner(),
|
alias: ByteArray::<32>::arbitrary(g).into_inner(),
|
||||||
addresses: Arbitrary::arbitrary(g),
|
addresses: Arbitrary::arbitrary(g),
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use crate::git;
|
||||||
use crate::git::fmt;
|
use crate::git::fmt;
|
||||||
use crate::hash::Digest;
|
use crate::hash::Digest;
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
|
use crate::node;
|
||||||
use crate::service;
|
use crate::service;
|
||||||
use crate::service::reactor::Io;
|
use crate::service::reactor::Io;
|
||||||
use crate::service::{filter, routing};
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Wire<R, S, T, G> {
|
pub struct Wire<R, S, T, G> {
|
||||||
inboxes: HashMap<IpAddr, Decoder>,
|
inboxes: HashMap<IpAddr, Decoder>,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
mod features;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
|
|
@ -6,6 +8,8 @@ use std::path::Path;
|
||||||
|
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
|
|
||||||
|
pub use features::Features;
|
||||||
|
|
||||||
/// Default name for control socket file.
|
/// Default name for control socket file.
|
||||||
pub const DEFAULT_SOCKET_NAME: &str = "radicle.sock";
|
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