node: Fix encoding of types, add fixtures

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-08-25 14:56:43 +02:00
parent 1ff8f625ff
commit 32591e6aa4
No known key found for this signature in database
7 changed files with 128 additions and 47 deletions

View File

@ -1,4 +1,5 @@
use std::fmt; use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{ use sha2::{
@ -8,11 +9,11 @@ use sha2::{
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)] #[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseError { pub enum DecodeError {
#[error("invalid string length")]
InvalidLength,
#[error(transparent)] #[error(transparent)]
ParseInt(#[from] std::num::ParseIntError), Multibase(#[from] multibase::Error),
#[error("invalid digest length {0}")]
InvalidLength(usize),
} }
/// A SHA-256 hash. /// A SHA-256 hash.
@ -25,23 +26,24 @@ impl Digest {
} }
pub fn encode(&self) -> String { pub fn encode(&self) -> String {
self.to_string() multibase::encode(multibase::Base::Base58Btc, &self.0)
} }
pub fn decode(s: &str) -> Result<Self, ParseError> { pub fn decode(s: &str) -> Result<Self, DecodeError> {
if s.len() != 64 { let (_, bytes) = multibase::decode(s)?;
Err(ParseError::InvalidLength) let array = bytes
} else { .try_into()
let mut bytes: [u8; 32] = Default::default(); .map_err(|v: Vec<u8>| DecodeError::InvalidLength(v.len()))?;
for (i, byte) in (0..s.len())
.step_by(2) Ok(Self(array))
.map(|i| u8::from_str_radix(&s[i..i + 2], 16)) }
.enumerate() }
{
bytes[i] = byte?; impl FromStr for Digest {
} type Err = DecodeError;
Ok(Self(bytes))
} fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::decode(s)
} }
} }
@ -77,3 +79,17 @@ impl From<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>> for Digest {
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);
}
}

View File

@ -8,6 +8,14 @@ use thiserror::Error;
use crate::hash; use crate::hash;
#[derive(Error, Debug)]
pub enum ProjIdError {
#[error("invalid ref '{0}'")]
InvalidRef(String),
#[error("invalid digest: {0}")]
InvalidDigest(#[from] hash::DecodeError),
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ProjId(hash::Digest); pub struct ProjId(hash::Digest);
@ -28,18 +36,20 @@ impl ProjId {
multibase::encode(multibase::Base::Base58Btc, &self.0.as_ref()) multibase::encode(multibase::Base::Base58Btc, &self.0.as_ref())
} }
pub(crate) fn from_ref(s: &str) -> Result<ProjId, IdError> { pub(crate) fn from_ref(s: &str) -> Result<ProjId, ProjIdError> {
if let Some(s) = s.split('/').nth(2) { if let Some(s) = s.split('/').nth(2) {
let mut array: [u8; 32] = [0; 32]; let id = Self::from_str(s)?;
let bytes = bs58::decode(s).into(&mut array)?; return Ok(id);
// TODO: Multi-hash?
assert_eq!(bytes, array.len());
return Ok(Self(hash::Digest::from(array)));
} }
Err(IdError::InvalidRef(s.to_owned())) Err(ProjIdError::InvalidRef(s.to_owned()))
}
}
impl FromStr for ProjId {
type Err = hash::DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hash::Digest::from_str(s)?))
} }
} }
@ -93,15 +103,15 @@ impl UserId {
} }
impl FromStr for UserId { impl FromStr for UserId {
type Err = IdError; type Err = UserIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut array: [u8; 32] = [0; 32]; let (_, bytes) = multibase::decode(s)?;
let bytes = bs58::decode(s).into(&mut array)?; let array: [u8; 32] = bytes
.try_into()
.map_err(|v: Vec<u8>| UserIdError::InvalidLength(v.len()))?;
let key = VerificationKey::try_from(VerificationKeyBytes::from(array))?; let key = VerificationKey::try_from(VerificationKeyBytes::from(array))?;
assert_eq!(bytes, array.len());
Ok(Self(key)) Ok(Self(key))
} }
} }
@ -115,11 +125,11 @@ impl Deref for UserId {
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum IdError { pub enum UserIdError {
#[error("invalid ref '{0}'")] #[error("invalid length {0}")]
InvalidRef(String), InvalidLength(usize),
#[error("invalid base58 string: {0}")] #[error("invalid multibase string: {0}")]
Base58(#[from] bs58::decode::Error), Multibase(#[from] multibase::Error),
#[error("invalid key: {0}")] #[error("invalid key: {0}")]
InvalidKey(#[from] ed25519_consensus::Error), InvalidKey(#[from] ed25519_consensus::Error),
} }
@ -178,4 +188,12 @@ mod test {
assert!(!hm.insert(a)); assert!(!hm.insert(a));
assert!(!hm.insert(b)); assert!(!hm.insert(b));
} }
#[quickcheck]
fn prop_encode_decode(input: UserId) {
let encoded = input.to_string();
let decoded = UserId::from_str(&encoded).unwrap();
assert_eq!(input, decoded);
}
} }

