Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle: fix diverging quorum
Fintan Halpenny committed 1 year ago
commit 3260046c67d03c5b161fcfdb148876e9cb9a0733
parent 589c3756246eaa3cffad7ff07bd1ef07543c64c3
1 file changed +31 -3
modified radicle/src/storage/git.rs
@@ -925,11 +925,18 @@ pub fn quorum(
    for (i, head) in heads.iter().enumerate() {
        let head = Oid::from(*head);

-
        for other in heads.iter().skip(i) {
+
        // Add a direct vote for this head.
+
        *candidates.entry(head).or_default() += 1;
+

+
        // Compare this head to all other heads ahead of it in the list.
+
        for other in heads.iter().skip(i + 1) {
+
            // N.b. if heads are equal then skip it, otherwise it will end up as
+
            // a double vote.
+
            if *head == *other {
+
                continue;
+
            }
            let base = repo.merge_base(*head, *other)?;

-
            // Nb. This also handles `head` == `other`, which happens once for every head,
-
            // as well as when there is more than one head with the same OID.
            if base == *other || base == *head {
                *candidates.entry(Oid::from(base)).or_default() += 1;
            }
@@ -1086,6 +1093,7 @@ mod tests {
        let c0: git::Oid = c0.into();
        let c1 = fixtures::commit("C1", &[*c0], &repo);
        let c2 = fixtures::commit("C2", &[*c1], &repo);
+
        let c3 = fixtures::commit("C3", &[*c1], &repo);
        let b2 = fixtures::commit("B2", &[*c1], &repo);
        let a1 = fixtures::commit("A1", &[*c0], &repo);
        let m1 = fixtures::commit("M1", &[*c2, *b2], &repo);
@@ -1094,6 +1102,7 @@ mod tests {
        eprintln!("C0: {c0}");
        eprintln!("C1: {c1}");
        eprintln!("C2: {c2}");
+
        eprintln!("C3: {c3}");
        eprintln!("B2: {b2}");
        eprintln!("A1: {a1}");
        eprintln!("M1: {m1}");
@@ -1145,6 +1154,25 @@ mod tests {
            Err(QuorumError::NoQuorum)
        );

+
        // B2 C2 C3
+
        //  \ | /
+
        //    C1
+
        //    |
+
        //    C0
+
        assert_matches!(quorum(&[*b2, *c2, *c2], 2, &repo), Ok(c2));
+
        assert_matches!(
+
            quorum(&[*b2, *c2, *c2], 3, &repo),
+
            Err(QuorumError::NoQuorum)
+
        );
+
        assert_matches!(
+
            quorum(&[*b2, *c2, *b2, *c2], 3, &repo),
+
            Err(QuorumError::NoQuorum)
+
        );
+
        assert_matches!(
+
            quorum(&[*c3, *b2, *c2, *b2, *c2, *c3], 3, &repo),
+
            Err(QuorumError::NoQuorum)
+
        );
+

        //  B2 C2
        //    \|
        // A1 C1