mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 18:17:56 +00:00
more type-safe justifications
This commit is contained in:
@@ -23,8 +23,8 @@ use std::hash::Hash;
|
||||
use super::{Message, LocalizedMessage};
|
||||
|
||||
/// Justification for some state at a given round.
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub struct Justification<D, S> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UncheckedJustification<D, S> {
|
||||
/// The round.
|
||||
pub round_number: usize,
|
||||
/// The digest prepared for.
|
||||
@@ -33,36 +33,61 @@ pub struct Justification<D, S> {
|
||||
pub signatures: Vec<S>,
|
||||
}
|
||||
|
||||
impl<D, S> Justification<D, S> {
|
||||
impl<D, S> UncheckedJustification<D, S> {
|
||||
/// Fails if there are duplicate signatures or invalid.
|
||||
///
|
||||
/// Provide a closure for checking whether the signature is valid on a
|
||||
/// digest.
|
||||
///
|
||||
/// The closure should return true iff the round number, digest, and signature
|
||||
/// The closure should returns a checked justification iff the round number, digest, and signature
|
||||
/// represent a valid message and the signer was authorized to issue
|
||||
/// it.
|
||||
///
|
||||
/// The `check_message` closure may vary based on context.
|
||||
pub fn check<F, V>(&self, threshold: usize, check_message: F) -> bool
|
||||
pub fn check<F, V>(self, threshold: usize, mut check_message: F)
|
||||
-> Result<Justification<D, S>, Self>
|
||||
where
|
||||
F: Fn(usize, &D, &S) -> Option<V>,
|
||||
F: FnMut(usize, &D, &S) -> Option<V>,
|
||||
V: Hash + Eq,
|
||||
{
|
||||
let mut voted = HashSet::new();
|
||||
let checks_out = {
|
||||
let mut checks_out = || {
|
||||
let mut voted = HashSet::new();
|
||||
|
||||
for signature in &self.signatures {
|
||||
match check_message(self.round_number, &self.digest, signature) {
|
||||
None => return false,
|
||||
Some(v) => {
|
||||
if !voted.insert(v) {
|
||||
return false;
|
||||
for signature in &self.signatures {
|
||||
match check_message(self.round_number, &self.digest, signature) {
|
||||
None => return false,
|
||||
Some(v) => {
|
||||
if !voted.insert(v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
voted.len() >= threshold
|
||||
voted.len() >= threshold
|
||||
};
|
||||
|
||||
checks_out()
|
||||
};
|
||||
|
||||
if checks_out {
|
||||
Ok(Justification(self))
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A checked justification.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Justification<D,S>(UncheckedJustification<D,S>);
|
||||
|
||||
impl<D, S> ::std::ops::Deref for Justification<D, S> {
|
||||
type Target = UncheckedJustification<D, S>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +253,11 @@ impl<Candidate, Digest, ValidatorId, Signature> Accumulator<Candidate, Digest, V
|
||||
.map(|&(_, ref s)| s.clone())
|
||||
.collect();
|
||||
|
||||
self.state = State::Prepared(PrepareJustification {
|
||||
self.state = State::Prepared(Justification(UncheckedJustification {
|
||||
round_number: self.round_number,
|
||||
digest: threshold_prepared,
|
||||
signatures: signatures,
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,11 +294,11 @@ impl<Candidate, Digest, ValidatorId, Signature> Accumulator<Candidate, Digest, V
|
||||
.map(|&(_, ref s)| s.clone())
|
||||
.collect();
|
||||
|
||||
self.state = State::Committed(Justification {
|
||||
self.state = State::Committed(Justification(UncheckedJustification {
|
||||
round_number: self.round_number,
|
||||
digest: threshold_committed,
|
||||
signatures: signatures,
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +339,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn justification_checks_out() {
|
||||
let mut justification = Justification {
|
||||
let mut justification = UncheckedJustification {
|
||||
round_number: 2,
|
||||
digest: Digest(600),
|
||||
signatures: (0..10).map(|i| Signature(600, i)).collect(),
|
||||
@@ -328,19 +353,19 @@ mod tests {
|
||||
}
|
||||
};
|
||||
|
||||
assert!(justification.check(7, &check_message));
|
||||
assert!(!justification.check(11, &check_message));
|
||||
assert!(justification.clone().check(7, &check_message).is_ok());
|
||||
assert!(justification.clone().check(11, &check_message).is_err());
|
||||
|
||||
{
|
||||
// one bad signature is enough to spoil it.
|
||||
justification.signatures.push(Signature(1001, 255));
|
||||
assert!(!justification.check(7, &check_message));
|
||||
assert!(justification.clone().check(7, &check_message).is_err());
|
||||
|
||||
justification.signatures.pop();
|
||||
}
|
||||
// duplicates not allowed.
|
||||
justification.signatures.extend((0..10).map(|i| Signature(600, i)));
|
||||
assert!(!justification.check(11, &check_message));
|
||||
assert!(justification.clone().check(11, &check_message).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -29,7 +29,7 @@ use futures::{future, Future, Stream, Sink, Poll, Async, AsyncSink};
|
||||
|
||||
use self::accumulator::State;
|
||||
|
||||
pub use self::accumulator::{Accumulator, Justification, PrepareJustification};
|
||||
pub use self::accumulator::{Accumulator, Justification, PrepareJustification, UncheckedJustification};
|
||||
|
||||
/// Messages over the proposal.
|
||||
/// Each message carries an associated round number.
|
||||
|
||||
@@ -331,13 +331,13 @@ fn threshold_plus_one_locked_on_proposal_only_one_with_candidate() {
|
||||
let locked_proposal = Candidate(999_999_999);
|
||||
let locked_digest = Digest(999_999_999);
|
||||
let locked_round = 1;
|
||||
let justification = PrepareJustification {
|
||||
let justification = UncheckedJustification {
|
||||
round_number: locked_round,
|
||||
digest: locked_digest.clone(),
|
||||
signatures: (0..7)
|
||||
.map(|i| Signature(Message::Prepare(locked_round, locked_digest.clone()), ValidatorId(i)))
|
||||
.collect()
|
||||
};
|
||||
}.check(7, |_, _, s| Some(s.1.clone())).unwrap();
|
||||
|
||||
let mut shared_context = SharedContext::new(node_count);
|
||||
shared_context.current_round = locked_round + 1;
|
||||
|
||||
Reference in New Issue
Block a user