rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwoodcdede8b3c028d82f8f4bf27d27a703eeec315405
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Created",
"patch": {
"id": "4659525c544ae0c73311ac8c55fcddda3b1f6fa2",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"title": "radicle: fix diverging quorum",
"state": {
"status": "merged",
"conflicts": []
},
"before": "a6e33ec19a662e23e998347ec2dd2d3c90bdb7ee",
"after": "cdede8b3c028d82f8f4bf27d27a703eeec315405",
"commits": [
"cdede8b3c028d82f8f4bf27d27a703eeec315405"
],
"target": "d285eccb23f3986bfa88fc1af033e6dcb0dc3a65",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "4659525c544ae0c73311ac8c55fcddda3b1f6fa2",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "When calculating a quorum there was a case where two heads that were equal would\nresult in double-counting, allowing the quorum to pass a threshold higher than\nthe expected votes.\n\nTo prevent this, each head is immediately counted as a direct vote. Then, when\ncomparing the head to the rest of the set, if they are equal that iteration\nwould be skipped. This is because the merge base of two equal commits is that\ncommit, resulting in the double-counting.\n\nNote that the `skip` can also skip the current head, so `i + 1` is used.",
"base": "a6e33ec19a662e23e998347ec2dd2d3c90bdb7ee",
"oid": "cdede8b3c028d82f8f4bf27d27a703eeec315405",
"timestamp": 1718357404
},
{
"id": "f91a0c3e434b5befce10d60e86b48008eb448032",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": null
},
"description": "Rebase, add a few tests.",
"base": "589c3756246eaa3cffad7ff07bd1ef07543c64c3",
"oid": "3260046c67d03c5b161fcfdb148876e9cb9a0733",
"timestamp": 1718888233
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "64b34e9a-f622-4751-b27b-0c8c79f810c5"
},
"info_url": "https://cci.rad.levitte.org//64b34e9a-f622-4751-b27b-0c8c79f810c5.html"
}
Started at: 2026-09-07 21:13:08.722731+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/64b34e9a-f622-4751-b27b-0c8c79f810c5/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 189 issues · 38 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 4659525c544ae0c73311ac8c55fcddda3b1f6fa2
✓ Switched to branch patch/4659525 at revision 4659525
✓ Branch patch/4659525 setup to track rad/patches/4659525c544ae0c73311ac8c55fcddda3b1f6fa2
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout cdede8b3c028d82f8f4bf27d27a703eeec315405
HEAD is now at cdede8b3 radicle: fix diverging quorum
Exit code: 0
$ rad patch show 4659525c544ae0c73311ac8c55fcddda3b1f6fa2 -p
╭──────────────────────────────────────────────────────────────────────────────────╮
│ Title radicle: fix diverging quorum │
│ Patch 4659525c544ae0c73311ac8c55fcddda3b1f6fa2 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head cdede8b3c028d82f8f4bf27d27a703eeec315405 │
│ Base a6e33ec19a662e23e998347ec2dd2d3c90bdb7ee │
│ Target master │
│ Branches patch/4659525 │
│ Commits ahead 1, behind 1306 │
│ Status merged │
│ │
│ When calculating a quorum there was a case where two heads that were equal would │
│ result in double-counting, allowing the quorum to pass a threshold higher than │
│ the expected votes. │
│ │
│ To prevent this, each head is immediately counted as a direct vote. Then, when │
│ comparing the head to the rest of the set, if they are equal that iteration │
│ would be skipped. This is because the merge base of two equal commits is that │
│ commit, resulting in the double-counting. │
│ │
│ Note that the `skip` can also skip the current head, so `i + 1` is used. │
├──────────────────────────────────────────────────────────────────────────────────┤
│ cdede8b radicle: fix diverging quorum │
├──────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision 4659525 @ a6e33ec..cdede8b by fintohaps z6Mkire…SQZ3voM 2 years ago │
│ ↑ Revision f91a0c3 @ 589c375..3260046 by cloudhead z6MksFq…bS9wzpT 2 years ago │
│ └─ ✓ merged by cloudhead z6MksFq…bS9wzpT │
╰──────────────────────────────────────────────────────────────────────────────────╯
commit cdede8b3c028d82f8f4bf27d27a703eeec315405
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Fri Jun 14 11:20:49 2024 +0200
radicle: fix diverging quorum
When calculating a quorum there was a case where two heads that were equal would
result in double-counting, allowing the quorum to pass a threshold higher than
the expected votes.
To prevent this, each head is immediately counted as a direct vote. Then, when
comparing the head to the rest of the set, if they are equal that iteration
would be skipped. This is because the merge base of two equal commits is that
commit, resulting in the double-counting.
Note that the `skip` can also skip the current head, so `i + 1` is used.
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs
index 77b61983e..54358648d 100644
--- a/radicle/src/storage/git.rs
+++ b/radicle/src/storage/git.rs
@@ -996,11 +996,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;
}
@@ -1157,6 +1164,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);
@@ -1165,6 +1173,7 @@ mod tests {
eprintln!("C0: {c0}");
eprintln!("C1: {c1}");
eprintln!("C2: {c2}");
+ eprintln!("C3: {c3}");
eprintln!("B2: {b2}");
eprintln!("A1: {a1}");
eprintln!("M1: {m1}");
@@ -1216,6 +1225,16 @@ mod tests {
Err(QuorumError::NoQuorum)
);
+ // B2 C2 C3
+ // \ | /
+ // C1
+ // |
+ // C0
+ assert_matches!(
+ quorum(&[*b2, *c2, *c2], 3, &repo),
+ Err(QuorumError::NoQuorum)
+ );
+
// B2 C2
// \|
// A1 C1
Exit code: 0
shell: 'cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny clippy::all cargo build --all-targets --workspace cargo doc --workspace cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 64b34e9a-f622-4751-b27b-0c8c79f810c5 -v /opt/radcis/ci.rad.levitte.org/cci/state/64b34e9a-f622-4751-b27b-0c8c79f810c5/s:/64b34e9a-f622-4751-b27b-0c8c79f810c5/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/64b34e9a-f622-4751-b27b-0c8c79f810c5/w:/64b34e9a-f622-4751-b27b-0c8c79f810c5/w -w /64b34e9a-f622-4751-b27b-0c8c79f810c5/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /64b34e9a-f622-4751-b27b-0c8c79f810c5/s/script.sh
+ cargo --version
info: syncing channel updates for '1.77-x86_64-unknown-linux-gnu'
info: latest update on 2024-04-09, rust version 1.77.2 (25ef9e3d8 2024-04-09)
info: downloading component 'cargo'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-std'
info: installing component 'rustc'
cargo 1.77.2 (e52e36006 2024-03-26)
+ rustc --version
rustc 1.77.2 (25ef9e3d8 2024-04-09)
+ cargo fmt --check
error: 'cargo-fmt' is not installed for the toolchain '1.77-x86_64-unknown-linux-gnu'.
To install, run `rustup component add --toolchain 1.77-x86_64-unknown-linux-gnu rustfmt`
Exit code: 1
{
"response": "finished",
"result": "failure"
}