node: Fix encoding of types, add fixtures
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
1ff8f625ff
commit
32591e6aa4
|
|
@ -1,4 +1,5 @@
|
|||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{
|
||||
|
|
@ -8,11 +9,11 @@ use sha2::{
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
pub enum ParseError {
|
||||
#[error("invalid string length")]
|
||||
InvalidLength,
|
||||
pub enum DecodeError {
|
||||
#[error(transparent)]
|
||||
ParseInt(#[from] std::num::ParseIntError),
|
||||
Multibase(#[from] multibase::Error),
|
||||
#[error("invalid digest length {0}")]
|
||||
InvalidLength(usize),
|
||||
}
|
||||
|
||||
/// A SHA-256 hash.
|
||||
|
|
@ -25,23 +26,24 @@ impl Digest {
|
|||
}
|
||||
|
||||
pub fn encode(&self) -> String {
|
||||
self.to_string()
|
||||
multibase::encode(multibase::Base::Base58Btc, &self.0)
|
||||
}
|
||||
|
||||
pub fn decode(s: &str) -> Result<Self, ParseError> {
|
||||
if s.len() != 64 {
|
||||
Err(ParseError::InvalidLength)
|
||||
} else {
|
||||
let mut bytes: [u8; 32] = Default::default();
|
||||
for (i, byte) in (0..s.len())
|
||||
.step_by(2)
|
||||
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
|
||||
.enumerate()
|
||||
{
|
||||
bytes[i] = byte?;
|
||||
}
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
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 {
|
||||
type Err = DecodeError;
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ use thiserror::Error;
|
|||
|
||||
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)]
|
||||
pub struct ProjId(hash::Digest);
|
||||
|
||||
|
|
@ -28,18 +36,20 @@ impl ProjId {
|
|||
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) {
|
||||
let mut array: [u8; 32] = [0; 32];
|
||||
let bytes = bs58::decode(s).into(&mut array)?;
|
||||
|
||||
// TODO: Multi-hash?
|
||||
|
||||
assert_eq!(bytes, array.len());
|
||||
|
||||
return Ok(Self(hash::Digest::from(array)));
|
||||
let id = Self::from_str(s)?;
|
||||
return Ok(id);
|
||||
}
|
||||
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 {
|
||||
type Err = IdError;
|
||||
type Err = UserIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut array: [u8; 32] = [0; 32];
|
||||
let bytes = bs58::decode(s).into(&mut array)?;
|
||||
let (_, bytes) = multibase::decode(s)?;
|
||||
let array: [u8; 32] = bytes
|
||||
.try_into()
|
||||
.map_err(|v: Vec<u8>| UserIdError::InvalidLength(v.len()))?;
|
||||
let key = VerificationKey::try_from(VerificationKeyBytes::from(array))?;
|
||||
|
||||
assert_eq!(bytes, array.len());
|
||||
|
||||
Ok(Self(key))
|
||||
}
|
||||
}
|
||||
|
|
@ -115,11 +125,11 @@ impl Deref for UserId {
|
|||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum IdError {
|
||||
#[error("invalid ref '{0}'")]
|
||||
InvalidRef(String),
|
||||
#[error("invalid base58 string: {0}")]
|
||||
Base58(#[from] bs58::decode::Error),
|
||||
pub enum UserIdError {
|
||||
#[error("invalid length {0}")]
|
||||
InvalidLength(usize),
|
||||
#[error("invalid multibase string: {0}")]
|
||||
Multibase(#[from] multibase::Error),
|
||||
#[error("invalid key: {0}")]
|
||||
InvalidKey(#[from] ed25519_consensus::Error),
|
||||
}
|
||||
|
|
@ -178,4 +188,12 @@ mod test {
|
|||
assert!(!hm.insert(a));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub use radicle_git_ext::Oid;
|
|||
|
||||
use crate::collections::HashMap;
|
||||
use crate::identity;
|
||||
use crate::identity::{IdError, ProjId, UserId};
|
||||
use crate::identity::{ProjId, ProjIdError, UserId};
|
||||
|
||||
pub static RAD_ID_GLOB: Lazy<refspec::PatternString> =
|
||||
Lazy::new(|| refspec::pattern!("refs/namespaces/*/refs/rad/id"));
|
||||
|
|
@ -30,7 +30,7 @@ pub enum Error {
|
|||
#[error("git: {0}")]
|
||||
Git(#[from] git2::Error),
|
||||
#[error("id: {0}")]
|
||||
ProjId(#[from] IdError),
|
||||
ProjId(#[from] ProjIdError),
|
||||
#[error("i/o: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("doc: {0}")]
|
||||
|
|
@ -101,6 +101,11 @@ pub trait ReadStorage {
|
|||
|
||||
pub trait WriteStorage {
|
||||
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
|
||||
|
|
@ -125,6 +130,14 @@ where
|
|||
fn repository(&mut self) -> &mut git2::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 {
|
||||
|
|
@ -167,6 +180,19 @@ impl WriteStorage for Storage {
|
|||
fn repository(&mut self) -> &mut git2::Repository {
|
||||
&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 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub(crate) mod arbitrary;
|
||||
pub(crate) mod assert;
|
||||
pub(crate) mod fixtures;
|
||||
pub(crate) mod logger;
|
||||
pub(crate) mod peer;
|
||||
pub(crate) mod storage;
|
||||
|
|
|
|||
|
|
@ -49,12 +49,8 @@ impl quickcheck::Arbitrary for ProjId {
|
|||
|
||||
impl quickcheck::Arbitrary for hash::Digest {
|
||||
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
|
||||
let mut bytes: [u8; 32] = [0; 32];
|
||||
|
||||
for byte in &mut bytes {
|
||||
*byte = u8::arbitrary(g);
|
||||
}
|
||||
hash::Digest::from(bytes)
|
||||
let bytes: Vec<u8> = quickcheck::Arbitrary::arbitrary(g);
|
||||
hash::Digest::new(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,4 +41,12 @@ impl WriteStorage for MockStorage {
|
|||
fn repository(&mut self) -> &mut git2::Repository {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn namespace(
|
||||
&mut self,
|
||||
_proj: &ProjId,
|
||||
_user: &crate::identity::UserId,
|
||||
) -> Result<&mut git2::Repository, git2::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,22 @@ fn test_wrong_peer_magic() {
|
|||
// 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]
|
||||
fn test_inventory_relay_bad_seq() {
|
||||
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
|
||||
|
|
|
|||
Loading…
Reference in New Issue