Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: Include commit timestamp in `Entry`
Alexis Sellier committed 3 years ago
commit d061afdab11ab6135c860f5288ad9d80faf1af17
parent 86beae2ebb25810b8005d9a111c45bd2113ab948
7 files changed +54 -14
modified radicle-cob/src/backend/git/change.rs
@@ -9,6 +9,7 @@ use git_commit::{self as commit, Commit};
use git_ext::Oid;
use git_trailers::OwnedTrailer;

+
use crate::history::entry::Timestamp;
use crate::{
    change::{self, store, Change},
    history::entry,
@@ -114,7 +115,7 @@ impl change::Storage for git2::Repository {
            Signature::from((*key, sig))
        };

-
        let id = write_commit(self, resource, tips, message, signature.clone(), tree)?;
+
        let (id, timestamp) = write_commit(self, resource, tips, message, signature.clone(), tree)?;
        Ok(Change {
            id,
            revision: revision.into(),
@@ -122,11 +123,13 @@ impl change::Storage for git2::Repository {
            resource,
            manifest,
            contents,
+
            timestamp,
        })
    }

    fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
        let commit = Commit::read(self, id.into())?;
+
        let timestamp = git2::Time::from(commit.committer().time).seconds() as u64;
        let resource = parse_resource_trailer(commit.trailers())?;
        let mut signatures = Signatures::try_from(&commit)?
            .into_iter()
@@ -149,6 +152,7 @@ impl change::Storage for git2::Repository {
            resource,
            manifest,
            contents,
+
            timestamp,
        })
    }
}
@@ -208,7 +212,7 @@ fn write_commit<O>(
    message: String,
    signature: Signature,
    tree: git2::Tree,
-
) -> Result<Oid, error::Create>
+
) -> Result<(Oid, Timestamp), error::Create>
where
    O: AsRef<git2::Oid>,
{
@@ -221,14 +225,14 @@ where

    {
        let author = repo.signature()?;
+
        let timestamp = author.when().seconds() as Timestamp;
        let mut headers = commit::Headers::new();
        headers.push(
            "gpgsig",
            &String::from_utf8(crypto::ssh::ExtendedSignature::from(signature).to_armored())?,
        );
        let author = commit::Author::try_from(&author)?;
-

-
        let commit = Commit::new(
+
        let oid = Commit::new(
            tree.id(),
            parents,
            author.clone(),
@@ -236,11 +240,10 @@ where
            headers,
            message,
            trailers,
-
        );
-
        commit
-
            .write(repo)
-
            .map(Oid::from)
-
            .map_err(error::Create::from)
+
        )
+
        .write(repo)?;
+

+
        Ok((Oid::from(oid), timestamp))
    }
}

modified radicle-cob/src/change/store.rs
@@ -7,7 +7,10 @@ use std::{error::Error, fmt};

use serde::{Deserialize, Serialize};

-
use crate::{history::Contents, signatures, TypeName};
+
use crate::{
+
    history::{Contents, Timestamp},
+
    signatures, TypeName,
+
};

pub trait Storage {
    type CreateError: Error + Send + Sync + 'static;
@@ -59,6 +62,8 @@ pub struct Change<Resource, Id, Signature> {
    pub manifest: Manifest,
    /// The contents that describe `Change`.
    pub contents: Contents,
+
    /// Timestamp of change.
+
    pub timestamp: Timestamp,
}

impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
modified radicle-cob/src/change_graph/evaluation.rs
@@ -70,6 +70,7 @@ fn evaluate_change(
        change.resource,
        child_commits.iter().cloned(),
        change.contents().clone(),
+
        change.timestamp,
    ))
}

modified radicle-cob/src/history.rs
@@ -15,7 +15,7 @@ use radicle_crypto::PublicKey;
use crate::pruning_fold;

pub mod entry;
-
pub use entry::{Clock, Contents, Entry, EntryId, EntryWithClock};
+
pub use entry::{Clock, Contents, Entry, EntryId, EntryWithClock, Timestamp};

