node: Move over to binary protocol

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-09-08 14:36:09 +02:00
parent b3d3077ccc
commit 9d3030a093
No known key found for this signature in database
8 changed files with 386 additions and 74 deletions

View File

@ -1,7 +1,8 @@
use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::protocol::message::Envelope; use crate::protocol::message::Envelope;
use serde::Deserialize; use crate::protocol::wire;
/// Message stream decoder. /// Message stream decoder.
/// ///
@ -21,7 +22,7 @@ impl<D> From<Vec<u8>> for Decoder<D> {
} }
} }
impl<'de, D: Deserialize<'de>> Decoder<D> { impl<D: wire::Decode> Decoder<D> {
/// Create a new stream decoder. /// Create a new stream decoder.
pub fn new(capacity: usize) -> Self { pub fn new(capacity: usize) -> Self {
Self { Self {
@ -36,23 +37,36 @@ impl<'de, D: Deserialize<'de>> Decoder<D> {
} }
/// Decode and return the next message. Returns [`None`] if nothing was decoded. /// Decode and return the next message. Returns [`None`] if nothing was decoded.
pub fn decode_next(&mut self) -> Result<Option<D>, serde_json::Error> { pub fn decode_next(&mut self) -> Result<Option<D>, wire::Error> {
let mut de = serde_json::Deserializer::from_reader(self.unparsed.as_slice()).into_iter(); let mut reader = io::Cursor::new(self.unparsed.as_mut_slice());
match D::decode(&mut reader) {
Ok(msg) => {
let pos = reader.position() as usize;
self.unparsed.drain(..pos);
match de.next() {
Some(Ok(msg)) => {
self.unparsed.drain(..de.byte_offset());
Ok(Some(msg)) Ok(Some(msg))
} }
Some(Err(err)) if err.is_eof() => Ok(None), Err(err) if err.is_eof() => Ok(None),
Err(err) => Err(err),
result => result.transpose(),
} }
} }
} }
impl<'de, D: Deserialize<'de>> Iterator for Decoder<D> { impl<D: wire::Decode> io::Write for Decoder<D> {
type Item = Result<D, serde_json::Error>; fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.input(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<D: wire::Decode> Iterator for Decoder<D> {
type Item = Result<D, wire::Error>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.decode_next().transpose() self.decode_next().transpose()
@ -64,14 +78,14 @@ mod test {
use super::*; use super::*;
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
const MSG_HELLO: &[u8] = b"{\"cmd\":\"hello\"}"; const MSG_HELLO: &[u8] = &[5, b'h', b'e', b'l', b'l', b'o'];
const MSG_BYE: &[u8] = b"{\"cmd\":\"goodbye\"}"; const MSG_BYE: &[u8] = &[3, b'b', b'y', b'e'];
#[quickcheck] #[quickcheck]
fn prop_decode_next(chunk_size: usize) { fn prop_decode_next(chunk_size: usize) {
let mut bytes = vec![]; let mut bytes = vec![];
let mut msgs = vec![]; let mut msgs = vec![];
let mut decoder = Decoder::<serde_json::Value>::new(64); let mut decoder = Decoder::<String>::new(8);
let chunk_size = 1 + chunk_size % MSG_HELLO.len() + MSG_BYE.len(); let chunk_size = 1 + chunk_size % MSG_HELLO.len() + MSG_BYE.len();
@ -88,17 +102,7 @@ mod test {
assert_eq!(decoder.unparsed.len(), 0); assert_eq!(decoder.unparsed.len(), 0);
assert_eq!(msgs.len(), 2); assert_eq!(msgs.len(), 2);
assert_eq!( assert_eq!(msgs[0], String::from("hello"));
msgs[0], assert_eq!(msgs[1], String::from("bye"));
serde_json::json!({
"cmd": "hello",
})
);
assert_eq!(
msgs[1],
serde_json::json!({
"cmd": "goodbye",
})
);
} }
} }

View File

@ -5,7 +5,7 @@ pub mod peer;
pub mod wire; 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, net, net::IpAddr};
use crossbeam_channel as chan; use crossbeam_channel as chan;
use fastrand::Rng; use fastrand::Rng;
@ -26,6 +26,7 @@ use crate::identity::{Id, Project, PublicKey};
use crate::protocol::config::ProjectTracking; use crate::protocol::config::ProjectTracking;
use crate::protocol::message::Message; use crate::protocol::message::Message;
use crate::protocol::peer::{Peer, PeerError, PeerState}; use crate::protocol::peer::{Peer, PeerError, PeerState};
use crate::protocol::wire::Encode;
use crate::storage::{self, ReadRepository, WriteRepository}; use crate::storage::{self, ReadRepository, WriteRepository};
use crate::storage::{Inventory, WriteStorage}; use crate::storage::{Inventory, WriteStorage};
@ -546,7 +547,7 @@ where
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (peer, msgs) = if let Some(peer) = self.peers.get_mut(&peer) { let (peer, msgs) = if let Some(peer) = self.peers.get_mut(&peer) {
let decoder = &mut peer.inbox(); let decoder = peer.inbox();
decoder.input(bytes); decoder.input(bytes);
let mut msgs = Vec::with_capacity(1); let mut msgs = Vec::with_capacity(1);
@ -555,8 +556,10 @@ where
Ok(Some(msg)) => msgs.push(msg), Ok(Some(msg)) => msgs.push(msg),
Ok(None) => break, Ok(None) => break,
Err(_err) => { Err(err) => {
// TODO: Disconnect peer. // TODO: Disconnect peer.
error!("Invalid message received from {}: {}", peer.addr, err);
return; return;
} }
} }
@ -731,8 +734,13 @@ where
fn fetch(&mut self, proj_id: &Id, remote: &Url) { fn fetch(&mut self, proj_id: &Id, remote: &Url) {
// TODO: Verify refs before adding them to storage. // TODO: Verify refs before adding them to storage.
let mut repo = self.storage.repository(proj_id).unwrap(); let mut repo = self.storage.repository(proj_id).unwrap();
let mut path = remote.path.clone();
path.push(b'/');
path.extend(proj_id.to_string().into_bytes());
repo.fetch(&Url { repo.fetch(&Url {
path: format!("/{}", proj_id).into(), path,
..remote.clone() ..remote.clone()
}) })
.unwrap(); .unwrap();
@ -757,22 +765,24 @@ impl<S, T, G> Context<S, T, G> {
} }
fn write_all(&mut self, remote: net::SocketAddr, msgs: impl IntoIterator<Item = Message>) { fn write_all(&mut self, remote: net::SocketAddr, msgs: impl IntoIterator<Item = Message>) {
let mut buf = io::Cursor::new(Vec::new()); let mut buf = Vec::new();
for msg in msgs { for msg in msgs {
debug!("Write {:?} to {}", &msg, remote.ip()); debug!("Write {:?} to {}", &msg, remote.ip());
let envelope = self.config.network.envelope(msg); let envelope = self.config.network.envelope(msg);
serde_json::to_writer(&mut buf, &envelope).unwrap(); envelope
.encode(&mut buf)
.expect("writing to an in-memory buffer doesn't fail");
} }
self.io.push_back(Io::Write(remote, buf.into_inner())); self.io.push_back(Io::Write(remote, buf));
} }
fn write(&mut self, remote: net::SocketAddr, msg: Message) { fn write(&mut self, remote: net::SocketAddr, msg: Message) {
debug!("Write {:?} to {}", &msg, remote.ip()); debug!("Write {:?} to {}", &msg, remote.ip());
let envelope = self.config.network.envelope(msg); let envelope = self.config.network.envelope(msg);
let bytes = serde_json::to_vec(&envelope).unwrap(); let bytes = wire::serialize(&envelope);
self.io.push_back(Io::Write(remote, bytes)); self.io.push_back(Io::Write(remote, bytes));
} }

View File

@ -3,6 +3,7 @@ use std::net;
use git_url::Url; use git_url::Url;
use crate::collections::HashSet; use crate::collections::HashSet;
use crate::git;
use crate::identity::{Id, PublicKey}; use crate::identity::{Id, PublicKey};
use crate::protocol::message::{Address, Envelope, Message}; use crate::protocol::message::{Address, Envelope, Message};
@ -88,7 +89,11 @@ impl Default for Config {
remote_tracking: RemoteTracking::default(), remote_tracking: RemoteTracking::default(),
relay: true, relay: true,
listen: vec![], listen: vec![],
git_url: Url::default(), git_url: Url {
scheme: git::url::Scheme::File,
path: "/dev/null".to_owned().into(),
..Url::default()
},
} }
} }
} }

View File

@ -12,7 +12,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)] #[derive(Debug, Serialize, Deserialize, 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,
@ -47,6 +47,26 @@ pub enum Address {
}, },
} }
impl wire::Encode for Envelope {
fn encode<W: std::io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
let mut n = 0;
n += self.magic.encode(writer)?;
n += self.msg.encode(writer)?;
Ok(n)
}
}
impl wire::Decode for Envelope {
fn decode<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Self, wire::Error> {
let magic = u32::decode(reader)?;
let msg = Message::decode(reader)?;
Ok(Self { magic, msg })
}
}
impl wire::Encode for Address { impl wire::Encode for Address {
fn encode<W: std::io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, std::io::Error> { fn encode<W: std::io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
let mut n = 0; let mut n = 0;
@ -84,6 +104,13 @@ impl wire::Decode for Address {
Ok(Self::Ip { ip, port }) Ok(Self::Ip { ip, port })
} }
2 => {
let octets: [u8; 16] = wire::Decode::decode(reader)?;
let ip = net::IpAddr::from(net::Ipv6Addr::from(octets));
let port = u16::decode(reader)?;
Ok(Self::Ip { ip, port })
}
_ => { _ => {
todo!(); todo!();
} }
@ -108,6 +135,7 @@ pub struct NodeAnnouncement {
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 = serde_json::to_vec(self).unwrap(); let msg = serde_json::to_vec(self).unwrap();
self.id.verify(signature, &msg).is_ok() self.id.verify(signature, &msg).is_ok()
} }
@ -115,7 +143,7 @@ impl NodeAnnouncement {
/// 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)] #[derive(Debug, Serialize, Deserialize, 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 {
@ -295,9 +323,60 @@ impl wire::Decode for Message {
Ok(Self::RefsUpdate { id, signer, refs }) Ok(Self::RefsUpdate { id, signer, refs })
} }
_ => { n => {
todo!(); todo!("Mesage type {} is not yet implemented", n);
} }
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use quickcheck_macros::quickcheck;
use crate::decoder::Decoder;
use crate::protocol::wire::{self, Encode};
#[quickcheck]
fn prop_message_encode_decode(message: Message) {
assert_eq!(
wire::deserialize::<Message>(&wire::serialize(&message)).unwrap(),
message
);
}
#[quickcheck]
fn prop_envelope_encode_decode(envelope: Envelope) {
assert_eq!(
wire::deserialize::<Envelope>(&wire::serialize(&envelope)).unwrap(),
envelope
);
}
#[test]
fn prop_envelope_decoder() {
fn property(items: Vec<Envelope>) {
let mut decoder = Decoder::<Envelope>::new(8);
for item in &items {
item.encode(&mut decoder).unwrap();
}
for item in items {
assert_eq!(decoder.next().unwrap().unwrap(), item);
}
}
quickcheck::QuickCheck::new()
.gen(quickcheck::Gen::new(16))
.quickcheck(property as fn(items: Vec<Envelope>));
}
#[quickcheck]
fn prop_addr(addr: Address) {
assert_eq!(
wire::deserialize::<Address>(&wire::serialize(&addr)).unwrap(),
addr
);
}
}

