mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-23 20:11:06 +00:00
BEEFY add tests for rounds (#10328)
* new_rounds() * WIP * test add_vote() * test drop() * learn to spell * go get some coffee * cargo fmt * lump everythings together again
This commit is contained in:
Generated
+1
@@ -494,6 +494,7 @@ dependencies = [
|
|||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-keystore",
|
"sp-keystore",
|
||||||
"sp-runtime",
|
"sp-runtime",
|
||||||
|
"sp-tracing",
|
||||||
"strum",
|
"strum",
|
||||||
"substrate-prometheus-endpoint",
|
"substrate-prometheus-endpoint",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ sc-network-gossip = { version = "0.10.0-dev", path = "../network-gossip" }
|
|||||||
beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy" }
|
beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
sp-tracing = { version = "4.0.0-dev", path = "../../primitives/tracing" }
|
||||||
sc-network-test = { version = "0.8.0", path = "../network/test" }
|
sc-network-test = { version = "0.8.0", path = "../network/test" }
|
||||||
|
|
||||||
strum = { version = "0.22", features = ["derive"] }
|
strum = { version = "0.22", features = ["derive"] }
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ use beefy_primitives::{
|
|||||||
|
|
||||||
use crate::keystore::BeefyKeystore;
|
use crate::keystore::BeefyKeystore;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[path = "gossip_tests.rs"]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
// Limit BEEFY gossip by keeping only a bound number of voting rounds alive.
|
// Limit BEEFY gossip by keeping only a bound number of voting rounds alive.
|
||||||
const MAX_LIVE_GOSSIP_ROUNDS: usize = 3;
|
const MAX_LIVE_GOSSIP_ROUNDS: usize = 3;
|
||||||
|
|
||||||
@@ -234,3 +230,177 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use sc_keystore::LocalKeystore;
|
||||||
|
use sc_network_test::Block;
|
||||||
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
|
|
||||||
|
use beefy_primitives::{crypto::Signature, Commitment, MmrRootHash, VoteMessage, KEY_TYPE};
|
||||||
|
|
||||||
|
use crate::keystore::{tests::Keyring, BeefyKeystore};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn note_round_works() {
|
||||||
|
let gv = GossipValidator::<Block>::new();
|
||||||
|
|
||||||
|
gv.note_round(1u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &1u64));
|
||||||
|
|
||||||
|
drop(live);
|
||||||
|
|
||||||
|
gv.note_round(3u64);
|
||||||
|
gv.note_round(7u64);
|
||||||
|
gv.note_round(10u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
|
||||||
|
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
||||||
|
|
||||||
|
assert!(!GossipValidator::<Block>::is_live(&live, &1u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &7u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &10u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keeps_most_recent_max_rounds() {
|
||||||
|
let gv = GossipValidator::<Block>::new();
|
||||||
|
|
||||||
|
gv.note_round(3u64);
|
||||||
|
gv.note_round(7u64);
|
||||||
|
gv.note_round(10u64);
|
||||||
|
gv.note_round(1u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
|
||||||
|
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
||||||
|
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
||||||
|
assert!(!GossipValidator::<Block>::is_live(&live, &1u64));
|
||||||
|
|
||||||
|
drop(live);
|
||||||
|
|
||||||
|
gv.note_round(23u64);
|
||||||
|
gv.note_round(15u64);
|
||||||
|
gv.note_round(20u64);
|
||||||
|
gv.note_round(2u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
|
||||||
|
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
||||||
|
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &15u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &20u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &23u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn note_same_round_twice() {
|
||||||
|
let gv = GossipValidator::<Block>::new();
|
||||||
|
|
||||||
|
gv.note_round(3u64);
|
||||||
|
gv.note_round(7u64);
|
||||||
|
gv.note_round(10u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
|
||||||
|
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
||||||
|
|
||||||
|
drop(live);
|
||||||
|
|
||||||
|
// note round #7 again -> should not change anything
|
||||||
|
gv.note_round(7u64);
|
||||||
|
|
||||||
|
let live = gv.known_votes.read();
|
||||||
|
|
||||||
|
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
||||||
|
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &7u64));
|
||||||
|
assert!(GossipValidator::<Block>::is_live(&live, &10u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestContext;
|
||||||
|
impl<B: sp_runtime::traits::Block> ValidatorContext<B> for TestContext {
|
||||||
|
fn broadcast_topic(&mut self, _topic: B::Hash, _force: bool) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast_message(&mut self, _topic: B::Hash, _message: Vec<u8>, _force: bool) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_message(&mut self, _who: &sc_network::PeerId, _message: Vec<u8>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_topic(&mut self, _who: &sc_network::PeerId, _topic: B::Hash, _force: bool) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign_commitment<BN: Encode, P: Encode>(
|
||||||
|
who: &Keyring,
|
||||||
|
commitment: &Commitment<BN, P>,
|
||||||
|
) -> Signature {
|
||||||
|
let store: SyncCryptoStorePtr = std::sync::Arc::new(LocalKeystore::in_memory());
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap();
|
||||||
|
let beefy_keystore: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_avoid_verifying_signatures_twice() {
|
||||||
|
let gv = GossipValidator::<Block>::new();
|
||||||
|
let sender = sc_network::PeerId::random();
|
||||||
|
let mut context = TestContext;
|
||||||
|
|
||||||
|
let commitment = Commitment {
|
||||||
|
payload: MmrRootHash::default(),
|
||||||
|
block_number: 3_u64,
|
||||||
|
validator_set_id: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let signature = sign_commitment(&Keyring::Alice, &commitment);
|
||||||
|
|
||||||
|
let vote = VoteMessage { commitment, id: Keyring::Alice.public(), signature };
|
||||||
|
|
||||||
|
gv.note_round(3u64);
|
||||||
|
gv.note_round(7u64);
|
||||||
|
gv.note_round(10u64);
|
||||||
|
|
||||||
|
// first time the cache should be populated.
|
||||||
|
let res = gv.validate(&mut context, &sender, &vote.encode());
|
||||||
|
|
||||||
|
assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
|
||||||
|
assert_eq!(
|
||||||
|
gv.known_votes.read().get(&vote.commitment.block_number).map(|x| x.len()),
|
||||||
|
Some(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// second time we should hit the cache
|
||||||
|
let res = gv.validate(&mut context, &sender, &vote.encode());
|
||||||
|
|
||||||
|
assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
|
||||||
|
|
||||||
|
// next we should quickly reject if the round is not live.
|
||||||
|
gv.note_round(11_u64);
|
||||||
|
gv.note_round(12_u64);
|
||||||
|
|
||||||
|
assert!(!GossipValidator::<Block>::is_live(
|
||||||
|
&*gv.known_votes.read(),
|
||||||
|
&vote.commitment.block_number
|
||||||
|
));
|
||||||
|
|
||||||
|
let res = gv.validate(&mut context, &sender, &vote.encode());
|
||||||
|
|
||||||
|
assert!(matches!(res, ValidationResult::Discard));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use sc_keystore::LocalKeystore;
|
|
||||||
use sc_network_test::Block;
|
|
||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
|
||||||
|
|
||||||
use beefy_primitives::{crypto::Signature, Commitment, MmrRootHash, VoteMessage, KEY_TYPE};
|
|
||||||
|
|
||||||
use crate::keystore::{tests::Keyring, BeefyKeystore};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn note_round_works() {
|
|
||||||
let gv = GossipValidator::<Block>::new();
|
|
||||||
|
|
||||||
gv.note_round(1u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &1u64));
|
|
||||||
|
|
||||||
drop(live);
|
|
||||||
|
|
||||||
gv.note_round(3u64);
|
|
||||||
gv.note_round(7u64);
|
|
||||||
gv.note_round(10u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
|
|
||||||
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
|
||||||
|
|
||||||
assert!(!GossipValidator::<Block>::is_live(&live, &1u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &7u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &10u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn keeps_most_recent_max_rounds() {
|
|
||||||
let gv = GossipValidator::<Block>::new();
|
|
||||||
|
|
||||||
gv.note_round(3u64);
|
|
||||||
gv.note_round(7u64);
|
|
||||||
gv.note_round(10u64);
|
|
||||||
gv.note_round(1u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
|
|
||||||
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
|
||||||
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
|
||||||
assert!(!GossipValidator::<Block>::is_live(&live, &1u64));
|
|
||||||
|
|
||||||
drop(live);
|
|
||||||
|
|
||||||
gv.note_round(23u64);
|
|
||||||
gv.note_round(15u64);
|
|
||||||
gv.note_round(20u64);
|
|
||||||
gv.note_round(2u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
|
|
||||||
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
|
||||||
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &15u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &20u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &23u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn note_same_round_twice() {
|
|
||||||
let gv = GossipValidator::<Block>::new();
|
|
||||||
|
|
||||||
gv.note_round(3u64);
|
|
||||||
gv.note_round(7u64);
|
|
||||||
gv.note_round(10u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
|
|
||||||
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
|
||||||
|
|
||||||
drop(live);
|
|
||||||
|
|
||||||
// note round #7 again -> should not change anything
|
|
||||||
gv.note_round(7u64);
|
|
||||||
|
|
||||||
let live = gv.known_votes.read();
|
|
||||||
|
|
||||||
assert_eq!(live.len(), MAX_LIVE_GOSSIP_ROUNDS);
|
|
||||||
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &3u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &7u64));
|
|
||||||
assert!(GossipValidator::<Block>::is_live(&live, &10u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestContext;
|
|
||||||
impl<B: sp_runtime::traits::Block> ValidatorContext<B> for TestContext {
|
|
||||||
fn broadcast_topic(&mut self, _topic: B::Hash, _force: bool) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast_message(&mut self, _topic: B::Hash, _message: Vec<u8>, _force: bool) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_message(&mut self, _who: &sc_network::PeerId, _message: Vec<u8>) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_topic(&mut self, _who: &sc_network::PeerId, _topic: B::Hash, _force: bool) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_commitment<BN: Encode, P: Encode>(
|
|
||||||
who: &Keyring,
|
|
||||||
commitment: &Commitment<BN, P>,
|
|
||||||
) -> Signature {
|
|
||||||
let store: SyncCryptoStorePtr = std::sync::Arc::new(LocalKeystore::in_memory());
|
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap();
|
|
||||||
let beefy_keystore: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_avoid_verifying_signatures_twice() {
|
|
||||||
let gv = GossipValidator::<Block>::new();
|
|
||||||
let sender = sc_network::PeerId::random();
|
|
||||||
let mut context = TestContext;
|
|
||||||
|
|
||||||
let commitment =
|
|
||||||
Commitment { payload: MmrRootHash::default(), block_number: 3_u64, validator_set_id: 0 };
|
|
||||||
|
|
||||||
let signature = sign_commitment(&Keyring::Alice, &commitment);
|
|
||||||
|
|
||||||
let vote = VoteMessage { commitment, id: Keyring::Alice.public(), signature };
|
|
||||||
|
|
||||||
gv.note_round(3u64);
|
|
||||||
gv.note_round(7u64);
|
|
||||||
gv.note_round(10u64);
|
|
||||||
|
|
||||||
// first time the cache should be populated.
|
|
||||||
let res = gv.validate(&mut context, &sender, &vote.encode());
|
|
||||||
|
|
||||||
assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
|
|
||||||
assert_eq!(gv.known_votes.read().get(&vote.commitment.block_number).map(|x| x.len()), Some(1));
|
|
||||||
|
|
||||||
// second time we should hit the cache
|
|
||||||
let res = gv.validate(&mut context, &sender, &vote.encode());
|
|
||||||
|
|
||||||
assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
|
|
||||||
|
|
||||||
// next we should quickly reject if the round is not live.
|
|
||||||
gv.note_round(11_u64);
|
|
||||||
gv.note_round(12_u64);
|
|
||||||
|
|
||||||
assert!(!GossipValidator::<Block>::is_live(
|
|
||||||
&*gv.known_votes.read(),
|
|
||||||
&vote.commitment.block_number
|
|
||||||
));
|
|
||||||
|
|
||||||
let res = gv.validate(&mut context, &sender, &vote.encode());
|
|
||||||
|
|
||||||
assert!(matches!(res, ValidationResult::Discard));
|
|
||||||
}
|
|
||||||
@@ -31,10 +31,6 @@ use beefy_primitives::{
|
|||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[path = "keystore_tests.rs"]
|
|
||||||
pub mod tests;
|
|
||||||
|
|
||||||
/// A BEEFY specific keystore implemented as a `Newtype`. This is basically a
|
/// A BEEFY specific keystore implemented as a `Newtype`. This is basically a
|
||||||
/// wrapper around [`sp_keystore::SyncCryptoStore`] and allows to customize
|
/// wrapper around [`sp_keystore::SyncCryptoStore`] and allows to customize
|
||||||
/// common cryptographic functionality.
|
/// common cryptographic functionality.
|
||||||
@@ -117,3 +113,268 @@ impl From<Option<SyncCryptoStorePtr>> for BeefyKeystore {
|
|||||||
BeefyKeystore(store)
|
BeefyKeystore(store)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use sc_keystore::LocalKeystore;
|
||||||
|
use sp_core::{ecdsa, keccak_256, Pair};
|
||||||
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
|
|
||||||
|
use beefy_primitives::{crypto, KEY_TYPE};
|
||||||
|
|
||||||
|
use super::BeefyKeystore;
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
/// Set of test accounts using [`beefy_primitives::crypto`] types.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display, strum::EnumIter)]
|
||||||
|
pub(crate) enum Keyring {
|
||||||
|
Alice,
|
||||||
|
Bob,
|
||||||
|
Charlie,
|
||||||
|
Dave,
|
||||||
|
Eve,
|
||||||
|
Ferdie,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keyring {
|
||||||
|
/// Sign `msg`.
|
||||||
|
pub fn sign(self, msg: &[u8]) -> crypto::Signature {
|
||||||
|
let msg = keccak_256(msg);
|
||||||
|
ecdsa::Pair::from(self).sign_prehashed(&msg).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return key pair.
|
||||||
|
pub fn pair(self) -> crypto::Pair {
|
||||||
|
ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return public key.
|
||||||
|
pub fn public(self) -> crypto::Public {
|
||||||
|
self.pair().public()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return seed string.
|
||||||
|
pub fn to_seed(self) -> String {
|
||||||
|
format!("//{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Keyring> for crypto::Pair {
|
||||||
|
fn from(k: Keyring) -> Self {
|
||||||
|
k.pair()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Keyring> for ecdsa::Pair {
|
||||||
|
fn from(k: Keyring) -> Self {
|
||||||
|
k.pair().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keystore() -> SyncCryptoStorePtr {
|
||||||
|
Arc::new(LocalKeystore::in_memory())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_should_work() {
|
||||||
|
let msg = keccak_256(b"I am Alice!");
|
||||||
|
let sig = Keyring::Alice.sign(b"I am Alice!");
|
||||||
|
|
||||||
|
assert!(ecdsa::Pair::verify_prehashed(
|
||||||
|
&sig.clone().into(),
|
||||||
|
&msg,
|
||||||
|
&Keyring::Alice.public().into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// different public key -> fail
|
||||||
|
assert!(!ecdsa::Pair::verify_prehashed(
|
||||||
|
&sig.clone().into(),
|
||||||
|
&msg,
|
||||||
|
&Keyring::Bob.public().into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let msg = keccak_256(b"I am not Alice!");
|
||||||
|
|
||||||
|
// different msg -> fail
|
||||||
|
assert!(
|
||||||
|
!ecdsa::Pair::verify_prehashed(&sig.into(), &msg, &Keyring::Alice.public().into(),)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pair_works() {
|
||||||
|
let want = crypto::Pair::from_string("//Alice", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Alice.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Bob", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Bob.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Charlie", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Charlie.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Dave", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Dave.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Eve", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Eve.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Ferdie", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Ferdie.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//One", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::One.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
|
||||||
|
let want = crypto::Pair::from_string("//Two", None).expect("Pair failed").to_raw_vec();
|
||||||
|
let got = Keyring::Two.pair().to_raw_vec();
|
||||||
|
assert_eq!(want, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authority_id_works() {
|
||||||
|
let store = keystore();
|
||||||
|
|
||||||
|
let alice: crypto::Public =
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
||||||
|
.ok()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let bob = Keyring::Bob.public();
|
||||||
|
let charlie = Keyring::Charlie.public();
|
||||||
|
|
||||||
|
let store: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
let mut keys = vec![bob, charlie];
|
||||||
|
|
||||||
|
let id = store.authority_id(keys.as_slice());
|
||||||
|
assert!(id.is_none());
|
||||||
|
|
||||||
|
keys.push(alice.clone());
|
||||||
|
|
||||||
|
let id = store.authority_id(keys.as_slice()).unwrap();
|
||||||
|
assert_eq!(id, alice);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sign_works() {
|
||||||
|
let store = keystore();
|
||||||
|
|
||||||
|
let alice: crypto::Public =
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
||||||
|
.ok()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let store: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
let msg = b"are you involved or commited?";
|
||||||
|
|
||||||
|
let sig1 = store.sign(&alice, msg).unwrap();
|
||||||
|
let sig2 = Keyring::Alice.sign(msg);
|
||||||
|
|
||||||
|
assert_eq!(sig1, sig2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sign_error() {
|
||||||
|
let store = keystore();
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Bob.to_seed()))
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let store: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
let alice = Keyring::Alice.public();
|
||||||
|
|
||||||
|
let msg = b"are you involved or commited?";
|
||||||
|
let sig = store.sign(&alice, msg).err().unwrap();
|
||||||
|
let err = Error::Signature("ecdsa_sign_prehashed() failed".to_string());
|
||||||
|
|
||||||
|
assert_eq!(sig, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sign_no_keystore() {
|
||||||
|
let store: BeefyKeystore = None.into();
|
||||||
|
|
||||||
|
let alice = Keyring::Alice.public();
|
||||||
|
let msg = b"are you involved or commited";
|
||||||
|
|
||||||
|
let sig = store.sign(&alice, msg).err().unwrap();
|
||||||
|
let err = Error::Keystore("no Keystore".to_string());
|
||||||
|
assert_eq!(sig, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_works() {
|
||||||
|
let store = keystore();
|
||||||
|
|
||||||
|
let alice: crypto::Public =
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
||||||
|
.ok()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let store: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
// `msg` and `sig` match
|
||||||
|
let msg = b"are you involved or commited?";
|
||||||
|
let sig = store.sign(&alice, msg).unwrap();
|
||||||
|
assert!(BeefyKeystore::verify(&alice, &sig, msg));
|
||||||
|
|
||||||
|
// `msg and `sig` don't match
|
||||||
|
let msg = b"you are just involved";
|
||||||
|
assert!(!BeefyKeystore::verify(&alice, &sig, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we use keys with and without a seed for this test.
|
||||||
|
#[test]
|
||||||
|
fn public_keys_works() {
|
||||||
|
const TEST_TYPE: sp_application_crypto::KeyTypeId =
|
||||||
|
sp_application_crypto::KeyTypeId(*b"test");
|
||||||
|
|
||||||
|
let store = keystore();
|
||||||
|
|
||||||
|
let add_key = |key_type, seed: Option<&str>| {
|
||||||
|
SyncCryptoStore::ecdsa_generate_new(&*store, key_type, seed).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// test keys
|
||||||
|
let _ = add_key(TEST_TYPE, Some(Keyring::Alice.to_seed().as_str()));
|
||||||
|
let _ = add_key(TEST_TYPE, Some(Keyring::Bob.to_seed().as_str()));
|
||||||
|
|
||||||
|
let _ = add_key(TEST_TYPE, None);
|
||||||
|
let _ = add_key(TEST_TYPE, None);
|
||||||
|
|
||||||
|
// BEEFY keys
|
||||||
|
let _ = add_key(KEY_TYPE, Some(Keyring::Dave.to_seed().as_str()));
|
||||||
|
let _ = add_key(KEY_TYPE, Some(Keyring::Eve.to_seed().as_str()));
|
||||||
|
|
||||||
|
let key1: crypto::Public = add_key(KEY_TYPE, None).into();
|
||||||
|
let key2: crypto::Public = add_key(KEY_TYPE, None).into();
|
||||||
|
|
||||||
|
let store: BeefyKeystore = Some(store).into();
|
||||||
|
|
||||||
|
let keys = store.public_keys().ok().unwrap();
|
||||||
|
|
||||||
|
assert!(keys.len() == 4);
|
||||||
|
assert!(keys.contains(&Keyring::Dave.public()));
|
||||||
|
assert!(keys.contains(&Keyring::Eve.public()));
|
||||||
|
assert!(keys.contains(&key1));
|
||||||
|
assert!(keys.contains(&key2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,275 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use sc_keystore::LocalKeystore;
|
|
||||||
use sp_core::{ecdsa, keccak_256, Pair};
|
|
||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
|
||||||
|
|
||||||
use beefy_primitives::{crypto, KEY_TYPE};
|
|
||||||
|
|
||||||
use super::BeefyKeystore;
|
|
||||||
use crate::error::Error;
|
|
||||||
|
|
||||||
/// Set of test accounts using [`beefy_primitives::crypto`] types.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display, strum::EnumIter)]
|
|
||||||
pub(crate) enum Keyring {
|
|
||||||
Alice,
|
|
||||||
Bob,
|
|
||||||
Charlie,
|
|
||||||
Dave,
|
|
||||||
Eve,
|
|
||||||
Ferdie,
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keyring {
|
|
||||||
/// Sign `msg`.
|
|
||||||
pub fn sign(self, msg: &[u8]) -> crypto::Signature {
|
|
||||||
let msg = keccak_256(msg);
|
|
||||||
ecdsa::Pair::from(self).sign_prehashed(&msg).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return key pair.
|
|
||||||
pub fn pair(self) -> crypto::Pair {
|
|
||||||
ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return public key.
|
|
||||||
pub fn public(self) -> crypto::Public {
|
|
||||||
self.pair().public()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return seed string.
|
|
||||||
pub fn to_seed(self) -> String {
|
|
||||||
format!("//{}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Keyring> for crypto::Pair {
|
|
||||||
fn from(k: Keyring) -> Self {
|
|
||||||
k.pair()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Keyring> for ecdsa::Pair {
|
|
||||||
fn from(k: Keyring) -> Self {
|
|
||||||
k.pair().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keystore() -> SyncCryptoStorePtr {
|
|
||||||
Arc::new(LocalKeystore::in_memory())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_should_work() {
|
|
||||||
let msg = keccak_256(b"I am Alice!");
|
|
||||||
let sig = Keyring::Alice.sign(b"I am Alice!");
|
|
||||||
|
|
||||||
assert!(ecdsa::Pair::verify_prehashed(
|
|
||||||
&sig.clone().into(),
|
|
||||||
&msg,
|
|
||||||
&Keyring::Alice.public().into(),
|
|
||||||
));
|
|
||||||
|
|
||||||
// different public key -> fail
|
|
||||||
assert!(!ecdsa::Pair::verify_prehashed(
|
|
||||||
&sig.clone().into(),
|
|
||||||
&msg,
|
|
||||||
&Keyring::Bob.public().into(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let msg = keccak_256(b"I am not Alice!");
|
|
||||||
|
|
||||||
// different msg -> fail
|
|
||||||
assert!(!ecdsa::Pair::verify_prehashed(&sig.into(), &msg, &Keyring::Alice.public().into(),));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pair_works() {
|
|
||||||
let want = crypto::Pair::from_string("//Alice", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Alice.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Bob", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Bob.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Charlie", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Charlie.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Dave", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Dave.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Eve", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Eve.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Ferdie", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Ferdie.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//One", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::One.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
|
|
||||||
let want = crypto::Pair::from_string("//Two", None).expect("Pair failed").to_raw_vec();
|
|
||||||
let got = Keyring::Two.pair().to_raw_vec();
|
|
||||||
assert_eq!(want, got);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn authority_id_works() {
|
|
||||||
let store = keystore();
|
|
||||||
|
|
||||||
let alice: crypto::Public =
|
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let bob = Keyring::Bob.public();
|
|
||||||
let charlie = Keyring::Charlie.public();
|
|
||||||
|
|
||||||
let store: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
let mut keys = vec![bob, charlie];
|
|
||||||
|
|
||||||
let id = store.authority_id(keys.as_slice());
|
|
||||||
assert!(id.is_none());
|
|
||||||
|
|
||||||
keys.push(alice.clone());
|
|
||||||
|
|
||||||
let id = store.authority_id(keys.as_slice()).unwrap();
|
|
||||||
assert_eq!(id, alice);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sign_works() {
|
|
||||||
let store = keystore();
|
|
||||||
|
|
||||||
let alice: crypto::Public =
|
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let store: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
let msg = b"are you involved or commited?";
|
|
||||||
|
|
||||||
let sig1 = store.sign(&alice, msg).unwrap();
|
|
||||||
let sig2 = Keyring::Alice.sign(msg);
|
|
||||||
|
|
||||||
assert_eq!(sig1, sig2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sign_error() {
|
|
||||||
let store = keystore();
|
|
||||||
|
|
||||||
let _ = SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Bob.to_seed()))
|
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let store: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
let alice = Keyring::Alice.public();
|
|
||||||
|
|
||||||
let msg = b"are you involved or commited?";
|
|
||||||
let sig = store.sign(&alice, msg).err().unwrap();
|
|
||||||
let err = Error::Signature("ecdsa_sign_prehashed() failed".to_string());
|
|
||||||
|
|
||||||
assert_eq!(sig, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sign_no_keystore() {
|
|
||||||
let store: BeefyKeystore = None.into();
|
|
||||||
|
|
||||||
let alice = Keyring::Alice.public();
|
|
||||||
let msg = b"are you involved or commited";
|
|
||||||
|
|
||||||
let sig = store.sign(&alice, msg).err().unwrap();
|
|
||||||
let err = Error::Keystore("no Keystore".to_string());
|
|
||||||
assert_eq!(sig, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_works() {
|
|
||||||
let store = keystore();
|
|
||||||
|
|
||||||
let alice: crypto::Public =
|
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&Keyring::Alice.to_seed()))
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let store: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
// `msg` and `sig` match
|
|
||||||
let msg = b"are you involved or commited?";
|
|
||||||
let sig = store.sign(&alice, msg).unwrap();
|
|
||||||
assert!(BeefyKeystore::verify(&alice, &sig, msg));
|
|
||||||
|
|
||||||
// `msg and `sig` don't match
|
|
||||||
let msg = b"you are just involved";
|
|
||||||
assert!(!BeefyKeystore::verify(&alice, &sig, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that we use keys with and without a seed for this test.
|
|
||||||
#[test]
|
|
||||||
fn public_keys_works() {
|
|
||||||
const TEST_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"test");
|
|
||||||
|
|
||||||
let store = keystore();
|
|
||||||
|
|
||||||
let add_key = |key_type, seed: Option<&str>| {
|
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, key_type, seed).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
// test keys
|
|
||||||
let _ = add_key(TEST_TYPE, Some(Keyring::Alice.to_seed().as_str()));
|
|
||||||
let _ = add_key(TEST_TYPE, Some(Keyring::Bob.to_seed().as_str()));
|
|
||||||
|
|
||||||
let _ = add_key(TEST_TYPE, None);
|
|
||||||
let _ = add_key(TEST_TYPE, None);
|
|
||||||
|
|
||||||
// BEEFY keys
|
|
||||||
let _ = add_key(KEY_TYPE, Some(Keyring::Dave.to_seed().as_str()));
|
|
||||||
let _ = add_key(KEY_TYPE, Some(Keyring::Eve.to_seed().as_str()));
|
|
||||||
|
|
||||||
let key1: crypto::Public = add_key(KEY_TYPE, None).into();
|
|
||||||
let key2: crypto::Public = add_key(KEY_TYPE, None).into();
|
|
||||||
|
|
||||||
let store: BeefyKeystore = Some(store).into();
|
|
||||||
|
|
||||||
let keys = store.public_keys().ok().unwrap();
|
|
||||||
|
|
||||||
assert!(keys.len() == 4);
|
|
||||||
assert!(keys.contains(&Keyring::Dave.public()));
|
|
||||||
assert!(keys.contains(&Keyring::Eve.public()));
|
|
||||||
assert!(keys.contains(&key1));
|
|
||||||
assert!(keys.contains(&key2));
|
|
||||||
}
|
|
||||||
@@ -123,3 +123,154 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use sc_network_test::Block;
|
||||||
|
use sp_core::H256;
|
||||||
|
use sp_runtime::traits::NumberFor;
|
||||||
|
|
||||||
|
use beefy_primitives::{crypto::Public, ValidatorSet};
|
||||||
|
|
||||||
|
use super::Rounds;
|
||||||
|
use crate::keystore::tests::Keyring;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_rounds() {
|
||||||
|
sp_tracing::try_init_simple();
|
||||||
|
|
||||||
|
let rounds = Rounds::<H256, NumberFor<Block>>::new(ValidatorSet::<Public>::empty());
|
||||||
|
|
||||||
|
assert_eq!(0, rounds.validator_set_id());
|
||||||
|
assert!(rounds.validators().is_empty());
|
||||||
|
|
||||||
|
let validators = ValidatorSet::<Public> {
|
||||||
|
validators: vec![
|
||||||
|
Keyring::Alice.public(),
|
||||||
|
Keyring::Bob.public(),
|
||||||
|
Keyring::Charlie.public(),
|
||||||
|
],
|
||||||
|
id: 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
let rounds = Rounds::<H256, NumberFor<Block>>::new(validators);
|
||||||
|
|
||||||
|
assert_eq!(42, rounds.validator_set_id());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()],
|
||||||
|
rounds.validators()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_vote() {
|
||||||
|
sp_tracing::try_init_simple();
|
||||||
|
|
||||||
|
let validators = ValidatorSet::<Public> {
|
||||||
|
validators: vec![
|
||||||
|
Keyring::Alice.public(),
|
||||||
|
Keyring::Bob.public(),
|
||||||
|
Keyring::Charlie.public(),
|
||||||
|
],
|
||||||
|
id: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rounds = Rounds::<H256, NumberFor<Block>>::new(validators);
|
||||||
|
|
||||||
|
assert!(rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed"))
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
|
||||||
|
// invalid vote
|
||||||
|
assert!(!rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Dave.public(), Keyring::Dave.sign(b"I am committed"))
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
|
||||||
|
assert!(rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed"))
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
|
||||||
|
assert!(rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am committed"))
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drop() {
|
||||||
|
sp_tracing::try_init_simple();
|
||||||
|
|
||||||
|
let validators = ValidatorSet::<Public> {
|
||||||
|
validators: vec![
|
||||||
|
Keyring::Alice.public(),
|
||||||
|
Keyring::Bob.public(),
|
||||||
|
Keyring::Charlie.public(),
|
||||||
|
],
|
||||||
|
id: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rounds = Rounds::<H256, NumberFor<Block>>::new(validators);
|
||||||
|
|
||||||
|
// round 1
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")),
|
||||||
|
);
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(1), 1),
|
||||||
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")),
|
||||||
|
);
|
||||||
|
|
||||||
|
// round 2
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(2), 2),
|
||||||
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am again committed")),
|
||||||
|
);
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(2), 2),
|
||||||
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am again committed")),
|
||||||
|
);
|
||||||
|
|
||||||
|
// round 3
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(3), 3),
|
||||||
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am still committed")),
|
||||||
|
);
|
||||||
|
rounds.add_vote(
|
||||||
|
(H256::from_low_u64_le(3), 3),
|
||||||
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am still committed")),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(3, rounds.rounds.len());
|
||||||
|
|
||||||
|
// drop unknown round
|
||||||
|
assert!(rounds.drop(&(H256::from_low_u64_le(5), 5)).is_none());
|
||||||
|
assert_eq!(3, rounds.rounds.len());
|
||||||
|
|
||||||
|
// drop round 2
|
||||||
|
let signatures = rounds.drop(&(H256::from_low_u64_le(2), 2)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(2, rounds.rounds.len());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
signatures,
|
||||||
|
vec![
|
||||||
|
Some(Keyring::Alice.sign(b"I am again committed")),
|
||||||
|
Some(Keyring::Bob.sign(b"I am again committed")),
|
||||||
|
None
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user