From 25984d8dc8368de2ded95a3633ec35d326b28dad Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Sat, 27 Oct 2018 16:18:01 +0200 Subject: [PATCH] add some tests for authority set logic --- substrate/core/finality-grandpa/Cargo.toml | 2 - .../core/finality-grandpa/src/authorities.rs | 167 ++++++++++++++++-- substrate/core/finality-grandpa/src/lib.rs | 12 +- 3 files changed, 154 insertions(+), 27 deletions(-) diff --git a/substrate/core/finality-grandpa/Cargo.toml b/substrate/core/finality-grandpa/Cargo.toml index 34c0e99b631..afff3ad5cba 100644 --- a/substrate/core/finality-grandpa/Cargo.toml +++ b/substrate/core/finality-grandpa/Cargo.toml @@ -11,7 +11,6 @@ sr-primitives = { path = "../sr-primitives" } substrate-consensus-common = { path = "../consensus/common" } substrate-primitives = { path = "../primitives" } substrate-client = { path = "../client" } -substrate-network = { path = "../network" } log = "0.4" parking_lot = "0.4" tokio = "0.1.7" @@ -23,5 +22,4 @@ features = ["derive-codec"] [dev-dependencies] substrate-network = { path = "../network", features = ["test-helpers"] } -parking_lot = "0.4" substrate-keyring = { path = "../keyring" } diff --git a/substrate/core/finality-grandpa/src/authorities.rs b/substrate/core/finality-grandpa/src/authorities.rs index cbe53ceb3db..4e435f54209 100644 --- a/substrate/core/finality-grandpa/src/authorities.rs +++ b/substrate/core/finality-grandpa/src/authorities.rs @@ -53,6 +53,11 @@ impl<H, N> SharedAuthoritySet<H, N> { { f(&*self.inner.read()) } + + /// Execute a closure with the inner set mutably. + pub(crate) fn with_mut<F, U>(&self, f: F) -> U where F: FnOnce(&mut AuthoritySet<H, N>) -> U { + f(&mut *self.inner.write()) + } } impl<H: Eq, N> SharedAuthoritySet<H, N> @@ -60,17 +65,7 @@ impl<H: Eq, N> SharedAuthoritySet<H, N> { /// Note an upcoming pending transition. pub(crate) fn add_pending_change(&self, pending: PendingChange<H, N>) { - // ordered first by effective number and then by signal-block number. - let mut inner = self.inner.write(); - let key = (pending.effective_number(), pending.canon_height.clone()); - let idx = inner.pending_changes - .binary_search_by_key(&key, |change| ( - change.effective_number(), - change.canon_height.clone(), - )) - .unwrap_or_else(|i| i); - - inner.pending_changes.insert(idx, pending); + self.inner.write().add_pending_change(pending) } /// Get the earliest limit-block number, if any. @@ -82,11 +77,6 @@ impl<H: Eq, N> SharedAuthoritySet<H, N> pub(crate) fn set_id(&self) -> u64 { self.inner.read().set_id } - - /// Execute a closure with the inner set mutably. - pub(crate) fn with_mut<F, U>(&self, f: F) -> U where F: FnOnce(&mut AuthoritySet<H, N>) -> U { - f(&mut *self.inner.write()) - } } impl<H, N> From<AuthoritySet<H, N>> for SharedAuthoritySet<H, N> { @@ -118,6 +108,19 @@ impl<H, N> AuthoritySet<H, N> { impl<H: Eq, N> AuthoritySet<H, N> where N: Add<Output=N> + Ord + Clone + Debug, { + /// Note an upcoming pending transition. + pub(crate) fn add_pending_change(&mut self, pending: PendingChange<H, N>) { + // ordered first by effective number and then by signal-block number. + let key = (pending.effective_number(), pending.canon_height.clone()); + let idx = self.pending_changes + .binary_search_by_key(&key, |change| ( + change.effective_number(), + change.canon_height.clone(), + )) + .unwrap_or_else(|i| i); + + self.pending_changes.insert(idx, pending); + } /// Get the earliest limit-block number, if any. pub(crate) fn current_limit(&self) -> Option<N> { self.pending_changes.get(0).map(|change| change.effective_number().clone()) @@ -173,7 +176,7 @@ impl<H: Eq, N> AuthoritySet<H, N> /// /// This will be applied when the announcing block is at some depth within /// the finalized chain. -#[derive(Debug, Clone, Encode, Decode)] +#[derive(Debug, Clone, Encode, Decode, PartialEq)] pub(crate) struct PendingChange<H, N> { /// The new authorities and weights to apply. pub(crate) next_authorities: Vec<(AuthorityId, u64)>, @@ -192,3 +195,133 @@ impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> { self.canon_height.clone() + self.finalization_depth.clone() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn changes_sorted_in_correct_order() { + let mut authorities = AuthoritySet { + current_authorities: Vec::new(), + set_id: 0, + pending_changes: Vec::new(), + }; + + let change_a = PendingChange { + next_authorities: Vec::new(), + finalization_depth: 10, + canon_height: 5, + canon_hash: "hash_a", + }; + + let change_b = PendingChange { + next_authorities: Vec::new(), + finalization_depth: 0, + canon_height: 16, + canon_hash: "hash_b", + }; + + let change_c = PendingChange { + next_authorities: Vec::new(), + finalization_depth: 5, + canon_height: 10, + canon_hash: "hash_c", + }; + + authorities.add_pending_change(change_a.clone()); + authorities.add_pending_change(change_b.clone()); + authorities.add_pending_change(change_c.clone()); + + assert_eq!(authorities.pending_changes, vec![change_a, change_c, change_b]); + } + + #[test] + fn apply_change() { + let mut authorities = AuthoritySet { + current_authorities: Vec::new(), + set_id: 0, + pending_changes: Vec::new(), + }; + + let set_a = vec![([1; 32].into(), 5)]; + let set_b = vec![([2; 32].into(), 5)]; + + let change_a = PendingChange { + next_authorities: set_a.clone(), + finalization_depth: 10, + canon_height: 5, + canon_hash: "hash_a", + }; + + let change_b = PendingChange { + next_authorities: set_b.clone(), + finalization_depth: 10, + canon_height: 5, + canon_hash: "hash_b", + }; + + authorities.add_pending_change(change_a.clone()); + authorities.add_pending_change(change_b.clone()); + + authorities.apply_changes::<_, ()>(10, |_| panic!()).unwrap(); + assert!(authorities.current_authorities.is_empty()); + + authorities.apply_changes::<_, ()>( + 15, + |n| if n == 5 { Ok("hash_a") } else { panic!() } + ).unwrap(); + + assert_eq!(authorities.current_authorities, set_a); + assert_eq!(authorities.set_id, 1); + } + + #[test] + fn apply_many_changes_at_once() { + let mut authorities = AuthoritySet { + current_authorities: Vec::new(), + set_id: 0, + pending_changes: Vec::new(), + }; + + let set_a = vec![([1; 32].into(), 5)]; + let set_b = vec![([2; 32].into(), 5)]; + let set_c = vec![([3; 32].into(), 5)]; + + let change_a = PendingChange { + next_authorities: set_a.clone(), + finalization_depth: 10, + canon_height: 5, + canon_hash: "hash_a", + }; + + // will be ignored because it was signalled when change_a still pending. + let change_b = PendingChange { + next_authorities: set_b.clone(), + finalization_depth: 10, + canon_height: 15, + canon_hash: "hash_b", + }; + + let change_c = PendingChange { + next_authorities: set_c.clone(), + finalization_depth: 10, + canon_height: 16, + canon_hash: "hash_c", + }; + + authorities.add_pending_change(change_a.clone()); + authorities.add_pending_change(change_b.clone()); + authorities.add_pending_change(change_c.clone()); + + authorities.apply_changes(26, |n| match n { + 5 => Ok("hash_a"), + 15 => Ok("hash_b"), + 16 => Ok("hash_c"), + _ => Err(()), + }).unwrap(); + + assert_eq!(authorities.current_authorities, set_c); + assert_eq!(authorities.set_id, 2); // has been bumped only twice + } +} diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs index 7c12be4a8a8..f5379a8113a 100644 --- a/substrate/core/finality-grandpa/src/lib.rs +++ b/substrate/core/finality-grandpa/src/lib.rs @@ -24,7 +24,6 @@ extern crate substrate_client as client; extern crate sr_primitives as runtime_primitives; extern crate substrate_consensus_common as consensus_common; extern crate substrate_primitives; -extern crate substrate_network as network; extern crate tokio; extern crate parking_lot; extern crate parity_codec as codec; @@ -35,9 +34,6 @@ extern crate log; #[cfg(test)] extern crate substrate_network as network; -#[cfg(test)] -extern crate parking_lot; - #[cfg(test)] extern crate substrate_keyring as keyring; @@ -871,10 +867,10 @@ mod tests { .take_while(|n| Ok(n.header.number() < &20)) .for_each(move |_| Ok(())) ); - let voter = run_grandpa( + let (voter, _) = run_grandpa( Config { gossip_duration: TEST_GOSSIP_DURATION, - voters: voters.clone(), + genesis_voters: voters.clone(), local_key: Some(Arc::new(key.clone().into())), }, client, @@ -930,10 +926,10 @@ mod tests { .take_while(|n| Ok(n.header.number() < &20)) .for_each(move |_| Ok(())) ); - let voter = run_grandpa( + let (voter, _) = run_grandpa( Config { gossip_duration: TEST_GOSSIP_DURATION, - voters: voters.keys().cloned().collect(), + genesis_voters: voters.keys().cloned().collect(), local_key, }, client, -- GitLab