summaryrefslogtreecommitdiffstats
path: root/src/remake/api.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-24 21:27:01 +0200
committertv <tv@krebsco.de>2026-10-24 21:27:01 +0200
commit6d128ad96ef165eb9781677b2659fcdfcfdee0b2 (patch)
treeffa7edaf38e082580838b067217b2077afe289e0 /src/remake/api.rs
parent7adda3354362ffd3396ee5ea9e84fa521c5e6d0c (diff)
add votes register
Diffstat (limited to 'src/remake/api.rs')
-rw-r--r--src/remake/api.rs29
1 files changed, 25 insertions, 4 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs
index 3345086..ffd1844 100644
--- a/src/remake/api.rs
+++ b/src/remake/api.rs
@@ -171,6 +171,9 @@ struct Report {
enum StartError {
#[error("remake {0} is already open for this epoch")]
Open(String),
+ /// The proposer votes for its own remake, so it must be free to.
+ #[error("this node can't vote for a new remake: {0}")]
+ VoteHeld(String),
#[error("base head {0} is not in this node's document")]
UnknownBase(String),
#[error(transparent)]
@@ -454,6 +457,9 @@ async fn start_kind<H: Host>(
Some(json!({ "remake": open })),
);
}
+ Err(error @ StartError::VoteHeld(_)) => {
+ return error_response(StatusCode::CONFLICT, error.to_string(), None);
+ }
Err(error @ StartError::UnknownBase(_)) => {
return error_response(StatusCode::BAD_REQUEST, error.to_string(), None);
}
@@ -502,13 +508,25 @@ async fn propose<H: Host>(
let record_id = id.clone();
let electorate_host = host.clone();
let me = host.node_id();
+ let started = host.remake().started_at() + grace;
let record = host
.commit_doc(move |doc| -> Result<Record, StartError> {
- if let Some((open, _)) = records::load_all(doc).into_iter().find(|(_, record)| {
+ let all = records::load_all(doc);
+ if let Some((open, _)) = all.iter().find(|(_, record)| {
record.proposal.from_epoch == proposal.from_epoch
&& record.phase(now, grace).is_open()
}) {
- return Err(StartError::Open(open));
+ return Err(StartError::Open(open.clone()));
+ }
+ let register = records::load_register(doc);
+ match vote::freedom(&me, &proposal.from_epoch, &all, &register, started) {
+ vote::Freedom::Any => {}
+ vote::Freedom::Only(id) => {
+ return Err(StartError::VoteHeld(format!(
+ "it may have voted for remake {id} before its start"
+ )));
+ }
+ vote::Freedom::Held(reason) => return Err(StartError::VoteHeld(reason)),
}
// The base is the requested heads, or everything before the proposal (the
@@ -538,8 +556,11 @@ async fn propose<H: Host>(
proposal.electorate = electorate;
records::write_proposal(doc, &record_id, &proposal)?;
- let own_vote = Vote { at: now };
- records::write_vote(doc, &record_id, &me, &own_vote)?;
+ let own_vote = Vote {
+ remake: record_id.clone(),
+ at: now,
+ };
+ records::write_vote(doc, &me, &own_vote)?;
Ok(Record {
proposal,
decision: None,