From f87893cd878b99643dcfc7a73c515530af707281 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sun, 14 Jan 2018 23:44:19 +0100 Subject: [PATCH] test context for full agreement protocol --- substrate/candidate-agreement/src/table.rs | 19 +- .../candidate-agreement/src/tests/mod.rs | 165 +++++++++++++++++- 2 files changed, 164 insertions(+), 20 deletions(-) diff --git a/substrate/candidate-agreement/src/table.rs b/substrate/candidate-agreement/src/table.rs index 469deff8c0..b0f0f586cf 100644 --- a/substrate/candidate-agreement/src/table.rs +++ b/substrate/candidate-agreement/src/table.rs @@ -701,6 +701,7 @@ impl Table { #[cfg(test)] mod tests { use super::*; + use ::tests::VecBatch; use std::collections::HashMap; fn create() -> Table { @@ -733,24 +734,6 @@ mod tests { validators: HashMap } - struct VecBatch { - max_len: usize, - targets: Vec, - items: Vec, - } - - impl ::StatementBatch for VecBatch { - fn targets(&self) -> &[V] { &self.targets } - fn push(&mut self, item: T) -> bool { - if self.items.len() == self.max_len { - false - } else { - self.items.push(item); - true - } - } - } - impl Context for TestContext { type ValidatorId = ValidatorId; type Digest = Digest; diff --git a/substrate/candidate-agreement/src/tests/mod.rs b/substrate/candidate-agreement/src/tests/mod.rs index d4f5532fbd..8e69e49824 100644 --- a/substrate/candidate-agreement/src/tests/mod.rs +++ b/substrate/candidate-agreement/src/tests/mod.rs @@ -16,7 +16,168 @@ //! Tests and test helpers for the candidate agreement. -const VALIDITY_CHECK_DELAY_MS: isize = 400; -const AVAILABILITY_CHECK_DELAY_MS: isize = 200; +const VALIDITY_CHECK_DELAY_MS: u64 = 400; +const AVAILABILITY_CHECK_DELAY_MS: u64 = 200; + +use tokio_timer::Timer; use super::*; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)] +struct ValidatorId(usize); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)] +struct Digest(usize); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)] +struct GroupId(usize); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)] +struct ParachainCandidate { + group: GroupId, + data: usize, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct Proposal { + candidates: Vec, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +enum Signature { + Table(ValidatorId, table::Statement), + Bft(ValidatorId, bft::Message), +} + +struct TestAuthority { + id: ValidatorId, +} + +enum Error { + Timer(tokio_timer::TimerError), +} + +#[derive(Clone)] +struct SharedTestContext { + n_authorities: usize, + n_groups: usize, + timer: Timer, +} + +#[derive(Clone)] +struct TestContext { + shared: Arc, + local_id: ValidatorId, +} + +impl Context for TestContext { + type ValidatorId = ValidatorId; + type Digest = Digest; + type GroupId = GroupId; + type Signature = Signature; + type Proposal = Proposal; + type ParachainCandidate = ParachainCandidate; + + type CheckCandidate = Box>; + type CheckAvailability = Box>; + + type StatementBatch = VecBatch< + ValidatorId, + table::SignedStatement + >; + + fn candidate_digest(candidate: &ParachainCandidate) -> Digest { + Digest(!candidate.data & candidate.group.0) + } + + fn proposal_digest(candidate: &Proposal) -> Digest { + Digest(candidate.candidates.iter().fold(0, |mut acc, c| { + acc = acc.wrapping_shl(2); + acc ^= Self::candidate_digest(c).0; + acc + })) + } + + fn candidate_group(candidate: &ParachainCandidate) -> GroupId { + candidate.group.clone() + } + + fn round_proposer(&self, round: usize) -> ValidatorId { + ValidatorId(round % self.shared.n_authorities) + } + + fn check_validity(&self, _candidate: &ParachainCandidate) -> Self::CheckCandidate { + let future = self.shared.timer + .sleep(::std::time::Duration::from_millis(VALIDITY_CHECK_DELAY_MS)) + .map_err(Error::Timer) + .map(|_| true); + + Box::new(future) + } + + fn check_availability(&self, _candidate: &ParachainCandidate) -> Self::CheckAvailability { + let future = self.shared.timer + .sleep(::std::time::Duration::from_millis(AVAILABILITY_CHECK_DELAY_MS)) + .map_err(Error::Timer) + .map(|_| true); + + Box::new(future) + } + + fn create_proposal(&self, candidates: Vec<&ParachainCandidate>) + -> Option + { + // only if it has at least than 2/3 of all groups. + if candidates.len() >= self.shared.n_groups * 2 / 3 { + Some(Proposal { + candidates: candidates.iter().map(|x| (&**x).clone()).collect() + }) + } else { + None + } + } + + fn proposal_valid(&self, proposal: &Proposal, check_candidate: F) -> bool + where F: FnMut(&ParachainCandidate) -> bool + { + // only if it has more than 2/3 of groups. + if proposal.candidates.len() >= self.shared.n_groups * 2 / 3 { + proposal.candidates.iter().all(check_candidate) + } else { + false + } + } + + fn local_id(&self) -> ValidatorId { + self.local_id.clone() + } + + fn sign_table_statement( + &self, + statement: &table::Statement + ) -> Signature { + Signature::Table(self.local_id(), statement.clone()) + } + + fn sign_bft_message(&self, message: &bft::Message) -> Signature { + Signature::Bft(self.local_id(), message.clone()) + } +} + +pub struct VecBatch { + pub max_len: usize, + pub targets: Vec, + pub items: Vec, +} + +impl ::StatementBatch for VecBatch { + fn targets(&self) -> &[V] { &self.targets } + fn push(&mut self, item: T) -> bool { + if self.items.len() == self.max_len { + false + } else { + self.items.push(item); + true + } + } +}