cob: Parametrize `Change` with single signature

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-11-29 10:31:50 +01:00
parent 2230c8ddc4
commit 1a5b68c2b3
No known key found for this signature in database
6 changed files with 41 additions and 14 deletions

View File

@ -64,6 +64,10 @@ pub mod error {
NoChange(Oid),
#[error("the 'change' found at '{0}' was not a blob")]
ChangeNotBlob(Oid),
#[error("the 'change' found at '{0}' was not signed")]
ChangeNotSigned(Oid),
#[error("the 'change' found at '{0}' has more than one signature")]
TooManySignatures(Oid),
#[error(transparent)]
AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer),
#[error(transparent)]
@ -82,7 +86,7 @@ impl change::Storage for git2::Repository {
type ObjectId = Oid;
type Author = Oid;
type Resource = Oid;
type Signatures = Signatures;
type Signatures = Signature;
fn create<Signer>(
&self,
@ -127,7 +131,7 @@ impl change::Storage for git2::Repository {
Ok(Change {
id,
revision: revision.into(),
signatures: signature.into(),
signature,
author,
resource,
manifest,
@ -138,7 +142,15 @@ impl change::Storage for git2::Repository {
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
let commit = Commit::read(self, id.into())?;
let (author, resource) = parse_trailers(commit.trailers())?;
let signatures = Signatures::try_from(&commit)?;
let mut signatures = Signatures::try_from(&commit)?
.into_iter()
.collect::<Vec<_>>();
let Some(signature) = signatures.pop() else {
return Err(error::Load::ChangeNotSigned(id));
};
if !signatures.is_empty() {
return Err(error::Load::TooManySignatures(id));
}
let tree = self.find_tree(commit.tree())?;
let manifest = load_manifest(self, &tree)?;
@ -147,7 +159,7 @@ impl change::Storage for git2::Repository {
Ok(Change {
id,
revision: tree.id().into(),
signatures,
signature: signature.into(),
author,
resource,
manifest,

View File

@ -8,9 +8,9 @@ use git_ext::Oid;
pub mod store;
pub use store::{Create, Storage};
use crate::signatures::Signatures;
use crate::signatures::Signature;
/// A single change in the change graph. The layout of changes in the repository
/// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
/// under "Change Commits".
pub type Change = store::Change<Oid, Oid, Oid, Signatures>;
pub type Change = store::Change<Oid, Oid, Oid, Signature>;

View File

@ -54,14 +54,14 @@ pub struct Create<Id> {
}
#[derive(Clone, Debug)]
pub struct Change<Author, Resource, Id, Signatures> {
pub struct Change<Author, Resource, Id, Signature> {
/// The content address of the `Change` itself.
pub id: Id,
/// The content address of the tree of the `Change`.
pub revision: Id,
/// The cryptographic signatures and their public keys of the
/// The cryptographic signature(s) and their public keys of the
/// authors.
pub signatures: Signatures,
pub signature: Signature,
/// The author of this change. The `Author` is expected to be a
/// content address to look up the identity of the author.
pub author: Option<Author>,
@ -111,12 +111,21 @@ where
Id: AsRef<[u8]>,
{
pub fn valid_signatures(&self) -> bool {
self.signatures
self.signature
.iter()
.all(|(key, sig)| key.verify(self.revision.as_ref(), sig).is_ok())
}
}
impl<A, R, Id> Change<A, R, Id, signatures::Signature>
where
Id: AsRef<[u8]>,
{
pub fn valid_signatures(&self) -> bool {
self.signature.verify(self.revision.as_ref())
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Manifest {
/// The name given to the type of collaborative object.

View File

@ -15,7 +15,7 @@ use petgraph::{
};
use crate::{
change, object, signatures::Signatures, Change, CollaborativeObject, ObjectId, TypeName,
change, object, signatures::Signature, Change, CollaborativeObject, ObjectId, TypeName,
};
mod evaluation;
@ -37,7 +37,7 @@ impl ChangeGraph {
oid: &ObjectId,
) -> Option<ChangeGraph>
where
S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signatures>,
S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signature>,
{
log::info!("loading object '{}' '{}'", typename, oid);
let mut builder = GraphBuilder::default();

View File

@ -97,7 +97,7 @@ pub use history::{Contents, Entry, History, HistoryType};
mod pruning_fold;
pub mod signatures;
use signatures::Signatures;
use signatures::Signature;
pub mod type_name;
pub use type_name::TypeName;
@ -134,7 +134,7 @@ where
ObjectId = git_ext::Oid,
Author = git_ext::Oid,
Resource = git_ext::Oid,
Signatures = Signatures,
Signatures = Signature,
>,
{
}

View File

@ -24,6 +24,12 @@ pub struct Signature {
sig: crypto::Signature,
}
impl Signature {
pub fn verify(&self, payload: &[u8]) -> bool {
self.key.verify(payload, &self.sig).is_ok()
}
}
impl From<Signature> for ExtendedSignature {
fn from(sig: Signature) -> Self {
Self::new(sig.key, sig.sig)