View File

@ -13,7 +13,7 @@ pub use radicle_git_ext::Oid;
use crate::collections::HashMap; use crate::collections::HashMap;
use crate::identity; use crate::identity;
use crate::identity::{IdError, ProjId, UserId}; use crate::identity::{ProjId, ProjIdError, UserId};
pub static RAD_ID_GLOB: Lazy<refspec::PatternString> = pub static RAD_ID_GLOB: Lazy<refspec::PatternString> =
Lazy::new(|| refspec::pattern!("refs/namespaces/*/refs/rad/id")); Lazy::new(|| refspec::pattern!("refs/namespaces/*/refs/rad/id"));
@ -30,7 +30,7 @@ pub enum Error {
#[error("git: {0}")] #[error("git: {0}")]
Git(#[from] git2::Error), Git(#[from] git2::Error),
#[error("id: {0}")] #[error("id: {0}")]
ProjId(#[from] IdError), ProjId(#[from] ProjIdError),
#[error("i/o: {0}")] #[error("i/o: {0}")]
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("doc: {0}")] #[error("doc: {0}")]
@ -101,6 +101,11 @@ pub trait ReadStorage {
pub trait WriteStorage { pub trait WriteStorage {
fn repository(&mut self) -> &mut git2::Repository; fn repository(&mut self) -> &mut git2::Repository;
fn namespace(
&mut self,
proj: &ProjId,
user: &UserId,
) -> Result<&mut git2::Repository, git2::Error>;
} }
impl<T, S> ReadStorage for T impl<T, S> ReadStorage for T
@ -125,6 +130,14 @@ where
fn repository(&mut self) -> &mut git2::Repository { fn repository(&mut self) -> &mut git2::Repository {
self.deref_mut().repository() self.deref_mut().repository()
} }
fn namespace(
&mut self,
proj: &ProjId,
user: &UserId,
) -> Result<&mut git2::Repository, git2::Error> {
self.deref_mut().namespace(proj, user)
}
} }
pub struct Storage { pub struct Storage {
@ -167,6 +180,19 @@ impl WriteStorage for Storage {
fn repository(&mut self) -> &mut git2::Repository { fn repository(&mut self) -> &mut git2::Repository {
&mut self.backend &mut self.backend
} }
fn namespace(
&mut self,
proj: &ProjId,
user: &UserId,
) -> Result<&mut git2::Repository, git2::Error> {
let path = self.backend.path();
self.backend = git2::Repository::open_bare(path)?;
self.backend.set_namespace(&format!("{}/{}", proj, user))?;
Ok(&mut self.backend)
}
} }
impl Storage { impl Storage {

View File

@ -1,5 +1,6 @@
pub(crate) mod arbitrary; pub(crate) mod arbitrary;
pub(crate) mod assert; pub(crate) mod assert;
pub(crate) mod fixtures;
pub(crate) mod logger; pub(crate) mod logger;
pub(crate) mod peer; pub(crate) mod peer;
pub(crate) mod storage; pub(crate) mod storage;

View File

@ -49,12 +49,8 @@ impl quickcheck::Arbitrary for ProjId {
impl quickcheck::Arbitrary for hash::Digest { impl quickcheck::Arbitrary for hash::Digest {
fn arbitrary(g: &mut quickcheck::Gen) -> Self { fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut bytes: [u8; 32] = [0; 32]; let bytes: Vec<u8> = quickcheck::Arbitrary::arbitrary(g);
hash::Digest::new(&bytes)
for byte in &mut bytes {
*byte = u8::arbitrary(g);
}
hash::Digest::from(bytes)
} }
} }

View File

@ -41,4 +41,12 @@ impl WriteStorage for MockStorage {
fn repository(&mut self) -> &mut git2::Repository { fn repository(&mut self) -> &mut git2::Repository {
todo!() todo!()
} }
fn namespace(
&mut self,
_proj: &ProjId,
_user: &crate::identity::UserId,
) -> Result<&mut git2::Repository, git2::Error> {
todo!()
}
} }

View File

@ -101,6 +101,22 @@ fn test_wrong_peer_magic() {
// TODO // TODO
} }
#[test]
fn test_inventory_fetch() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
alice.connect_to(&bob.addr());
alice.receive(
&bob.addr(),
Message::Inventory {
seq: 1,
inv: vec![],
origin: None,
},
);
}
#[test] #[test]
fn test_inventory_relay_bad_seq() { fn test_inventory_relay_bad_seq() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty()); let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());