node: Implement binary encoding for messages
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
c7f78eafb6
commit
b3d3077ccc
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||||
anyhow = { version = "1" }
|
anyhow = { version = "1" }
|
||||||
bs58 = { version = "0.4.0" }
|
bs58 = { version = "0.4.0" }
|
||||||
ed25519-consensus = { version = "2.0.1" }
|
ed25519-consensus = { version = "2.0.1" }
|
||||||
|
byteorder = { version = "1" }
|
||||||
chrono = { version = "0.4.0" }
|
chrono = { version = "0.4.0" }
|
||||||
colored = { version = "1.9.0" }
|
colored = { version = "1.9.0" }
|
||||||
crossbeam-channel = { version = "0.5.6" }
|
crossbeam-channel = { version = "0.5.6" }
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,13 @@ impl std::hash::Hash for PublicKey {
|
||||||
|
|
||||||
impl fmt::Display for PublicKey {
|
impl fmt::Display for PublicKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.encode())
|
write!(f, "{}", self.to_human())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PublicKey> for String {
|
impl From<PublicKey> for String {
|
||||||
fn from(other: PublicKey) -> Self {
|
fn from(other: PublicKey) -> Self {
|
||||||
other.encode()
|
other.to_human()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,8 +96,16 @@ impl From<ed25519::VerificationKey> for PublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<[u8; 32]> for PublicKey {
|
||||||
|
type Error = ed25519::Error;
|
||||||
|
|
||||||
|
fn try_from(other: [u8; 32]) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self(ed25519::VerificationKey::try_from(other)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
pub fn encode(&self) -> String {
|
pub fn to_human(&self) -> String {
|
||||||
multibase::encode(multibase::Base::Base58Btc, &self.0)
|
multibase::encode(multibase::Base::Base58Btc, &self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,3 +139,18 @@ impl Deref for PublicKey {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::crypto::PublicKey;
|
||||||
|
use quickcheck_macros::quickcheck;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[quickcheck]
|
||||||
|
fn prop_encode_decode(input: PublicKey) {
|
||||||
|
let encoded = input.to_string();
|
||||||
|
let decoded = PublicKey::from_str(&encoded).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(input, decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use crate::storage::refs::Refs;
|
||||||
use crate::storage::RemoteId;
|
use crate::storage::RemoteId;
|
||||||
|
|
||||||
pub use ext::Oid;
|
pub use ext::Oid;
|
||||||
|
pub use git_ref_format as fmt;
|
||||||
pub use git_ref_format::{refname, RefStr, RefString};
|
pub use git_ref_format::{refname, RefStr, RefString};
|
||||||
pub use git_url as url;
|
pub use git_url as url;
|
||||||
pub use git_url::Url;
|
pub use git_url::Url;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use std::fmt;
|
use std::{convert::TryInto, fmt};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{
|
use sha2::{
|
||||||
|
|
@ -10,8 +9,6 @@ use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
pub enum DecodeError {
|
pub enum DecodeError {
|
||||||
#[error(transparent)]
|
|
||||||
Multibase(#[from] multibase::Error),
|
|
||||||
#[error("invalid digest length {0}")]
|
#[error("invalid digest length {0}")]
|
||||||
InvalidLength(usize),
|
InvalidLength(usize),
|
||||||
}
|
}
|
||||||
|
|
@ -24,38 +21,17 @@ impl Digest {
|
||||||
pub fn new(bytes: impl AsRef<[u8]>) -> Self {
|
pub fn new(bytes: impl AsRef<[u8]>) -> Self {
|
||||||
Self::from(Sha256::digest(bytes))
|
Self::from(Sha256::digest(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(&self) -> String {
|
|
||||||
multibase::encode(multibase::Base::Base58Btc, &self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(s: &str) -> Result<Self, DecodeError> {
|
|
||||||
let (_, bytes) = multibase::decode(s)?;
|
|
||||||
let array = bytes
|
|
||||||
.try_into()
|
|
||||||
.map_err(|v: Vec<u8>| DecodeError::InvalidLength(v.len()))?;
|
|
||||||
|
|
||||||
Ok(Self(array))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Digest {
|
impl AsRef<[u8; 32]> for Digest {
|
||||||
type Err = DecodeError;
|
fn as_ref(&self) -> &[u8; 32] {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Self::decode(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for Digest {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Digest {
|
impl fmt::Debug for Digest {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Hash({})", self.encode())
|
write!(f, "Hash({})", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,22 +50,20 @@ impl From<[u8; 32]> for Digest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Digest {
|
||||||
|
type Error = DecodeError;
|
||||||
|
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, DecodeError> {
|
||||||
|
let bytes: [u8; 32] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| DecodeError::InvalidLength(bytes.len()))?;
|
||||||
|
|
||||||
|
Ok(bytes.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>> for Digest {
|
impl From<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>> for Digest {
|
||||||
fn from(array: GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>) -> Self {
|
fn from(array: GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>) -> Self {
|
||||||
Self(array.into())
|
Self(array.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use quickcheck_macros::quickcheck;
|
|
||||||
|
|
||||||
#[quickcheck]
|
|
||||||
fn prop_encode_decode(input: Digest) {
|
|
||||||
let encoded = input.encode();
|
|
||||||
let decoded = Digest::decode(&encoded).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(input, decoded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{ffi::OsString, fmt, io, str::FromStr};
|
use std::{ffi::OsString, fmt, io, str::FromStr};
|
||||||
|
|
||||||
|
|
@ -20,6 +21,8 @@ pub static IDENTITY_PATH: Lazy<&Path> = Lazy::new(|| Path::new("radicle.json"));
|
||||||
pub enum IdError {
|
pub enum IdError {
|
||||||
#[error("invalid digest: {0}")]
|
#[error("invalid digest: {0}")]
|
||||||
InvalidDigest(#[from] hash::DecodeError),
|
InvalidDigest(#[from] hash::DecodeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Multibase(#[from] multibase::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
|
@ -27,27 +30,34 @@ pub struct Id(hash::Digest);
|
||||||
|
|
||||||
impl fmt::Display for Id {
|
impl fmt::Display for Id {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.encode())
|
f.write_str(&self.to_human())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Id {
|
impl fmt::Debug for Id {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "ProjId({})", self.encode())
|
write!(f, "Id({})", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id {
|
impl Id {
|
||||||
pub fn encode(&self) -> String {
|
pub fn to_human(&self) -> String {
|
||||||
multibase::encode(multibase::Base::Base58Btc, &self.0.as_ref())
|
multibase::encode(multibase::Base::Base58Btc, &self.0.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_human(s: &str) -> Result<Self, IdError> {
|
||||||
|
let (_, bytes) = multibase::decode(s)?;
|
||||||
|
let array: hash::Digest = bytes.as_slice().try_into()?;
|
||||||
|
|
||||||
|
Ok(Self(array))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Id {
|
impl FromStr for Id {
|
||||||
type Err = IdError;
|
type Err = IdError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self(hash::Digest::from_str(s)?))
|
Self::from_human(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +76,14 @@ impl From<hash::Digest> for Id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Id {
|
||||||
|
type Target = hash::Digest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Id {
|
impl serde::Serialize for Id {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|
@ -98,7 +116,7 @@ pub struct Did(crypto::PublicKey);
|
||||||
|
|
||||||
impl Did {
|
impl Did {
|
||||||
pub fn encode(&self) -> String {
|
pub fn encode(&self) -> String {
|
||||||
format!("did:key:{}", self.0.encode())
|
format!("did:key:{}", self.0.to_human())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(input: &str) -> Result<Self, DidError> {
|
pub fn decode(input: &str) -> Result<Self, DidError> {
|
||||||
|
|
@ -219,9 +237,9 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn prop_encode_decode(input: PublicKey) {
|
fn prop_from_str(input: Id) {
|
||||||
let encoded = input.to_string();
|
let encoded = input.to_string();
|
||||||
let decoded = PublicKey::from_str(&encoded).unwrap();
|
let decoded = Id::from_str(&encoded).unwrap();
|
||||||
|
|
||||||
assert_eq!(input, decoded);
|
assert_eq!(input, decoded);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod peer;
|
pub mod peer;
|
||||||
|
pub mod wire;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::{collections::VecDeque, fmt, io, net, net::IpAddr};
|
use std::{collections::VecDeque, fmt, io, net, net::IpAddr};
|
||||||
|
|
@ -667,6 +668,16 @@ pub struct Context<S, T, G> {
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T, G> Context<S, T, G>
|
||||||
|
where
|
||||||
|
T: storage::ReadStorage,
|
||||||
|
G: crypto::Signer,
|
||||||
|
{
|
||||||
|
pub(crate) fn id(&self) -> NodeId {
|
||||||
|
*self.signer.public_key()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'r, S, T, G> Context<S, T, G>
|
impl<'r, S, T, G> Context<S, T, G>
|
||||||
where
|
where
|
||||||
T: storage::WriteStorage<'r>,
|
T: storage::WriteStorage<'r>,
|
||||||
|
|
@ -692,10 +703,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn id(&self) -> NodeId {
|
|
||||||
*self.signer.public_key()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process a peer inventory announcement by updating our routing table.
|
/// Process a peer inventory announcement by updating our routing table.
|
||||||
fn process_inventory(&mut self, inventory: &Inventory, from: NodeId, remote: &Url) {
|
fn process_inventory(&mut self, inventory: &Inventory, from: NodeId, remote: &Url) {
|
||||||
for proj_id in inventory {
|
for proj_id in inventory {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
use git_url::Url;
|
use byteorder::NetworkEndian;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
|
use crate::git;
|
||||||
use crate::identity::Id;
|
use crate::identity::Id;
|
||||||
|
use crate::protocol::wire;
|
||||||
use crate::protocol::{Context, NodeId, Timestamp, PROTOCOL_VERSION};
|
use crate::protocol::{Context, NodeId, Timestamp, PROTOCOL_VERSION};
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::refs::SignedRefs;
|
use crate::storage::refs::SignedRefs;
|
||||||
|
|
@ -28,13 +30,6 @@ pub struct Hostname(String);
|
||||||
/// Peer public protocol address.
|
/// Peer public protocol address.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum Address {
|
pub enum Address {
|
||||||
/// Tor V3 onion address.
|
|
||||||
Onion {
|
|
||||||
key: crypto::PublicKey,
|
|
||||||
port: u16,
|
|
||||||
checksum: u16,
|
|
||||||
version: u8,
|
|
||||||
},
|
|
||||||
Ip {
|
Ip {
|
||||||
ip: net::IpAddr,
|
ip: net::IpAddr,
|
||||||
port: u16,
|
port: u16,
|
||||||
|
|
@ -43,6 +38,57 @@ pub enum Address {
|
||||||
host: Hostname,
|
host: Hostname,
|
||||||
port: u16,
|
port: u16,
|
||||||
},
|
},
|
||||||
|
/// Tor V3 onion address.
|
||||||
|
Onion {
|
||||||
|
key: crypto::PublicKey,
|
||||||
|
port: u16,
|
||||||
|
checksum: u16,
|
||||||
|
version: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wire::Encode for Address {
|
||||||
|
fn encode<W: std::io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Ip { ip, port } => {
|
||||||
|
match ip {
|
||||||
|
net::IpAddr::V4(addr) => {
|
||||||
|
n += 1u8.encode(writer)?;
|
||||||
|
n += addr.octets().encode(writer)?;
|
||||||
|
}
|
||||||
|
net::IpAddr::V6(addr) => {
|
||||||
|
n += 2u8.encode(writer)?;
|
||||||
|
n += addr.octets().encode(writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n += port.encode(writer)?;
|
||||||
|
}
|
||||||
|
Self::Hostname { .. } => todo!(),
|
||||||
|
Self::Onion { .. } => todo!(),
|
||||||
|
}
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wire::Decode for Address {
|
||||||
|
fn decode<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
|
||||||
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
match reader.read_u8()? {
|
||||||
|
1 => {
|
||||||
|
let octets: [u8; 4] = wire::Decode::decode(reader)?;
|
||||||
|
let ip = net::IpAddr::from(net::Ipv4Addr::from(octets));
|
||||||
|
let port = u16::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::Ip { ip, port })
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -78,7 +124,7 @@ pub enum Message {
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
version: u32,
|
version: u32,
|
||||||
addrs: Vec<Address>,
|
addrs: Vec<Address>,
|
||||||
git: Url,
|
git: git::Url,
|
||||||
},
|
},
|
||||||
Node {
|
Node {
|
||||||
/// Signature over the announcement, by the node being announced.
|
/// Signature over the announcement, by the node being announced.
|
||||||
|
|
@ -91,11 +137,9 @@ pub enum Message {
|
||||||
/// Send our inventory to a peer. Sent in response to [`Message::GetInventory`].
|
/// Send our inventory to a peer. Sent in response to [`Message::GetInventory`].
|
||||||
/// Nb. This should be the whole inventory, not a partial update.
|
/// Nb. This should be the whole inventory, not a partial update.
|
||||||
Inventory {
|
Inventory {
|
||||||
|
node: NodeId,
|
||||||
inv: Vec<Id>,
|
inv: Vec<Id>,
|
||||||
timestamp: Timestamp,
|
timestamp: Timestamp,
|
||||||
/// Original peer this inventory came from. We don't set this when we
|
|
||||||
/// are the originator, only when relaying.
|
|
||||||
origin: Option<NodeId>,
|
|
||||||
},
|
},
|
||||||
/// Project refs were updated. Includes the signature of the user who updated
|
/// Project refs were updated. Includes the signature of the user who updated
|
||||||
/// their view of the project.
|
/// their view of the project.
|
||||||
|
|
@ -110,7 +154,7 @@ pub enum Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn hello(id: NodeId, timestamp: Timestamp, addrs: Vec<Address>, git: Url) -> Self {
|
pub fn hello(id: NodeId, timestamp: Timestamp, addrs: Vec<Address>, git: git::Url) -> Self {
|
||||||
Self::Hello {
|
Self::Hello {
|
||||||
id,
|
id,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
|
@ -133,18 +177,127 @@ impl Message {
|
||||||
pub fn inventory<S, T, G>(ctx: &Context<S, T, G>) -> Result<Self, storage::Error>
|
pub fn inventory<S, T, G>(ctx: &Context<S, T, G>) -> Result<Self, storage::Error>
|
||||||
where
|
where
|
||||||
T: storage::ReadStorage,
|
T: storage::ReadStorage,
|
||||||
|
G: crypto::Signer,
|
||||||
{
|
{
|
||||||
let timestamp = ctx.timestamp();
|
let timestamp = ctx.timestamp();
|
||||||
let inv = ctx.storage.inventory()?;
|
let inv = ctx.storage.inventory()?;
|
||||||
|
|
||||||
Ok(Self::Inventory {
|
Ok(Self::Inventory {
|
||||||
timestamp,
|
node: ctx.id(),
|
||||||
inv,
|
inv,
|
||||||
origin: None,
|
timestamp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inventory(ids: impl Into<Vec<Id>>) -> Self {
|
pub fn get_inventory(ids: impl Into<Vec<Id>>) -> Self {
|
||||||
Self::GetInventory { ids: ids.into() }
|
Self::GetInventory { ids: ids.into() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_id(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::Hello { .. } => 0,
|
||||||
|
Self::Node { .. } => 2,
|
||||||
|
Self::GetInventory { .. } => 4,
|
||||||
|
Self::Inventory { .. } => 6,
|
||||||
|
Self::RefsUpdate { .. } => 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wire::Encode for Message {
|
||||||
|
fn encode<W: std::io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
|
||||||
|
let mut n = self.type_id().encode(writer)?;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Hello {
|
||||||
|
id,
|
||||||
|
timestamp,
|
||||||
|
version,
|
||||||
|
addrs,
|
||||||
|
git,
|
||||||
|
} => {
|
||||||
|
n += id.encode(writer)?;
|
||||||
|
n += timestamp.encode(writer)?;
|
||||||
|
n += version.encode(writer)?;
|
||||||
|
n += addrs.as_slice().encode(writer)?;
|
||||||
|
n += git.encode(writer)?;
|
||||||
|
}
|
||||||
|
Self::RefsUpdate { id, signer, refs } => {
|
||||||
|
n += id.encode(writer)?;
|
||||||
|
n += signer.encode(writer)?;
|
||||||
|
n += refs.encode(writer)?;
|
||||||
|
}
|
||||||
|
Self::GetInventory { ids } => {
|
||||||
|
n += ids.as_slice().encode(writer)?;
|
||||||
|
}
|
||||||
|
Self::Inventory {
|
||||||
|
node,
|
||||||
|
inv,
|
||||||
|
timestamp,
|
||||||
|
} => {
|
||||||
|
n += node.encode(writer)?;
|
||||||
|
n += inv.as_slice().encode(writer)?;
|
||||||
|
n += timestamp.encode(writer)?;
|
||||||
|
}
|
||||||
|
Self::Node { .. } => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wire::Decode for Message {
|
||||||
|
fn decode<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
|
||||||
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
let type_id = reader.read_u16::<NetworkEndian>()?;
|
||||||
|
|
||||||
|
match type_id {
|
||||||
|
0 => {
|
||||||
|
let id = NodeId::decode(reader)?;
|
||||||
|
let timestamp = Timestamp::decode(reader)?;
|
||||||
|
let version = u32::decode(reader)?;
|
||||||
|
let addrs = Vec::<Address>::decode(reader)?;
|
||||||
|
let git = git::Url::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::Hello {
|
||||||
|
id,
|
||||||
|
timestamp,
|
||||||
|
version,
|
||||||
|
addrs,
|
||||||
|
git,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let ids = Vec::<Id>::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::GetInventory { ids })
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let node = NodeId::decode(reader)?;
|
||||||
|
let inv = Vec::<Id>::decode(reader)?;
|
||||||
|
let timestamp = Timestamp::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::Inventory {
|
||||||
|
node,
|
||||||
|
inv,
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
let id = Id::decode(reader)?;
|
||||||
|
let signer = crypto::PublicKey::decode(reader)?;
|
||||||
|
let refs = SignedRefs::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::RefsUpdate { id, signer, refs })
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,11 +163,11 @@ impl Peer {
|
||||||
ctx.write(self.addr, inventory);
|
ctx.write(self.addr, inventory);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
PeerState::Negotiated { id, git, .. },
|
PeerState::Negotiated { git, .. },
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node,
|
||||||
timestamp,
|
timestamp,
|
||||||
inv,
|
inv,
|
||||||
origin,
|
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
let now = ctx.clock.local_time();
|
let now = ctx.clock.local_time();
|
||||||
|
|
@ -184,13 +184,13 @@ impl Peer {
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
ctx.process_inventory(&inv, origin.unwrap_or(*id), git);
|
ctx.process_inventory(&inv, node, git);
|
||||||
|
|
||||||
if ctx.config.relay {
|
if ctx.config.relay {
|
||||||
return Ok(Some(Message::Inventory {
|
return Ok(Some(Message::Inventory {
|
||||||
timestamp,
|
node,
|
||||||
inv,
|
inv,
|
||||||
origin: origin.or(Some(*id)),
|
timestamp,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,348 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
use std::{io, mem, net};
|
||||||
|
|
||||||
|
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::crypto::{PublicKey, Signature};
|
||||||
|
use crate::git;
|
||||||
|
use crate::git::fmt;
|
||||||
|
use crate::hash::Digest;
|
||||||
|
use crate::identity::Id;
|
||||||
|
use crate::storage::refs::Refs;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("i/o: {0}")]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
#[error("UTF-8 error: {0}")]
|
||||||
|
FromUtf8(#[from] FromUtf8Error),
|
||||||
|
#[error("invalid size: expected {expected}, got {actual}")]
|
||||||
|
InvalidSize { expected: usize, actual: usize },
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidRefName(#[from] fmt::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Encode {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Decode: Sized {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode an object into a vector.
|
||||||
|
pub fn serialize<T: Encode + ?Sized>(data: &T) -> Vec<u8> {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let len = data
|
||||||
|
.encode(&mut buffer)
|
||||||
|
.expect("in-memory writes don't error");
|
||||||
|
|
||||||
|
debug_assert_eq!(len, buffer.len());
|
||||||
|
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for u8 {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_u8(*self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for u16 {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_u16::<NetworkEndian>(*self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for u32 {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_u32::<NetworkEndian>(*self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for u64 {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_u64::<NetworkEndian>(*self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for usize {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let u = (*self)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
|
||||||
|
|
||||||
|
writer.write_u32::<NetworkEndian>(u)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of_val(&u))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for PublicKey {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.as_bytes().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const T: usize> Encode for &[u8; T] {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_all(*self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const T: usize> Encode for [u8; T] {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
writer.write_all(self)?;
|
||||||
|
|
||||||
|
Ok(mem::size_of::<Self>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Encode for &[T]
|
||||||
|
where
|
||||||
|
T: Encode,
|
||||||
|
{
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let mut n = self.len().encode(writer)?;
|
||||||
|
|
||||||
|
for item in self.iter() {
|
||||||
|
n += item.encode(writer)?;
|
||||||
|
}
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for net::IpAddr {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
match self {
|
||||||
|
net::IpAddr::V4(addr) => addr.octets().encode(writer),
|
||||||
|
net::IpAddr::V6(addr) => addr.octets().encode(writer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for str {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
|
n += self.len().encode(writer)?;
|
||||||
|
n += self.as_bytes().encode(writer)?;
|
||||||
|
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for String {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.as_str().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for git::Url {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.to_string().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Digest {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.as_ref().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Id {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.deref().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Refs {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let mut n = self.len().encode(writer)?;
|
||||||
|
|
||||||
|
for (name, oid) in self.iter() {
|
||||||
|
n += name.as_str().encode(writer)?;
|
||||||
|
n += oid.encode(writer)?;
|
||||||
|
}
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Signature {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
self.to_bytes().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for git::Oid {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
// Nb. We use length-encoding here to support future SHA-2 object ids.
|
||||||
|
self.as_bytes().encode(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
impl Decode for PublicKey {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let buf: [u8; 32] = Decode::decode(reader)?;
|
||||||
|
|
||||||
|
PublicKey::try_from(buf)
|
||||||
|
.map_err(|e| Error::Io(io::Error::new(io::ErrorKind::InvalidInput, e.to_string())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Refs {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let len = usize::decode(reader)?;
|
||||||
|
let mut refs = BTreeMap::new();
|
||||||
|
|
||||||
|
for _ in 0..len {
|
||||||
|
let name = String::decode(reader)?;
|
||||||
|
let name = git::RefString::try_from(name).map_err(Error::from)?;
|
||||||
|
let oid = git::Oid::decode(reader)?;
|
||||||
|
|
||||||
|
refs.insert(name, oid);
|
||||||
|
}
|
||||||
|
Ok(refs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for git::Oid {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let len = usize::decode(reader)?;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
const expected: usize = mem::size_of::<git2::Oid>();
|
||||||
|
|
||||||
|
if len != expected {
|
||||||
|
return Err(Error::InvalidSize {
|
||||||
|
expected,
|
||||||
|
actual: len,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf: [u8; expected] = Decode::decode(reader)?;
|
||||||
|
let oid = git2::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
|
||||||
|
let oid = git::Oid::from(oid);
|
||||||
|
|
||||||
|
Ok(oid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Signature {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let bytes: [u8; 64] = Decode::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Signature::from(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for u8 {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
reader.read_u8().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for u16 {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
reader.read_u16::<NetworkEndian>().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for u32 {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
reader.read_u32::<NetworkEndian>().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for u64 {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
reader.read_u64::<NetworkEndian>().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for usize {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let size: usize = u64::decode(reader)?
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Decode for [u8; N] {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let mut ary = [0; N];
|
||||||
|
reader.read_exact(&mut ary)?;
|
||||||
|
|
||||||
|
Ok(ary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Decode for Vec<T>
|
||||||
|
where
|
||||||
|
T: Decode,
|
||||||
|
{
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let len: usize = usize::decode(reader)?;
|
||||||
|
let mut vec = Vec::with_capacity(len);
|
||||||
|
|
||||||
|
for _ in 0..len {
|
||||||
|
let item = T::decode(reader)?;
|
||||||
|
vec.push(item);
|
||||||
|
}
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for String {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let len = usize::decode(reader)?;
|
||||||
|
let mut bytes = vec![0; len];
|
||||||
|
|
||||||
|
reader.read_exact(&mut bytes)?;
|
||||||
|
|
||||||
|
let string = String::from_utf8(bytes)?;
|
||||||
|
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for git::Url {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let string = String::decode(reader)?;
|
||||||
|
|
||||||
|
Self::from_bytes(string.as_bytes())
|
||||||
|
.map_err(|e| Error::Io(io::Error::new(io::ErrorKind::InvalidInput, e.to_string())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Id {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let digest: Digest = Decode::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::from(digest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Digest {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let bytes: [u8; 32] = Decode::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self::from(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ use crate::crypto;
|
||||||
use crate::crypto::{PublicKey, Signature, Signer, Unverified, Verified};
|
use crate::crypto::{PublicKey, Signature, Signer, Unverified, Verified};
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::git::Oid;
|
use crate::git::Oid;
|
||||||
|
use crate::protocol::wire;
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
use crate::storage::{ReadRepository, RemoteId, WriteRepository};
|
use crate::storage::{ReadRepository, RemoteId, WriteRepository};
|
||||||
|
|
||||||
|
|
@ -295,6 +296,30 @@ impl SignedRefs<Verified> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V> wire::Encode for SignedRefs<V> {
|
||||||
|
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
|
n += self.refs.encode(writer)?;
|
||||||
|
n += self.signature.encode(writer)?;
|
||||||
|
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wire::Decode for SignedRefs<Unverified> {
|
||||||
|
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
|
||||||
|
let refs = Refs::decode(reader)?;
|
||||||
|
let signature = Signature::decode(reader)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
refs,
|
||||||
|
signature,
|
||||||
|
_verified: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<V> Deref for SignedRefs<V> {
|
impl<V> Deref for SignedRefs<V> {
|
||||||
type Target = Refs;
|
type Target = Refs;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,9 +146,9 @@ fn test_inventory_sync() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&bob.addr(),
|
&bob.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: bob.id(),
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
inv: projs.clone(),
|
inv: projs.clone(),
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -213,9 +213,9 @@ fn test_inventory_relay_bad_timestamp() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&bob.addr(),
|
&bob.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: bob.id(),
|
||||||
timestamp,
|
timestamp,
|
||||||
inv: vec![],
|
inv: vec![],
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
|
|
@ -240,15 +240,15 @@ fn test_inventory_relay() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&bob.addr(),
|
&bob.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: bob.id(),
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
inv: inv.clone(),
|
inv: inv.clone(),
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
alice.messages(&eve.addr()).next(),
|
alice.messages(&eve.addr()).next(),
|
||||||
Some(Message::Inventory { timestamp, origin, .. })
|
Some(Message::Inventory { node, timestamp, .. })
|
||||||
if origin == Some(bob.id()) && timestamp == now
|
if node == bob.id() && timestamp == now
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
alice.messages(&bob.addr()).next(),
|
alice.messages(&bob.addr()).next(),
|
||||||
|
|
@ -259,9 +259,9 @@ fn test_inventory_relay() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&bob.addr(),
|
&bob.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: bob.id(),
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
inv: inv.clone(),
|
inv: inv.clone(),
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
|
|
@ -273,15 +273,15 @@ fn test_inventory_relay() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&bob.addr(),
|
&bob.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: bob.id(),
|
||||||
timestamp: now + 1,
|
timestamp: now + 1,
|
||||||
inv: inv.clone(),
|
inv: inv.clone(),
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
alice.messages(&eve.addr()).next(),
|
alice.messages(&eve.addr()).next(),
|
||||||
Some(Message::Inventory { timestamp, origin, .. })
|
Some(Message::Inventory { node, timestamp, .. })
|
||||||
if origin == Some(bob.id()) && timestamp == now + 1,
|
if node == bob.id() && timestamp == now + 1,
|
||||||
"Sending a new inventory does trigger the relay"
|
"Sending a new inventory does trigger the relay"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -289,15 +289,15 @@ fn test_inventory_relay() {
|
||||||
alice.receive(
|
alice.receive(
|
||||||
&eve.addr(),
|
&eve.addr(),
|
||||||
Message::Inventory {
|
Message::Inventory {
|
||||||
|
node: eve.id(),
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
inv,
|
inv,
|
||||||
origin: None,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
alice.messages(&bob.addr()).next(),
|
alice.messages(&bob.addr()).next(),
|
||||||
Some(Message::Inventory { timestamp, origin, .. })
|
Some(Message::Inventory { node, timestamp, .. })
|
||||||
if origin == Some(eve.id()) && timestamp == now
|
if node == eve.id() && timestamp == now
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue