Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: Parametrize `Change` with single signature
Alexis Sellier committed 3 years ago
commit 1a5b68c2b3d47928cf1331e09b8e2a4546c95a69
parent 2230c8ddc4f91b7b16810dc164b1c2b89f871754
6 files changed +41 -14
modified radicle-cob/src/backend/git/change.rs
@@ -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,
modified radicle-cob/src/change.rs
@@ -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>;
modified radicle-cob/src/change/store.rs
@@ -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.
modified radicle-cob/src/change_graph.rs
@@ -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();
modified radicle-cob/src/lib.rs
@@ -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,
        >,
{
}
modified radicle-cob/src/signatures.rs
@@ -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)