radicle: canoncial refs/rad/id

Introduce a canonical reference for refs/rad/id to aid in discovering
information about a repository without knowing a peer -- e.g. if a
peer wants to clone a new repository it needs to find out which peers
are delegates for that project, but does not know what their NIDs are
up front.

The approach mirrors the canonical `head` for a repository and
introduces useful constructors for `Doc`/`DocAt`.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2023-03-23 14:02:54 +00:00
parent b030e9ea80
commit a32101ab75
No known key found for this signature in database
GPG Key ID: 2552FB6F64066CB7
4 changed files with 71 additions and 0 deletions

View File

@ -18,6 +18,7 @@ use crate::crypto;
use crate::crypto::{Signature, Unverified, Verified};
use crate::git;
use crate::identity::{project::Project, Did};
use crate::storage;
use crate::storage::git::trailers;
use crate::storage::{ReadRepository, RemoteId};
@ -142,6 +143,13 @@ pub struct Doc<V> {
}
impl<V> Doc<V> {
pub fn canonical_head(repo: &storage::git::Repository) -> Result<Oid, DocError> {
repo.backend
.refname_to_id(storage::git::CANONICAL_IDENTITY.as_str())
.map(Oid::from)
.map_err(DocError::from)
}
pub fn head<R: ReadRepository>(remote: &RemoteId, repo: &R) -> Result<Oid, DocError> {
repo.reference_oid(remote, &git::refs::storage::IDENTITY_BRANCH)
.map_err(DocError::from)
@ -215,6 +223,11 @@ impl Doc<Verified> {
Ok((oid, sig))
}
pub fn canonical(repo: &storage::git::Repository) -> Result<DocAt, DocError> {
let oid = Self::canonical_head(repo)?;
Self::load_at(oid, repo)
}
pub fn load_at<R: ReadRepository>(oid: Oid, repo: &R) -> Result<DocAt, DocError> {
let blob = Self::blob_at(oid, repo)?;
let doc = Doc::from_json(blob.content())?.verified()?;

View File

@ -353,6 +353,17 @@ pub trait ReadRepository {
/// Returns the [`Oid`] as well as the qualified reference name.
fn canonical_head(&self) -> Result<(Qualified, Oid), IdentityError>;
/// Get the head of the `rad/id` reference in this repository.
///
/// Returns the reference pointed to by `rad/id` if it is set. Otherwise, computes the canonical
/// `rad/id` using [`ReadRepository::canonical_identity_head`].
fn identity_head(&self) -> Result<Oid, IdentityError>;
/// Compute the canonical `rad/id` of this repository.
///
/// Ignores any existing `rad/id` reference.
fn canonical_identity_head(&self) -> Result<Oid, IdentityError>;
/// Get the `reference` for the given `remote`.
///
/// Returns `None` is the reference did not exist.
@ -403,6 +414,8 @@ pub trait WriteRepository: ReadRepository {
/// Set the repository head to the canonical branch.
/// This computes the head based on the delegate set.
fn set_head(&self) -> Result<Oid, IdentityError>;
/// Set the repository 'rad/id' to the canonical commit, agreed by quorum.
fn set_identity_head(&self) -> Result<Oid, IdentityError>;
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
/// Get the underlying git repository.

View File

@ -28,6 +28,13 @@ pub static NAMESPACES_GLOB: Lazy<refspec::PatternString> =
Lazy::new(|| refspec::pattern!("refs/namespaces/*"));
pub static SIGREFS_GLOB: Lazy<refspec::PatternString> =
Lazy::new(|| refspec::pattern!("refs/namespaces/*/rad/sigrefs"));
pub static CANONICAL_IDENTITY: Lazy<git::Qualified> = Lazy::new(|| {
git::Qualified::from_components(
git::name::component!("rad"),
git::name::component!("id"),
None,
)
});
/// A parsed Git reference.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -584,6 +591,18 @@ impl ReadRepository for Repository {
Ok((branch_ref, oid.into()))
}
fn identity_head(&self) -> Result<Oid, IdentityError> {
match Doc::<Verified>::canonical_head(self) {
Ok(oid) => Ok(oid),
Err(err) if err.is_not_found() => self.canonical_identity_head(),
Err(err) => Err(err.into()),
}
}
fn canonical_identity_head(&self) -> Result<Oid, IdentityError> {
Repository::identity_head(self)
}
}
impl WriteRepository for Repository {
@ -602,6 +621,20 @@ impl WriteRepository for Repository {
Ok(head)
}
fn set_identity_head(&self) -> Result<Oid, IdentityError> {
let head = self.canonical_identity_head()?;
log::debug!(target: "storage", "Setting ref: {} -> {}", *CANONICAL_IDENTITY, head);
self.raw().reference(
CANONICAL_IDENTITY.as_str(),
*head,
true,
"set-local-branch (radicle)",
)?;
Ok(head)
}
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error> {
let remote = signer.public_key();
let refs = self.references_of(remote)?;

View File

@ -194,6 +194,14 @@ impl ReadRepository for MockRepository {
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
Ok((git2::Oid::zero().into(), self.doc.clone().unverified()))
}
fn identity_head(&self) -> Result<Oid, IdentityError> {
todo!()
}
fn canonical_identity_head(&self) -> Result<Oid, IdentityError> {
todo!()
}
}
impl WriteRepository for MockRepository {
@ -211,4 +219,8 @@ impl WriteRepository for MockRepository {
) -> Result<crate::storage::refs::SignedRefs<Verified>, Error> {
todo!()
}
fn set_identity_head(&self) -> Result<Oid, IdentityError> {
todo!()
}
}