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:
parent
b030e9ea80
commit
a32101ab75
|
|
@ -18,6 +18,7 @@ use crate::crypto;
|
||||||
use crate::crypto::{Signature, Unverified, Verified};
|
use crate::crypto::{Signature, Unverified, Verified};
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity::{project::Project, Did};
|
use crate::identity::{project::Project, Did};
|
||||||
|
use crate::storage;
|
||||||
use crate::storage::git::trailers;
|
use crate::storage::git::trailers;
|
||||||
use crate::storage::{ReadRepository, RemoteId};
|
use crate::storage::{ReadRepository, RemoteId};
|
||||||
|
|
||||||
|
|
@ -142,6 +143,13 @@ pub struct Doc<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> 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> {
|
pub fn head<R: ReadRepository>(remote: &RemoteId, repo: &R) -> Result<Oid, DocError> {
|
||||||
repo.reference_oid(remote, &git::refs::storage::IDENTITY_BRANCH)
|
repo.reference_oid(remote, &git::refs::storage::IDENTITY_BRANCH)
|
||||||
.map_err(DocError::from)
|
.map_err(DocError::from)
|
||||||
|
|
@ -215,6 +223,11 @@ impl Doc<Verified> {
|
||||||
Ok((oid, sig))
|
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> {
|
pub fn load_at<R: ReadRepository>(oid: Oid, repo: &R) -> Result<DocAt, DocError> {
|
||||||
let blob = Self::blob_at(oid, repo)?;
|
let blob = Self::blob_at(oid, repo)?;
|
||||||
let doc = Doc::from_json(blob.content())?.verified()?;
|
let doc = Doc::from_json(blob.content())?.verified()?;
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,17 @@ pub trait ReadRepository {
|
||||||
/// Returns the [`Oid`] as well as the qualified reference name.
|
/// Returns the [`Oid`] as well as the qualified reference name.
|
||||||
fn canonical_head(&self) -> Result<(Qualified, Oid), IdentityError>;
|
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`.
|
/// Get the `reference` for the given `remote`.
|
||||||
///
|
///
|
||||||
/// Returns `None` is the reference did not exist.
|
/// Returns `None` is the reference did not exist.
|
||||||
|
|
@ -403,6 +414,8 @@ pub trait WriteRepository: ReadRepository {
|
||||||
/// Set the repository head to the canonical branch.
|
/// Set the repository head to the canonical branch.
|
||||||
/// This computes the head based on the delegate set.
|
/// This computes the head based on the delegate set.
|
||||||
fn set_head(&self) -> Result<Oid, IdentityError>;
|
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.
|
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
|
||||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
|
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
|
||||||
/// Get the underlying git repository.
|
/// Get the underlying git repository.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,13 @@ pub static NAMESPACES_GLOB: Lazy<refspec::PatternString> =
|
||||||
Lazy::new(|| refspec::pattern!("refs/namespaces/*"));
|
Lazy::new(|| refspec::pattern!("refs/namespaces/*"));
|
||||||
pub static SIGREFS_GLOB: Lazy<refspec::PatternString> =
|
pub static SIGREFS_GLOB: Lazy<refspec::PatternString> =
|
||||||
Lazy::new(|| refspec::pattern!("refs/namespaces/*/rad/sigrefs"));
|
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.
|
/// A parsed Git reference.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -584,6 +591,18 @@ impl ReadRepository for Repository {
|
||||||
|
|
||||||
Ok((branch_ref, oid.into()))
|
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 {
|
impl WriteRepository for Repository {
|
||||||
|
|
@ -602,6 +621,20 @@ impl WriteRepository for Repository {
|
||||||
Ok(head)
|
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> {
|
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error> {
|
||||||
let remote = signer.public_key();
|
let remote = signer.public_key();
|
||||||
let refs = self.references_of(remote)?;
|
let refs = self.references_of(remote)?;
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,14 @@ impl ReadRepository for MockRepository {
|
||||||
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
|
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
|
||||||
Ok((git2::Oid::zero().into(), self.doc.clone().unverified()))
|
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 {
|
impl WriteRepository for MockRepository {
|
||||||
|
|
@ -211,4 +219,8 @@ impl WriteRepository for MockRepository {
|
||||||
) -> Result<crate::storage::refs::SignedRefs<Verified>, Error> {
|
) -> Result<crate::storage::refs::SignedRefs<Verified>, Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_identity_head(&self) -> Result<Oid, IdentityError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue