From bc226a9c38e117e2c2ec5ca2776499087d86961c Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Mon, 19 Sep 2022 22:08:52 +0200 Subject: [PATCH] node: Function to load quorum identity Signed-off-by: Alexis Sellier --- node/src/identity/doc.rs | 43 +++++++++++++++++++++++++--------------- node/src/storage/git.rs | 37 ++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/node/src/identity/doc.rs b/node/src/identity/doc.rs index 75073919..b82043f4 100644 --- a/node/src/identity/doc.rs +++ b/node/src/identity/doc.rs @@ -368,8 +368,8 @@ pub enum IdentityError { Git(#[from] git::Error), #[error("verification: {0}")] Verification(#[from] VerificationError), - #[error("root hash `{0}` does not match repository")] - MismatchedRoot(Id), + #[error("root hash `{0}` does not match project")] + MismatchedRoot(Oid), #[error("commit signature for {0} is invalid: {1}")] InvalidSignature(PublicKey, crypto::Error), #[error("quorum not reached: {0} signatures for a threshold of {1}")] @@ -377,13 +377,13 @@ pub enum IdentityError { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Identity { +pub struct Identity { /// The head of the identity branch. This points to a commit that /// contains the current document blob. pub head: Oid, /// The canonical identifier for this identity. /// This is the object id of the initial document blob. - pub root: Id, + pub root: I, /// The object id of the current document blob. pub current: Oid, /// Revision number. The initial document has a revision of `0`. @@ -392,33 +392,43 @@ pub struct Identity { pub doc: Doc, /// Signatures over this identity. pub signatures: HashMap, +} - verified: PhantomData, +impl Identity { + pub fn verified(self, id: Id) -> Result, IdentityError> { + // The root hash must be equal to the id. + if self.root != *id { + return Err(IdentityError::MismatchedRoot(self.root)); + } + + Ok(Identity { + root: id, + head: self.head, + current: self.current, + revision: self.revision, + doc: self.doc, + signatures: self.signatures, + }) + } } impl Identity { pub fn load<'r, R: ReadRepository<'r>>( - id: &Id, remote: &RemoteId, repo: &R, - ) -> Result>, IdentityError> { + ) -> Result>, IdentityError> { if let Some(head) = Doc::::head(remote, repo)? { let mut history = repo.revwalk(head)?.collect::>(); // Retrieve root document. let root_oid = history.pop().unwrap()?.into(); let root_blob = Doc::blob_at(root_oid, repo)?.unwrap(); - let root = Id::from(root_blob.id()); + let root: git::Oid = root_blob.id().into(); let trusted = Doc::from_json(root_blob.content()).unwrap(); let revision = history.len() as u32; - // The root hash must be equal to the id. - if root != *id { - return Err(IdentityError::MismatchedRoot(root)); - } - let mut trusted = trusted.verified()?; - let mut current = *root; + let mut current = root; let mut signatures = Vec::new(); // Traverse the history chronologically. @@ -458,7 +468,6 @@ impl Identity { revision, doc: trusted, signatures: signatures.into_iter().collect(), - verified: PhantomData, })); } Ok(None) @@ -558,8 +567,10 @@ mod test { }) .unwrap(); - let identity = Identity::load(&id, alice.public_key(), &repo) + let identity: Identity = Identity::load(alice.public_key(), &repo) .unwrap() + .unwrap() + .verified(id.clone()) .unwrap(); assert_eq!(identity.signatures.len(), 2); diff --git a/node/src/storage/git.rs b/node/src/storage/git.rs index 6c724ac8..c448a250 100644 --- a/node/src/storage/git.rs +++ b/node/src/storage/git.rs @@ -7,9 +7,9 @@ use once_cell::sync::Lazy; pub use radicle_git_ext::Oid; -use crate::crypto::{Signer, Verified}; +use crate::crypto::{Signer, Unverified, Verified}; use crate::git; -use crate::identity; +use crate::identity::{self, Doc}; use crate::identity::{Id, Project}; use crate::storage::refs; use crate::storage::refs::{Refs, SignedRefs}; @@ -50,7 +50,7 @@ impl ReadStorage for Storage { // Perhaps for checking we could have a `contains` method? let repo = self.repository(proj)?; - if let Some(doc) = repo.identity(remote)? { + if let Some(doc) = repo.identity_of(remote)? { let remotes = repo.remotes()?.collect::>()?; let path = repo.path().to_path_buf(); @@ -252,7 +252,7 @@ impl Repository { Ok(()) } - pub fn identity( + pub fn identity_of( &self, remote: &RemoteId, ) -> Result>, refs::Error> { @@ -262,6 +262,35 @@ impl Repository { Ok(None) } } + + pub fn identity(&self) -> Result, refs::Error> { + let mut heads = Vec::new(); + for remote in self.remote_ids()? { + let remote = remote?; + let oid = Doc::::head(&remote, self)?.unwrap(); + + heads.push(*oid); + } + let oid = self.raw().merge_base_many(&heads)?; + let (doc, _) = Doc::load_at(oid.into(), self)?.ok_or(refs::Error::NotFound)?; + + Ok(doc) + } + + pub fn remote_ids( + &self, + ) -> Result> + '_, git2::Error> { + let iter = self.backend.references_glob(SIGNATURES_GLOB.as_str())?.map( + |reference| -> Result { + let r = reference?; + let name = r.name().ok_or(refs::Error::InvalidRef)?; + let (id, _) = git::parse_ref::(name)?; + + Ok(id) + }, + ); + Ok(iter) + } } impl<'r> ReadRepository<'r> for Repository {