diff --git a/Cargo.toml b/Cargo.toml index e1ac56e..d857de1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multicid" -version = "1.0.5" +version = "1.1.0" edition = "2021" authors = ["Dave Grantham "] description = "Multicodec compatible content identifier implementation" @@ -13,13 +13,7 @@ default = ["serde"] dag_cbor = ["serde_cbor", "serde_cbor/tags"] [dependencies] -multibase = { version = "1.0", git = "https://github.com/cryptidtech/rust-multibase.git" } -multicodec = { version = "1.0", git = "https://github.com/cryptidtech/rust-multicodec.git" } -multihash = { version = "1.0", git = "https://github.com/cryptidtech/multihash.git" } -multikey = { version = "1.0", git = "https://github.com/DougAnderson444/multikey.git" } -multisig = { version = "1.0", git = "https://github.com/DougAnderson444/multisig.git" } -multitrait = { version = "1.0", git = "https://github.com/cryptidtech/multitrait.git" } -multiutil = { version = "1.0", git = "https://github.com/cryptidtech/multiutil.git" } +multikey = { version = "1.0", git = "https://github.com/DougAnderson444/multikey.git", branch = "deps" } rand = "0.8" serde = { version = "1.0", default-features = false, features = [ "alloc", diff --git a/src/cid.rs b/src/cid.rs index 5d878c5..7d7f188 100644 --- a/src/cid.rs +++ b/src/cid.rs @@ -1,4 +1,5 @@ // SPDX-License-Idnetifier: Apache-2.0 +use super::*; use crate::{error::CidError, Error}; use core::fmt; use multibase::Base; @@ -18,7 +19,7 @@ pub type LegacyEncodedCid = BaseEncoded; pub type EncodedCid = BaseEncoded; /// implementation of cid -#[derive(Clone, Eq, Ord, PartialOrd, PartialEq)] +#[derive(Clone, Eq, Ord, PartialOrd, PartialEq, Hash)] pub struct Cid { /// the version of the Cid pub(crate) codec: Codec, @@ -156,6 +157,13 @@ impl fmt::Debug for Cid { } } +impl fmt::Display for Cid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let encoded = EncodedCid::new(self.encoding(), self.clone()); + write!(f, "{}", encoded) + } +} + /// Hash builder that takes the codec and the data and produces a Multihash #[derive(Clone, Debug, Default)] pub struct Builder { @@ -210,8 +218,7 @@ impl Builder { return Err(CidError::LegacyCid.into()); } Ok(EncodedCid::new( - self.base_encoding - .unwrap_or_else(Cid::preferred_encoding), + self.base_encoding.unwrap_or_else(Cid::preferred_encoding), self.try_build()?, )) } @@ -275,17 +282,23 @@ mod tests { assert_eq!(Codec::Sha2256, v0_1.hash.codec()); // this does not assume a multibase encoded CID - let v0_2 = EncodedCid::try_from("bafybeihcrr5owouhnms63areolshu2lp4jjbjqlhf4exegk7tnso5ja6py").unwrap(); + let v0_2 = + EncodedCid::try_from("bafybeihcrr5owouhnms63areolshu2lp4jjbjqlhf4exegk7tnso5ja6py") + .unwrap(); assert_eq!(Codec::Cidv1, v0_2.codec()); assert_eq!(Codec::DagPb, v0_2.target_codec); assert_eq!(Codec::Sha2256, v0_2.hash.codec()); - let v0_3 = EncodedCid::try_from("f01701220e28c7aeb3a876b25ed822472e47a696fe25214c1672f0972195f9b64eea41e7e").unwrap(); + let v0_3 = EncodedCid::try_from( + "f01701220e28c7aeb3a876b25ed822472e47a696fe25214c1672f0972195f9b64eea41e7e", + ) + .unwrap(); assert_eq!(Codec::Cidv1, v0_3.codec()); assert_eq!(Codec::DagPb, v0_3.target_codec); assert_eq!(Codec::Sha2256, v0_3.hash.codec()); - let v0_4 = EncodedCid::try_from("uAXASIOKMeus6h2sl7YIkcuR6aW_iUhTBZy8Jchlfm2TupB5-").unwrap(); + let v0_4 = + EncodedCid::try_from("uAXASIOKMeus6h2sl7YIkcuR6aW_iUhTBZy8Jchlfm2TupB5-").unwrap(); assert_eq!(Codec::Cidv1, v0_4.codec()); assert_eq!(Codec::DagPb, v0_4.target_codec); assert_eq!(Codec::Sha2256, v0_4.hash.codec()); @@ -392,4 +405,20 @@ mod tests { assert!(cid1 != cid2); assert!(!cid2.is_null()); } + + #[test] + fn test_string_roundtrip() { + let v1 = Builder::new(Codec::Cidv1) + .with_target_codec(Codec::DagCbor) + .with_hash( + &mh::Builder::new_from_bytes(Codec::Sha3512, b"for great justice, move every zig!") + .unwrap() + .try_build() + .unwrap(), + ) + .try_build() + .unwrap(); + let s = v1.to_string(); + assert_eq!(s, EncodedCid::try_from(s.as_str()).unwrap().to_string()); + } } diff --git a/src/error.rs b/src/error.rs index bc85dc2..51a6fd8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ // SPDX-License-Idnetifier: Apache-2.0 +use super::*; /// Errors created by this library #[derive(Clone, Debug, thiserror::Error)] diff --git a/src/lib.rs b/src/lib.rs index 717d477..ea8153b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,3 +33,8 @@ pub mod prelude { pub use multicodec::Codec; pub use multiutil::BaseEncoded; } + +// Re-export all multi* crates to avoid dependency conflicts +pub use multikey; +pub use multikey::multicrates; +pub use multikey::multicrates::*; diff --git a/src/serde/de.rs b/src/serde/de.rs index 311c912..e389975 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -1,11 +1,13 @@ // SPDX-License-Idnetifier: Apache-2.0 +use super::{multicodec, multihash}; use crate::{vlad, Cid, Vlad}; + use core::fmt; use multicodec::Codec; use multihash::Multihash; -use multikey::Nonce; #[cfg(feature = "dag_cbor")] -use multitrait::TryDecodeFrom; +use multikey::multicrates::multitrait::TryDecodeFrom; +use multikey::Nonce; use serde::{ de::{Error, MapAccess, Visitor}, Deserialize, Deserializer, diff --git a/src/serde/mod.rs b/src/serde/mod.rs index 967bc19..3e15d71 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -3,9 +3,15 @@ mod de; mod ser; +use super::*; + #[cfg(test)] mod tests { - use crate::{cid, vlad}; + use super::*; + use crate::{ + cid::{self, Cid, EncodedCid}, + vlad::{self, EncodedVlad, Vlad}, + }; use multicodec::Codec; use multihash::mh; use multikey::nonce; @@ -261,7 +267,7 @@ mod tests { let vlad = vlad::Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build_encoded() + .try_build_encoded(|cid| Ok(cid.clone().into())) .unwrap(); assert_tokens( @@ -292,7 +298,7 @@ mod tests { let vlad = vlad::Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| Ok(cid.clone().into())) .unwrap(); assert_tokens( @@ -355,7 +361,7 @@ mod tests { let vlad = vlad::Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| Ok(cid.clone().into())) .unwrap(); let s = serde_json::to_string(&vlad).unwrap(); @@ -385,7 +391,7 @@ mod tests { let vlad = vlad::Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| Ok(cid.clone().into())) .unwrap(); let v = serde_cbor::to_vec(&vlad).unwrap(); @@ -416,7 +422,7 @@ mod tests { let vlad = vlad::Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| Ok(cid.clone().into())) .unwrap(); let v = serde_cbor::to_vec(&vlad).unwrap(); @@ -427,13 +433,13 @@ mod tests { #[test] fn test_null_cid_serde_compact() { - let c = cid::Cid::null(); + let c = Cid::null(); assert_tokens(&c.compact(), &[Token::BorrowedBytes(&[1, 0, 0, 0])]); } #[test] fn test_null_cid_serde_readable() { - let c = cid::Cid::null(); + let c = Cid::null(); assert_tokens( &c.readable(), &[ @@ -462,13 +468,13 @@ mod tests { #[test] fn test_encoded_null_cid_serde_readable() { - let c: cid::EncodedCid = cid::Cid::null().into(); + let c: EncodedCid = Cid::null().into(); assert_tokens(&c.readable(), &[Token::BorrowedStr("z2UzHM")]); } #[test] fn test_null_vlad_serde_compact() { - let v = vlad::Vlad::null(); + let v = Vlad::null(); assert_tokens( &v.compact(), &[Token::BorrowedBytes(&[135, 36, 187, 36, 0, 1, 0, 0, 0])], @@ -477,7 +483,7 @@ mod tests { #[test] fn test_null_vlad_serde_readable() { - let v = vlad::Vlad::null(); + let v = Vlad::null(); assert_tokens( &v.readable(), &[ @@ -520,7 +526,7 @@ mod tests { #[test] fn test_encoded_null_vlad_serde_readable() { - let v: vlad::EncodedVlad = vlad::Vlad::null().into(); + let v: EncodedVlad = Vlad::null().into(); assert_tokens(&v.readable(), &[Token::BorrowedStr("bq4slwjaaaeaaaaa")]); } } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 39178d1..f8d9b86 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -17,7 +17,7 @@ impl ser::Serialize for Cid { } else { #[cfg(feature = "dag_cbor")] { - use multicodec::Codec; + use multikey::multicrates::multicodec::Codec; // build the byte string for DAG-CBOR according to the spec // https://github.com/ipld/specs/blob/master/block-layer/codecs/dag-cbor.md#links diff --git a/src/vlad.rs b/src/vlad.rs index 5342248..a5f1500 100644 --- a/src/vlad.rs +++ b/src/vlad.rs @@ -1,5 +1,7 @@ // SPDX-License-Idnetifier: Apache-2.0 +use super::*; use crate::{error::VladError, Cid, Error}; + use core::fmt; use multibase::Base; use multicodec::Codec; @@ -52,6 +54,13 @@ impl Vlad { vv.verify(&ms, Some(&cidv))?; Ok(()) } + + /// Return the Vlad's [Cid]. + /// This is the content address of the verification function. + /// It should match the `vlad/cid` field in a Provenance Log's first Entry. + pub fn cid(&self) -> &Cid { + &self.cid + } } impl CodecInfo for Vlad { @@ -138,7 +147,6 @@ impl fmt::Debug for Vlad { #[derive(Clone, Debug, Default)] pub struct Builder { nonce: Option, - mk: Option, cid: Option, base_encoding: Option, } @@ -156,12 +164,6 @@ impl Builder { self } - /// set the signing key to generate a signature nonce - pub fn with_signing_key(mut self, mk: &Multikey) -> Self { - self.mk = Some(mk.clone()); - self - } - /// set the base encoding codec pub fn with_base_encoding(mut self, base: Base) -> Self { self.base_encoding = Some(base); @@ -169,32 +171,33 @@ impl Builder { } /// build a base encoded vlad - pub fn try_build_encoded(&self) -> Result { + pub fn try_build_encoded( + &self, + gen_proof: impl FnMut(&Cid) -> Result, Error>, + ) -> Result { Ok(EncodedVlad::new( self.base_encoding.unwrap_or_else(Vlad::preferred_encoding), - self.try_build()?, + self.try_build(gen_proof)?, )) } /// build the vlad - pub fn try_build(&self) -> Result { + pub fn try_build( + &self, + mut gen_proof: impl FnMut(&Cid) -> Result, Error>, + ) -> Result { let cid = self.cid.clone().ok_or(VladError::MissingCid)?; match &self.nonce { Some(nonce) => Ok(Vlad { nonce: nonce.clone(), cid, }), - None => match &self.mk { - Some(mk) => { - let sv = mk.sign_view()?; - let cidv: Vec = cid.clone().into(); - let ms = sv.sign(&cidv, false, None)?; - let msv: Vec = ms.clone().into(); - let nonce = nonce::Builder::new_from_bytes(&msv).try_build()?; - Ok(Vlad { nonce, cid }) - } - None => Err(VladError::MissingNonce.into()), - }, + None => { + let msv = gen_proof(&cid)?; + let nonce = nonce::Builder::new_from_bytes(&msv).try_build()?; + + Ok(Vlad { nonce, cid }) + } } } } @@ -230,7 +233,11 @@ mod tests { let vlad = Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| { + // sign those bytes + let v: Vec = cid.clone().into(); + Ok(v) + }) .unwrap(); assert_eq!(Codec::Vlad, vlad.codec()); @@ -259,7 +266,11 @@ mod tests { let vlad = Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build() + .try_build(|cid| { + // sign those bytes + let v: Vec = cid.clone().into(); + Ok(v) + }) .unwrap(); let v: Vec = vlad.clone().into(); @@ -290,7 +301,11 @@ mod tests { let vlad = Builder::default() .with_nonce(&nonce) .with_cid(&cid) - .try_build_encoded() + .try_build_encoded(|cid| { + // sign those bytes + let v: Vec = cid.clone().into(); + Ok(v) + }) .unwrap(); let s = vlad.to_string(); @@ -327,7 +342,11 @@ mod tests { .with_nonce(&nonce) .with_cid(&cid) .with_base_encoding(encoding) - .try_build_encoded() + .try_build_encoded(|cid| { + // sign those bytes + let v: Vec = cid.clone().into(); + Ok(v) + }) .unwrap(); let s = vlad.to_string(); @@ -385,10 +404,16 @@ mod tests { let mk = EncodedMultikey::try_from(s).unwrap(); let vlad = Builder::default() - .with_signing_key(&mk) .with_cid(&cid) .with_base_encoding(Base::Base32Z) - .try_build_encoded() + .try_build_encoded(|cid| { + // use mk to sign those cid bytes + let signing_view = mk.sign_view()?; + let cidv: Vec = cid.clone().into(); + let ms = signing_view.sign(&cidv, false, None)?; + let msv: Vec = ms.clone().into(); + Ok(msv) + }) .unwrap(); // make sure the signature checks out