/// The DAG of changes making up the history of a collaborative object.
#[derive(Clone, Debug)]
@@ -44,6 +44,7 @@ impl History {
        actor: PublicKey,
        resource: Oid,
        contents: Contents,
+
        timestamp: Timestamp,
    ) -> Self
    where
        Id: Into<EntryId>,
@@ -55,6 +56,7 @@ impl History {
            resource,
            children: vec![],
            contents,
+
            timestamp,
        };
        let mut entries = HashMap::new();
        entries.insert(id, EntryWithClock::from(root_entry));
@@ -85,6 +87,16 @@ impl History {
            .unwrap_or_default()
    }

+
    /// Get the current history timestamp.
+
    /// This is the latest timestamp of any tip.
+
    pub fn timestamp(&self) -> Clock {
+
        self.graph
+
            .externals(petgraph::Direction::Outgoing)
+
            .map(|n| self.graph[n].timestamp())
+
            .max()
+
            .unwrap_or_default()
+
    }
+

    /// A topological (parents before children) traversal of the dependency
    /// graph of this history. This is analagous to
    /// [`std::iter::Iterator::fold`] in that it folds every change into an
@@ -120,6 +132,7 @@ impl History {
        new_actor: PublicKey,
        new_resource: Oid,
        new_contents: Contents,
+
        new_timestamp: Timestamp,
    ) where
        Id: Into<EntryId>,
    {
@@ -131,6 +144,7 @@ impl History {
            new_resource,
            std::iter::empty::<git2::Oid>(),
            new_contents,
+
            new_timestamp,
        );
        let new_ix = self.graph.add_node(EntryWithClock {
            entry: new_entry,
modified radicle-cob/src/history/entry.rs
@@ -16,6 +16,9 @@ pub type Contents = Vec<u8>;
/// Logical clock used to track causality in change graph.
pub type Clock = u64;

+
/// Local time in seconds since epoch.
+
pub type Timestamp = u64;
+

/// A unique identifier for a history entry.
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct EntryId(Oid);
@@ -51,6 +54,8 @@ pub struct Entry {
    pub(super) children: Vec<EntryId>,
    /// The contents of this entry.
    pub(super) contents: Contents,
+
    /// The entry timestamp, as seconds since epoch.
+
    pub(super) timestamp: Timestamp,
}

impl Entry {
@@ -60,6 +65,7 @@ impl Entry {
        resource: Oid,
        children: ChildIds,
        contents: Contents,
+
        timestamp: Timestamp,
    ) -> Self
    where
        Id1: Into<EntryId>,
@@ -72,6 +78,7 @@ impl Entry {
            resource,
            children: children.into_iter().map(|id| id.into()).collect(),
            contents,
+
            timestamp,
        }
    }

@@ -90,6 +97,11 @@ impl Entry {
        &self.actor
    }

+
    /// The entry timestamp.
+
    pub fn timestamp(&self) -> Timestamp {
+
        self.timestamp
+
    }
+

    /// The contents of this change
    pub fn contents(&self) -> &Contents {
        &self.contents
modified radicle-cob/src/object/collaboration/create.rs
@@ -76,6 +76,7 @@ where
        init_change.signature.key,
        resource.content_id(),
        contents.clone(),
+
        init_change.timestamp,
    );

    let object_id = init_change.id().into();
modified radicle-cob/src/object/collaboration/update.rs
@@ -81,9 +81,13 @@ where
            message,
        },
    )?;
-
    object
-
        .history
-
        .extend(change.id, change.signature.key, change.resource, changes);
+
    object.history.extend(
+
        change.id,
+
        change.signature.key,
+
        change.resource,
+
        changes,
+
        change.timestamp,
+
    );
    storage
        .update(identifier, typename, &object_id, &change)
        .map_err(|err| error::Update::Refs { err: Box::new(err) })?;