node: Function to load quorum identity
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
2f07e39312
commit
bc226a9c38
|
|
@ -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<V> {
|
||||
pub struct Identity<I> {
|
||||
/// 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<V> {
|
|||
pub doc: Doc<Verified>,
|
||||
/// Signatures over this identity.
|
||||
pub signatures: HashMap<PublicKey, Signature>,
|
||||
}
|
||||
|
||||
verified: PhantomData<V>,
|
||||
impl Identity<Oid> {
|
||||
pub fn verified(self, id: Id) -> Result<Identity<Id>, 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<Untrusted> {
|
||||
pub fn load<'r, R: ReadRepository<'r>>(
|
||||
id: &Id,
|
||||
remote: &RemoteId,
|
||||
repo: &R,
|
||||
) -> Result<Option<Identity<Trusted>>, IdentityError> {
|
||||
) -> Result<Option<Identity<Oid>>, IdentityError> {
|
||||
if let Some(head) = Doc::<Untrusted>::head(remote, repo)? {
|
||||
let mut history = repo.revwalk(head)?.collect::<Vec<_>>();
|
||||
|
||||
// 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<Untrusted> {
|
|||
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<Id> = Identity::load(alice.public_key(), &repo)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.verified(id.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(identity.signatures.len(), 2);
|
||||
|
|
|
|||
|
|
@ -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::<Result<_, _>>()?;
|
||||
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<Option<identity::Doc<Verified>>, refs::Error> {
|
||||
|
|
@ -262,6 +262,35 @@ impl Repository {
|
|||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> Result<identity::Doc<Unverified>, refs::Error> {
|
||||
let mut heads = Vec::new();
|
||||
for remote in self.remote_ids()? {
|
||||
let remote = remote?;
|
||||
let oid = Doc::<Unverified>::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<impl Iterator<Item = Result<RemoteId, refs::Error>> + '_, git2::Error> {
|
||||
let iter = self.backend.references_glob(SIGNATURES_GLOB.as_str())?.map(
|
||||
|reference| -> Result<RemoteId, refs::Error> {
|
||||
let r = reference?;
|
||||
let name = r.name().ok_or(refs::Error::InvalidRef)?;
|
||||
let (id, _) = git::parse_ref::<RemoteId>(name)?;
|
||||
|
||||
Ok(id)
|
||||
},
|
||||
);
|
||||
Ok(iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ReadRepository<'r> for Repository {
|
||||
|
|
|
|||
Loading…
Reference in New Issue