diff options
Diffstat (limited to 'src/remake/vote.rs')
| -rw-r--r-- | src/remake/vote.rs | 54 |
1 files changed, 47 insertions, 7 deletions
diff --git a/src/remake/vote.rs b/src/remake/vote.rs index 12c1333..c231afa 100644 --- a/src/remake/vote.rs +++ b/src/remake/vote.rs @@ -38,7 +38,7 @@ use super::Host; use super::content; use super::records::{self, Decision, EpochRecord, Phase, Record, Register, Vote}; use crate::clock::UnixMillis; -use crate::document::store::StoreError; +use crate::document::store::{self, StoreError}; /// A majority of `electorate` members. pub(crate) fn quorum(electorate: usize) -> usize { @@ -281,7 +281,7 @@ pub(super) async fn cast<H: Host>(host: &H) { // Usually there is nothing to vote on: look first, without writing. let (voter, wanted) = (me.clone(), epoch.clone()); let now = host.now_millis(); - let (due, missed) = host + let (due, missed, swarm) = host .read_doc(move |doc| { let all = records::load_all(doc); let register = records::load_register(doc); @@ -294,9 +294,17 @@ pub(super) async fn cast<H: Host>(host: &H) { return None; } let content = record.proposal.content.clone(); - Some((id, content)) + Some((id, content, record.proposal.kind.clone())) }); - (due, missed) + let swarm = store::read_path( + doc, + &crate::document::meta::path(&[crate::document::meta::SWARM]), + ) + .ok() + .flatten() + .and_then(|swarm| swarm.as_str().map(str::to_owned)) + .unwrap_or_default(); + (due, missed, swarm) }) .await; for id in missed { @@ -311,13 +319,24 @@ pub(super) async fn cast<H: Host>(host: &H) { } } } - let Some((chosen, content)) = due else { + let Some((chosen, content, kind)) = due else { return; }; // Only with the content in hand: a committed remake must have it on a majority. - if let Some(reference) = &content - && content::available_or_fetch(host, reference).is_none() + let bytes = match &content { + Some(reference) => match content::available_or_fetch(host, reference) { + Some(bytes) => Some(bytes), + None => return, + }, + None => None, + }; + // A strict remake: only with its manifest signed by a key this node was given. + if kind == "strict" + && let Err(why) = strict_taken(host, bytes.as_deref().map(Vec::as_slice), &swarm) { + if host.remake().refuses_strict(&chosen) { + warn!(remake = %chosen, why = %why, "Not voting for a strict remake"); + } return; } // Spread over half a keepalive interval, so later voters see the earlier votes @@ -355,6 +374,27 @@ pub(super) async fn cast<H: Host>(host: &H) { } } +/// Whether this node takes a strict remake's manifest (`content`, for `swarm`): signed +/// by one of its `--strict-keys`; without any, if it follows strict remakes all the same +/// (`--follow-strict-remakes`). Why not, if not. +pub(super) fn strict_taken<H: Host>( + host: &H, + content: Option<&[u8]>, + swarm: &str, +) -> Result<(), String> { + let text = content + .and_then(|bytes| std::str::from_utf8(bytes).ok()) + .ok_or("no manifest")?; + if host.strict_keys().is_empty() { + return if host.follows_strict_remakes() { + crate::access::strict::read(text).map(|_| ()) + } else { + Err("this node has no --strict-keys (nor --follow-strict-remakes)".to_owned()) + }; + } + crate::access::strict::verify(text, host.strict_keys(), swarm).map(|_| ()) +} + /// A random duration below `limit`. fn random_part(limit: Duration) -> Duration { let mut bytes = [0u8; 8]; |