View File

@ -23,6 +23,17 @@ pub enum Error {
InvalidSize { expected: usize, actual: usize }, InvalidSize { expected: usize, actual: usize },
#[error(transparent)] #[error(transparent)]
InvalidRefName(#[from] fmt::Error), InvalidRefName(#[from] fmt::Error),
#[error("invalid git url `{url}`: {error}")]
InvalidGitUrl {
url: String,
error: git::url::parse::Error,
},
}
impl Error {
pub fn is_eof(&self) -> bool {
matches!(self, Self::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof)
}
} }
pub trait Encode { pub trait Encode {
@ -45,6 +56,13 @@ pub fn serialize<T: Encode + ?Sized>(data: &T) -> Vec<u8> {
buffer buffer
} }
/// Decode an object from a vector.
pub fn deserialize<T: Decode>(data: &[u8]) -> Result<T, Error> {
let mut cursor = io::Cursor::new(data);
T::decode(&mut cursor)
}
impl Encode for u8 { impl Encode for u8 {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> { fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
writer.write_u8(*self)?; writer.write_u8(*self)?;
@ -79,13 +97,14 @@ impl Encode for u64 {
impl Encode for usize { impl Encode for usize {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> { fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
let u = (*self) assert!(
.try_into() *self <= u32::MAX as usize,
.map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?; "Cannot encode sizes larger than {}",
u32::MAX
);
writer.write_u32::<NetworkEndian>(*self as u32)?;
writer.write_u32::<NetworkEndian>(u)?; Ok(mem::size_of::<u32>())
Ok(mem::size_of_val(&u))
} }
} }
@ -134,14 +153,18 @@ impl Encode for net::IpAddr {
} }
} }
impl Encode for str { impl Encode for &str {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> { fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
let mut n = 0; assert!(self.len() <= u8::MAX as usize);
n += self.len().encode(writer)?; let n = (self.len() as u8).encode(writer)?;
n += self.as_bytes().encode(writer)?; let bytes = self.as_bytes();
Ok(n) // Nb. Don't use the [`Encode`] instance here for &[u8], because we are prefixing the
// length ourselves.
writer.write_all(bytes)?;
Ok(n + bytes.len())
} }
} }
@ -276,7 +299,7 @@ impl Decode for u64 {
impl Decode for usize { impl Decode for usize {
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> { fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
let size: usize = u64::decode(reader)? let size: usize = u32::decode(reader)?
.try_into() .try_into()
.map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?; .map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
@ -311,8 +334,8 @@ where
impl Decode for String { impl Decode for String {
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> { fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
let len = usize::decode(reader)?; let len = u8::decode(reader)?;
let mut bytes = vec![0; len]; let mut bytes = vec![0; len as usize];
reader.read_exact(&mut bytes)?; reader.read_exact(&mut bytes)?;
@ -324,10 +347,11 @@ impl Decode for String {
impl Decode for git::Url { impl Decode for git::Url {
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> { fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
let string = String::decode(reader)?; let url = String::decode(reader)?;
let url = Self::from_bytes(url.as_bytes())
.map_err(|error| Error::InvalidGitUrl { url, error })?;
Self::from_bytes(string.as_bytes()) Ok(url)
.map_err(|e| Error::Io(io::Error::new(io::ErrorKind::InvalidInput, e.to_string())))
} }
} }
@ -346,3 +370,118 @@ impl Decode for Digest {
Ok(Self::from(bytes)) Ok(Self::from(bytes))
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use quickcheck_macros::quickcheck;
use crate::crypto::Unverified;
use crate::storage::refs::SignedRefs;
use crate::test::arbitrary;
#[quickcheck]
fn prop_u8(input: u8) {
assert_eq!(deserialize::<u8>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_u16(input: u16) {
assert_eq!(deserialize::<u16>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_u32(input: u32) {
assert_eq!(deserialize::<u32>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_u64(input: u64) {
assert_eq!(deserialize::<u64>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_usize(input: usize) -> quickcheck::TestResult {
if input > u32::MAX as usize {
return quickcheck::TestResult::discard();
}
assert_eq!(deserialize::<usize>(&serialize(&input)).unwrap(), input);
quickcheck::TestResult::passed()
}
#[quickcheck]
fn prop_string(input: String) -> quickcheck::TestResult {
if input.len() > u8::MAX as usize {
return quickcheck::TestResult::discard();
}
assert_eq!(deserialize::<String>(&serialize(&input)).unwrap(), input);
quickcheck::TestResult::passed()
}
#[quickcheck]
fn prop_pubkey(input: PublicKey) {
assert_eq!(deserialize::<PublicKey>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_id(input: Id) {
assert_eq!(deserialize::<Id>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_digest(input: Digest) {
assert_eq!(deserialize::<Digest>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_refs(input: Refs) {
assert_eq!(deserialize::<Refs>(&serialize(&input)).unwrap(), input);
}
#[quickcheck]
fn prop_signature(input: arbitrary::ByteArray<64>) {
let signature = Signature::from(input.into_inner());
assert_eq!(
deserialize::<Signature>(&serialize(&signature)).unwrap(),
signature
);
}
#[quickcheck]
fn prop_oid(input: arbitrary::ByteArray<20>) {
let oid = git::Oid::try_from(input.into_inner().as_slice()).unwrap();
assert_eq!(deserialize::<git::Oid>(&serialize(&oid)).unwrap(), oid);
}
#[quickcheck]
fn prop_signed_refs(input: SignedRefs<Unverified>) {
assert_eq!(
deserialize::<SignedRefs<Unverified>>(&serialize(&input)).unwrap(),
input
);
}
#[test]
fn test_string() {
assert_eq!(
serialize(&String::from("hello")),
vec![5, b'h', b'e', b'l', b'l', b'o']
);
}
#[test]
fn test_git_url() {
let url = git::Url {
scheme: git::url::Scheme::Https,
path: "/git".to_owned().into(),
host: Some("seed.radicle.xyz".to_owned()),
port: Some(8888),
..git::Url::default()
};
assert_eq!(deserialize::<git::Url>(&serialize(&url)).unwrap(), url);
}
}

View File

@ -45,7 +45,8 @@ impl ReadStorage for Storage {
fn url(&self) -> git::Url { fn url(&self) -> git::Url {
git::Url { git::Url {
scheme: git_url::Scheme::File, scheme: git_url::Scheme::File,
host: Some(self.path.to_string_lossy().to_string()), host: None,
path: self.path.to_string_lossy().to_string().into(),
..git::Url::default() ..git::Url::default()
} }
} }

View File

@ -1,5 +1,6 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::hash::Hash; use std::hash::Hash;
use std::net;
use std::ops::RangeBounds; use std::ops::RangeBounds;
use std::path::PathBuf; use std::path::PathBuf;
@ -7,13 +8,15 @@ use nonempty::NonEmpty;
use quickcheck::Arbitrary; use quickcheck::Arbitrary;
use crate::collections::HashMap; use crate::collections::HashMap;
use crate::crypto::{self, Signer}; use crate::crypto::{self, Signer, Unverified};
use crate::crypto::{PublicKey, SecretKey}; 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::{NodeId, Timestamp};
use crate::storage; use crate::storage;
use crate::storage::refs::Refs; use crate::storage::refs::{Refs, SignedRefs};
use crate::test::storage::MockStorage; use crate::test::storage::MockStorage;
use super::crypto::MockSigner; use super::crypto::MockSigner;
@ -35,6 +38,75 @@ pub fn gen<T: Arbitrary>(size: usize) -> T {
T::arbitrary(&mut gen) T::arbitrary(&mut gen)
} }
#[derive(Clone, Debug)]
pub struct ByteArray<const N: usize>([u8; N]);
impl<const N: usize> ByteArray<N> {
pub fn into_inner(self) -> [u8; N] {
self.0
}
}
impl<const N: usize> Arbitrary for ByteArray<N> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut bytes: [u8; N] = [0; N];
for byte in &mut bytes {
*byte = u8::arbitrary(g);
}
Self(bytes)
}
}
impl Arbitrary for Envelope {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
magic: u32::arbitrary(g),
msg: Message::arbitrary(g),
}
}
}
impl Arbitrary for Message {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let type_id = g.choose(&[4, 6, 8]).unwrap();
match type_id {
4 => Self::GetInventory {
ids: Vec::<Id>::arbitrary(g),
},
6 => Self::Inventory {
node: NodeId::arbitrary(g),
inv: Vec::<Id>::arbitrary(g),
timestamp: Timestamp::arbitrary(g),
},
8 => Self::RefsUpdate {
id: Id::arbitrary(g),
signer: PublicKey::arbitrary(g),
refs: SignedRefs::<Unverified>::arbitrary(g),
},
_ => unreachable!(),
}
}
}
impl Arbitrary for Address {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
if bool::arbitrary(g) {
Address::Ip {
ip: net::IpAddr::V4(net::Ipv4Addr::from(u32::arbitrary(g))),
port: u16::arbitrary(g),
}
} else {
let octets: [u8; 16] = ByteArray::<16>::arbitrary(g).into_inner();
Address::Ip {
ip: net::IpAddr::V6(net::Ipv6Addr::from(octets)),
port: u16::arbitrary(g),
}
}
}
}
impl Arbitrary for storage::Remotes<crypto::Verified> { impl Arbitrary for storage::Remotes<crypto::Verified> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self { fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> = let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> =
@ -104,6 +176,16 @@ impl Arbitrary for Doc {
} }
} }
impl Arbitrary for SignedRefs<Unverified> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let bytes: ByteArray<64> = Arbitrary::arbitrary(g);
let signature = crypto::Signature::from(bytes.into_inner());
let refs = Refs::arbitrary(g);
Self::new(refs, signature)
}
}
impl Arbitrary for Refs { impl Arbitrary for Refs {
fn arbitrary(g: &mut quickcheck::Gen) -> Self { fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut refs: BTreeMap<git::RefString, storage::Oid> = BTreeMap::new(); let mut refs: BTreeMap<git::RefString, storage::Oid> = BTreeMap::new();
@ -146,12 +228,8 @@ impl Arbitrary for storage::Remote<crypto::Verified> {
impl Arbitrary for MockSigner { impl Arbitrary for MockSigner {
fn arbitrary(g: &mut quickcheck::Gen) -> Self { fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut bytes: [u8; 32] = [0; 32]; let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
MockSigner::from(SecretKey::from(bytes.into_inner()))
for byte in &mut bytes {
*byte = u8::arbitrary(g);
}
MockSigner::from(SecretKey::from(bytes))
} }
} }
@ -173,12 +251,8 @@ impl Arbitrary for PublicKey {
fn arbitrary(g: &mut quickcheck::Gen) -> Self { fn arbitrary(g: &mut quickcheck::Gen) -> Self {
use ed25519_consensus::SigningKey; use ed25519_consensus::SigningKey;
let mut bytes: [u8; 32] = [0; 32]; let bytes: ByteArray<32> = Arbitrary::arbitrary(g);
let sk = SigningKey::from(bytes.into_inner());
for byte in &mut bytes {
*byte = u8::arbitrary(g);
}
let sk = SigningKey::from(bytes);
let vk = sk.verification_key(); let vk = sk.verification_key();
PublicKey(vk) PublicKey(vk)

View File

@ -129,7 +129,7 @@ where
} }
pub fn receive(&mut self, peer: &net::SocketAddr, msg: Message) { pub fn receive(&mut self, peer: &net::SocketAddr, msg: Message) {
let bytes = serde_json::to_vec(&self.config().network.envelope(msg)).unwrap(); let bytes = wire::serialize(&self.config().network.envelope(msg));
self.protocol.received_bytes(peer, &bytes); self.protocol.received_bytes(peer, &bytes);
} }
@ -137,7 +137,7 @@ where
pub fn connect_from(&mut self, peer: &Self) { pub fn connect_from(&mut self, peer: &Self) {
let remote = simulator::Peer::<Protocol<S>>::addr(peer); let remote = simulator::Peer::<Protocol<S>>::addr(peer);
let local = net::SocketAddr::new(self.ip, self.rng.u16(..)); let local = net::SocketAddr::new(self.ip, self.rng.u16(..));
let git = format!("file://{}.git", remote.ip()); let git = format!("file:///{}.git", remote.ip());
let git = Url::from_bytes(git.as_bytes()).unwrap(); let git = Url::from_bytes(git.as_bytes()).unwrap();
self.initialize(); self.initialize();