node: More work on encoding
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
1dec499e3d
commit
cb4939fd75
|
|
@ -9,10 +9,10 @@ pub use ed25519::Error;
|
||||||
pub use ed25519::Signature;
|
pub use ed25519::Signature;
|
||||||
|
|
||||||
/// Verified (used as type witness).
|
/// Verified (used as type witness).
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Verified;
|
pub struct Verified;
|
||||||
/// Unverified (used as type witness).
|
/// Unverified (used as type witness).
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Unverified;
|
pub struct Unverified;
|
||||||
|
|
||||||
pub trait Signer: Send + Sync {
|
pub trait Signer: Send + Sync {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::net;
|
use std::{io, net};
|
||||||
|
|
||||||
use byteorder::{NetworkEndian, ReadBytesExt};
|
use byteorder::{NetworkEndian, ReadBytesExt};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use crate::git;
|
use crate::git;
|
||||||
|
|
@ -12,7 +11,7 @@ use crate::storage;
|
||||||
use crate::storage::refs::SignedRefs;
|
use crate::storage::refs::SignedRefs;
|
||||||
|
|
||||||
/// Message envelope. All messages sent over the network are wrapped in this type.
|
/// 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 {
|
pub struct Envelope {
|
||||||
/// Network magic constant. Used to differentiate networks.
|
/// Network magic constant. Used to differentiate networks.
|
||||||
pub magic: u32,
|
pub magic: u32,
|
||||||
|
|
@ -23,7 +22,7 @@ pub struct Envelope {
|
||||||
/// Advertized node feature. Signals what services the node supports.
|
/// Advertized node feature. Signals what services the node supports.
|
||||||
pub type NodeFeatures = [u8; 32];
|
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.
|
// TODO: We should check the length and charset when deserializing.
|
||||||
pub struct Hostname(String);
|
pub struct Hostname(String);
|
||||||
|
|
||||||
|
|
@ -90,7 +89,7 @@ impl TryFrom<u8> for AddressType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peer public protocol address.
|
/// Peer public protocol address.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Address {
|
pub enum Address {
|
||||||
Ipv4 {
|
Ipv4 {
|
||||||
ip: net::Ipv4Addr,
|
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 {
|
pub struct NodeAnnouncement {
|
||||||
/// Node identifier.
|
/// Node identifier.
|
||||||
id: NodeId,
|
pub id: NodeId,
|
||||||
/// Advertized features.
|
/// Advertized features.
|
||||||
features: NodeFeatures,
|
pub features: NodeFeatures,
|
||||||
/// Monotonic timestamp.
|
/// Monotonic timestamp.
|
||||||
timestamp: Timestamp,
|
pub timestamp: Timestamp,
|
||||||
/// Non-unique alias. Must be valid UTF-8.
|
/// Non-unique alias. Must be valid UTF-8.
|
||||||
alias: [u8; 32],
|
pub alias: [u8; 32],
|
||||||
/// Announced addresses.
|
/// Announced addresses.
|
||||||
addresses: Vec<Address>,
|
pub addresses: Vec<Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeAnnouncement {
|
impl NodeAnnouncement {
|
||||||
/// Verify a signature on this message.
|
/// Verify a signature on this message.
|
||||||
pub fn verify(&self, signature: &crypto::Signature) -> bool {
|
pub fn verify(&self, signature: &crypto::Signature) -> bool {
|
||||||
// TODO: Use binary serialization.
|
let msg = wire::serialize(self);
|
||||||
let msg = serde_json::to_vec(self).unwrap();
|
|
||||||
self.id.verify(signature, &msg).is_ok()
|
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.
|
/// Message payload.
|
||||||
/// These are the messages peers send to each other.
|
/// These are the messages peers send to each other.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
/// Say hello to a peer. This is the first message sent to a peer after connection.
|
/// Say hello to a peer. This is the first message sent to a peer after connection.
|
||||||
Hello {
|
Hello {
|
||||||
|
|
@ -233,10 +263,10 @@ pub enum Message {
|
||||||
git: git::Url,
|
git: git::Url,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
/// Signature over the announcement, by the node being announced.
|
|
||||||
signature: crypto::Signature,
|
|
||||||
/// Unsigned node announcement.
|
/// Unsigned node announcement.
|
||||||
announcement: NodeAnnouncement,
|
announcement: NodeAnnouncement,
|
||||||
|
/// Signature over the announcement, by the node being announced.
|
||||||
|
signature: crypto::Signature,
|
||||||
},
|
},
|
||||||
/// Get a peer's inventory.
|
/// Get a peer's inventory.
|
||||||
GetInventory { ids: Vec<Id> },
|
GetInventory { ids: Vec<Id> },
|
||||||
|
|
@ -271,7 +301,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node<S: crypto::Signer>(announcement: NodeAnnouncement, signer: S) -> Self {
|
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);
|
let signature = signer.sign(&msg);
|
||||||
|
|
||||||
Self::Node {
|
Self::Node {
|
||||||
|
|
@ -346,8 +376,12 @@ impl wire::Encode for Message {
|
||||||
n += inv.as_slice().encode(writer)?;
|
n += inv.as_slice().encode(writer)?;
|
||||||
n += timestamp.encode(writer)?;
|
n += timestamp.encode(writer)?;
|
||||||
}
|
}
|
||||||
Self::Node { .. } => {
|
Self::Node {
|
||||||
todo!();
|
announcement,
|
||||||
|
signature,
|
||||||
|
} => {
|
||||||
|
n += announcement.encode(writer)?;
|
||||||
|
n += signature.encode(writer)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(n)
|
Ok(n)
|
||||||
|
|
@ -375,7 +409,13 @@ impl wire::Decode for Message {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Ok(MessageType::Node) => {
|
Ok(MessageType::Node) => {
|
||||||
todo!();
|
let announcement = NodeAnnouncement::decode(reader)?;
|
||||||
|
let signature = crypto::Signature::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::Node {
|
||||||
|
announcement,
|
||||||
|
signature,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Ok(MessageType::GetInventory) => {
|
Ok(MessageType::GetInventory) => {
|
||||||
let ids = Vec::<Id>::decode(reader)?;
|
let ids = Vec::<Id>::decode(reader)?;
|
||||||
|
|
|
||||||
|
|
@ -421,6 +421,14 @@ mod tests {
|
||||||
quickcheck::TestResult::passed()
|
quickcheck::TestResult::passed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[quickcheck]
|
||||||
|
fn prop_vec(input: Vec<String>) {
|
||||||
|
assert_eq!(
|
||||||
|
deserialize::<Vec<String>>(&serialize(&input.as_slice())).unwrap(),
|
||||||
|
input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn prop_pubkey(input: PublicKey) {
|
fn prop_pubkey(input: PublicKey) {
|
||||||
assert_eq!(deserialize::<PublicKey>(&serialize(&input)).unwrap(), input);
|
assert_eq!(deserialize::<PublicKey>(&serialize(&input)).unwrap(), input);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use std::ops::{Deref, DerefMut};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use radicle_git_ext as git_ext;
|
use radicle_git_ext as git_ext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use radicle_git_ext::Oid;
|
pub use radicle_git_ext::Oid;
|
||||||
|
|
@ -110,7 +109,7 @@ impl<V> From<Remotes<V>> for HashMap<RemoteId, Refs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A project remote.
|
/// A project remote.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Remote<V> {
|
pub struct Remote<V> {
|
||||||
/// ID of remote.
|
/// ID of remote.
|
||||||
pub id: PublicKey,
|
pub id: PublicKey,
|
||||||
|
|
@ -119,7 +118,6 @@ pub struct Remote<V> {
|
||||||
/// Whether this remote is of a project delegate.
|
/// Whether this remote is of a project delegate.
|
||||||
pub delegate: bool,
|
pub delegate: bool,
|
||||||
/// Whether the remote is verified or not, ie. whether its signed refs were checked.
|
/// Whether the remote is verified or not, ie. whether its signed refs were checked.
|
||||||
#[serde(skip)]
|
|
||||||
verified: PhantomData<V>,
|
verified: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use radicle_git_ext as git_ext;
|
use radicle_git_ext as git_ext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
|
|
@ -52,7 +51,7 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The published state of a local repository.
|
/// 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>);
|
pub struct Refs(BTreeMap<git::RefString, Oid>);
|
||||||
|
|
||||||
impl Refs {
|
impl Refs {
|
||||||
|
|
@ -162,11 +161,10 @@ impl DerefMut for Refs {
|
||||||
///
|
///
|
||||||
/// The type parameter keeps track of whether the signature was [`Verified`] or
|
/// The type parameter keeps track of whether the signature was [`Verified`] or
|
||||||
/// [`Unverified`].
|
/// [`Unverified`].
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SignedRefs<V> {
|
pub struct SignedRefs<V> {
|
||||||
refs: Refs,
|
refs: Refs,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
#[serde(skip)]
|
|
||||||
_verified: PhantomData<V>,
|
_verified: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::crypto::{PublicKey, SecretKey};
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::hash;
|
use crate::hash;
|
||||||
use crate::identity::{Delegate, Did, Doc, Id, Project};
|
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::protocol::{NodeId, Timestamp};
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::refs::{Refs, SignedRefs};
|
use crate::storage::refs::{Refs, SignedRefs};
|
||||||
|
|
@ -68,22 +68,45 @@ impl Arbitrary for Envelope {
|
||||||
|
|
||||||
impl Arbitrary for Message {
|
impl Arbitrary for Message {
|
||||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
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 {
|
match type_id {
|
||||||
4 => Self::GetInventory {
|
MessageType::GetInventory => Self::GetInventory {
|
||||||
ids: Vec::<Id>::arbitrary(g),
|
ids: Vec::<Id>::arbitrary(g),
|
||||||
},
|
},
|
||||||
6 => Self::Inventory {
|
MessageType::Inventory => Self::Inventory {
|
||||||
node: NodeId::arbitrary(g),
|
node: NodeId::arbitrary(g),
|
||||||
inv: Vec::<Id>::arbitrary(g),
|
inv: Vec::<Id>::arbitrary(g),
|
||||||
timestamp: Timestamp::arbitrary(g),
|
timestamp: Timestamp::arbitrary(g),
|
||||||
},
|
},
|
||||||
8 => Self::RefsUpdate {
|
MessageType::RefsUpdate => Self::RefsUpdate {
|
||||||
id: Id::arbitrary(g),
|
id: Id::arbitrary(g),
|
||||||
signer: PublicKey::arbitrary(g),
|
signer: PublicKey::arbitrary(g),
|
||||||
refs: SignedRefs::<Unverified>::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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue