Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add lamport clock
Alexis Sellier committed 3 years ago
commit 64862b3ab38843f5a68408aabfca716b300b4d16
parent 0482757ce91cec74dddafaff44a4d6f342a47763
4 files changed +59 -9
added radicle-crdt/src/clock.rs
@@ -0,0 +1,39 @@
+
use serde::{Deserialize, Serialize};
+

+
use crate::ord::Max;
+

+
/// Lamport clock.
+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+
#[serde(transparent)]
+
pub struct LClock {
+
    counter: Max<u64>,
+
}
+

+
impl LClock {
+
    /// Return the clock value.
+
    pub fn get(&self) -> u64 {
+
        *self.counter.get()
+
    }
+

+
    /// Increment clock and return new value.
+
    /// Must be called before sending a message.
+
    pub fn tick(&mut self) -> u64 {
+
        self.counter.incr();
+
        self.get()
+
    }
+

+
    /// Merge clock with another clock, and increment value.
+
    /// Must be called whenever a message is received.
+
    pub fn merge(&mut self, other: Self) {
+
        self.counter.merge(other.counter);
+
        self.counter.incr();
+
    }
+
}
+

+
impl From<u64> for LClock {
+
    fn from(counter: u64) -> Self {
+
        Self {
+
            counter: Max::from(counter),
+
        }
+
    }
+
}
modified radicle-crdt/src/lib.rs
@@ -1,6 +1,7 @@
#![allow(clippy::collapsible_if)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::type_complexity)]
+
pub mod clock;
pub mod lwwmap;
pub mod lwwreg;
pub mod lwwset;
modified radicle-crdt/src/ord.rs
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::Semilattice;

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+
#[serde(transparent)]
pub struct Max<T>(T);

impl<T> Max<T> {
modified radicle-crdt/src/thread.rs
@@ -8,6 +8,7 @@ use radicle::cob::Timestamp;
use radicle::crypto::{PublicKey, Signature, Signer};
use radicle::hash;

+
use crate::clock::LClock;
use crate::lwwreg::LWWReg;
use crate::lwwset::LWWSet;

@@ -31,6 +32,8 @@ pub struct Change {
    author: Author,
    /// The time at which this change was authored.
    timestamp: Timestamp,
+
    /// Lamport clock.
+
    clock: LClock,
}

impl Change {
@@ -244,11 +247,12 @@ impl Thread {
#[derive(Default)]
pub struct Actor<G> {
    signer: G,
+
    clock: LClock,
}

impl<G: Signer> Actor<G> {
    /// Create a new thread.
-
    pub fn thread(&self, title: &str, timestamp: Timestamp) -> Change {
+
    pub fn thread(&mut self, title: &str, timestamp: Timestamp) -> Change {
        self.change(
            Action::Thread {
                title: title.to_owned(),
@@ -258,7 +262,7 @@ impl<G: Signer> Actor<G> {
    }

    /// Create a new comment.
-
    pub fn comment(&self, body: &str, timestamp: Timestamp, parent: ChangeId) -> Change {
+
    pub fn comment(&mut self, body: &str, timestamp: Timestamp, parent: ChangeId) -> Change {
        self.change(
            Action::Comment {
                comment: Comment::new(String::from(body), parent),
@@ -268,28 +272,30 @@ impl<G: Signer> Actor<G> {
    }

    /// Add a tag.
-
    pub fn tag(&self, tag: TagId, timestamp: Timestamp) -> Change {
+
    pub fn tag(&mut self, tag: TagId, timestamp: Timestamp) -> Change {
        self.change(Action::Tag { tag }, timestamp)
    }

    /// Remove a tag.
-
    pub fn untag(&self, tag: TagId, timestamp: Timestamp) -> Change {
+
    pub fn untag(&mut self, tag: TagId, timestamp: Timestamp) -> Change {
        self.change(Action::Untag { tag }, timestamp)
    }

    /// Create a new redaction.
-
    pub fn redact(&self, id: ChangeId, timestamp: Timestamp) -> Change {
+
    pub fn redact(&mut self, id: ChangeId, timestamp: Timestamp) -> Change {
        self.change(Action::Redact { id }, timestamp)
    }

    /// Create a new change.
-
    pub fn change(&self, action: Action, timestamp: Timestamp) -> Change {
+
    pub fn change(&mut self, action: Action, timestamp: Timestamp) -> Change {
        let author = *self.signer.public_key();
+
        let clock = self.clock.tick();

        Change {
            action,
            author,
            timestamp,
+
            clock: clock.into(),
        }
    }

@@ -397,15 +403,18 @@ mod tests {

            let mut changes = Vec::new();
            let mut permutations: [Vec<Change>; N] = array::from_fn(|_| Vec::new());
+
            let mut clock = LClock::default();
            let author = PublicKey::from([0; 32]);

            for action in gen.take(g.size().min(8)) {
                let timestamp = Timestamp::now() + rng.u64(0..3);

+
                clock.tick();
                changes.push(Change {
                    action,
                    author,
                    timestamp,
+
                    clock,
                });
            }

@@ -420,7 +429,7 @@ mod tests {

    #[quickcheck]
    fn prop_invariants(log: Changes<3>) {
-
        let bob = Actor::<MockSigner>::default();
+
        let mut bob = Actor::<MockSigner>::default();
        let b0 = bob.thread("The Thread", Timestamp::now());
        let t = Thread::new(b0);
        let [p1, p2, p3] = log.permutations;
@@ -440,8 +449,8 @@ mod tests {

    #[test]
    fn test_invariants() {
-
        let alice = Actor::<MockSigner>::default();
-
        let bob = Actor::<MockSigner>::default();
+
        let mut alice = Actor::<MockSigner>::default();
+
        let mut bob = Actor::<MockSigner>::default();
        let time = Timestamp::now();

        let b0 = bob.thread("Dinner Ingredients", time);