node: Fix encoding of identifiers

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-09-03 23:27:50 +02:00
parent c3b7a6fbd7
commit 078184b7d6
No known key found for this signature in database
5 changed files with 109 additions and 18 deletions

View File

@ -50,7 +50,7 @@ where
/// The public/verification key. /// The public/verification key.
#[derive(Serialize, Deserialize, Eq, Debug, Copy, Clone)] #[derive(Serialize, Deserialize, Eq, Debug, Copy, Clone)]
#[serde(transparent)] #[serde(into = "String", try_from = "String")]
pub struct PublicKey(pub ed25519::VerificationKey); pub struct PublicKey(pub ed25519::VerificationKey);
/// The private/signing key. /// The private/signing key.
@ -78,6 +78,12 @@ impl fmt::Display for PublicKey {
} }
} }
impl From<PublicKey> for String {
fn from(other: PublicKey) -> Self {
other.encode()
}
}
impl PartialEq for PublicKey { impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0 == other.0 self.0 == other.0
@ -110,6 +116,14 @@ impl FromStr for PublicKey {
} }
} }
impl TryFrom<String> for PublicKey {
type Error = PublicKeyError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_str(&value)
}
}
impl Deref for PublicKey { impl Deref for PublicKey {
type Target = ed25519::VerificationKey; type Target = ed25519::VerificationKey;

View File

@ -9,6 +9,7 @@ use thiserror::Error;
use crate::crypto::{self, Verified}; use crate::crypto::{self, Verified};
use crate::hash; use crate::hash;
use crate::serde_ext;
use crate::storage::Remotes; use crate::storage::Remotes;
pub static IDENTITY_PATH: Lazy<&Path> = Lazy::new(|| Path::new("Radicle.toml")); pub static IDENTITY_PATH: Lazy<&Path> = Lazy::new(|| Path::new("Radicle.toml"));
@ -22,7 +23,7 @@ pub enum ProjIdError {
InvalidDigest(#[from] hash::DecodeError), InvalidDigest(#[from] hash::DecodeError),
} }
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ProjId(hash::Digest); pub struct ProjId(hash::Digest);
impl fmt::Display for ProjId { impl fmt::Display for ProjId {
@ -66,13 +67,50 @@ impl From<hash::Digest> for ProjId {
} }
} }
impl serde::Serialize for ProjId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_ext::string::serialize(self, serializer)
}
}
impl<'de> serde::Deserialize<'de> for ProjId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
serde_ext::string::deserialize(deserializer)
}
}
#[derive(Error, Debug)]
pub enum DidError {
#[error("invalid did: {0}")]
Did(String),
#[error("invalid public key: {0}")]
PublicKey(#[from] crypto::PublicKeyError),
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Clone)]
#[serde(into = "String", try_from = "String")]
pub struct Did(crypto::PublicKey); 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.encode())
} }
pub fn decode(input: &str) -> Result<Self, DidError> {
let key = input
.strip_prefix("did:key:")
.ok_or_else(|| DidError::Did(input.to_owned()))?;
crypto::PublicKey::from_str(key)
.map(Did)
.map_err(DidError::from)
}
} }
impl From<crypto::PublicKey> for Did { impl From<crypto::PublicKey> for Did {
@ -81,6 +119,20 @@ impl From<crypto::PublicKey> for Did {
} }
} }
impl From<Did> for String {
fn from(other: Did) -> Self {
other.encode()
}
}
impl TryFrom<String> for Did {
type Error = DidError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::decode(&value)
}
}
impl fmt::Display for Did { impl fmt::Display for Did {
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.encode())
@ -163,4 +215,16 @@ mod test {
assert_eq!(input, decoded); assert_eq!(input, decoded);
} }
#[quickcheck]
fn prop_json_eq_str(user: UserId, proj: ProjId, did: Did) {
let json = serde_json::to_string(&user).unwrap();
assert_eq!(format!("\"{}\"", user), json);
let json = serde_json::to_string(&proj).unwrap();
assert_eq!(format!("\"{}\"", proj), json);
let json = serde_json::to_string(&did).unwrap();
assert_eq!(format!("\"{}\"", did), json);
}
} }

View File

@ -16,6 +16,7 @@ mod identity;
mod logger; mod logger;
mod protocol; mod protocol;
mod rad; mod rad;
mod serde_ext;
mod storage; mod storage;
#[cfg(test)] #[cfg(test)]
mod test; mod test;

25
node/src/serde_ext.rs Normal file
View File

@ -0,0 +1,25 @@
pub mod string {
use std::fmt::Display;
use std::str::FromStr;
use serde::{de, Deserialize, Deserializer, Serializer};
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: Display,
S: Serializer,
{
serializer.collect_str(value)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: FromStr,
T::Err: Display,
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(de::Error::custom)
}
}

View File

@ -92,24 +92,11 @@ impl<V> IntoIterator for Remotes<V> {
} }
} }
#[allow(clippy::from_over_into)] impl<V> From<Remotes<V>> for HashMap<RemoteId, Refs> {
impl Into<HashMap<String, Remote<Unverified>>> for Remotes<Unverified> { fn from(other: Remotes<V>) -> Self {
fn into(self) -> HashMap<String, Remote<Unverified>> {
let mut remotes = HashMap::with_hasher(fastrand::Rng::new().into()); let mut remotes = HashMap::with_hasher(fastrand::Rng::new().into());
for (k, v) in self.0 { for (k, v) in other.into_iter() {
remotes.insert(k.to_string(), v);
}
remotes
}
}
#[allow(clippy::from_over_into)]
impl<V> Into<HashMap<RemoteId, Refs>> for Remotes<V> {
fn into(self) -> HashMap<RemoteId, Refs> {
let mut remotes = HashMap::with_hasher(fastrand::Rng::new().into());
for (k, v) in self.into_iter() {
remotes.insert(k, v.refs.into()); remotes.insert(k, v.refs.into());
} }
remotes remotes