mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 07:31:08 +00:00
1a524b8207
* Add Call type to extensible transactions. Cleanup some naming * Merge Resource and BlockExhausted into just Exhausted * Fix * Another fix * Call * Some fixes * Fix srml tests. * Fix all tests. * Refactor crypto so each application of it has its own type. * Introduce new AuthorityProvider API into Aura This will eventually allow for dynamic determination of authority keys and avoid having to set them directly on CLI. * Introduce authority determinator for Babe. Experiment with modular consensus API. * Work in progress to introduce KeyTypeId and avoid polluting API with validator IDs * Finish up drafting imonline * Rework offchain workers API. * Rework API implementation. * Make it compile for wasm, simplify app_crypto. * Fix compilation of im-online. * Fix compilation of im-online. * Fix more compilation errors. * Make it compile. * Fixing tests. * Rewrite `keystore` * Fix session tests * Bring back `TryFrom`'s' * Fix `srml-grandpa` * Fix `srml-aura` * Fix consensus babe * More fixes * Make service generate keys from dev_seed * Build fixes * Remove offchain tests * More fixes and cleanups * Fixes finality grandpa * Fix `consensus-aura` * Fix cli * Fix `node-cli` * Fix chain_spec builder * Fix doc tests * Add authority getter for grandpa. * Test fix * Fixes * Make keystore accessible from the runtime * Move app crypto to its own crate * Update `Cargo.lock` * Make the crypto stuff usable from the runtime * Adds some runtime crypto tests * Use last finalized block for grandpa authority * Fix warning * Adds `SessionKeys` runtime api * Remove `FinalityPair` and `ConsensusPair` * Minor governance tweaks to get it inline with docs. * Make the governance be up to date with the docs. * Build fixes. * Generate the inital session keys * Failing keystore is a hard error * Make babe work again * Fix grandpa * Fix tests * Disable `keystore` in consensus critical stuff * Build fix. * ImOnline supports multiple authorities at once. * Update core/application-crypto/src/ed25519.rs * Merge branch 'master' into gav-in-progress * Remove unneeded code for now. * Some `session` testing * Support querying the public keys * Cleanup offchain * Remove warnings * More cleanup * Apply suggestions from code review Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com> * More cleanups * JSONRPC API for setting keys. Also, rename traits::KeyStore* -> traits::BareCryptoStore* * Bad merge * Fix integration tests * Fix test build * Test fix * Fixes * Warnings * Another warning * Bump version.
508 lines
15 KiB
Rust
508 lines
15 KiB
Rust
// Copyright 2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Substrate.
|
|
|
|
// Substrate 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.
|
|
|
|
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Tests for the communication portion of the GRANDPA crate.
|
|
|
|
use futures::sync::mpsc;
|
|
use futures::prelude::*;
|
|
use network::consensus_gossip as network_gossip;
|
|
use network::test::{Block, Hash};
|
|
use network_gossip::Validator;
|
|
use tokio::runtime::current_thread;
|
|
use std::sync::Arc;
|
|
use keyring::Ed25519Keyring;
|
|
use codec::Encode;
|
|
|
|
use crate::environment::SharedVoterSetState;
|
|
use super::gossip::{self, GossipValidator};
|
|
use super::{AuthorityId, VoterSet, Round, SetId};
|
|
|
|
enum Event {
|
|
MessagesFor(Hash, mpsc::UnboundedSender<network_gossip::TopicNotification>),
|
|
RegisterValidator(Arc<dyn network_gossip::Validator<Block>>),
|
|
GossipMessage(Hash, Vec<u8>, bool),
|
|
SendMessage(Vec<network::PeerId>, Vec<u8>),
|
|
Report(network::PeerId, i32),
|
|
Announce(Hash),
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct TestNetwork {
|
|
sender: mpsc::UnboundedSender<Event>,
|
|
}
|
|
|
|
impl super::Network<Block> for TestNetwork {
|
|
type In = mpsc::UnboundedReceiver<network_gossip::TopicNotification>;
|
|
|
|
/// Get a stream of messages for a specific gossip topic.
|
|
fn messages_for(&self, topic: Hash) -> Self::In {
|
|
let (tx, rx) = mpsc::unbounded();
|
|
let _ = self.sender.unbounded_send(Event::MessagesFor(topic, tx));
|
|
|
|
rx
|
|
}
|
|
|
|
/// Register a gossip validator.
|
|
fn register_validator(&self, validator: Arc<dyn network_gossip::Validator<Block>>) {
|
|
let _ = self.sender.unbounded_send(Event::RegisterValidator(validator));
|
|
}
|
|
|
|
/// Gossip a message out to all connected peers.
|
|
///
|
|
/// Force causes it to be sent to all peers, even if they've seen it already.
|
|
/// Only should be used in case of consensus stall.
|
|
fn gossip_message(&self, topic: Hash, data: Vec<u8>, force: bool) {
|
|
let _ = self.sender.unbounded_send(Event::GossipMessage(topic, data, force));
|
|
}
|
|
|
|
/// Send a message to a bunch of specific peers, even if they've seen it already.
|
|
fn send_message(&self, who: Vec<network::PeerId>, data: Vec<u8>) {
|
|
let _ = self.sender.unbounded_send(Event::SendMessage(who, data));
|
|
}
|
|
|
|
/// Register a message with the gossip service, it isn't broadcast right
|
|
/// away to any peers, but may be sent to new peers joining or when asked to
|
|
/// broadcast the topic. Useful to register previous messages on node
|
|
/// startup.
|
|
fn register_gossip_message(&self, _topic: Hash, _data: Vec<u8>) {
|
|
// NOTE: only required to restore previous state on startup
|
|
// not required for tests currently
|
|
}
|
|
|
|
/// Report a peer's cost or benefit after some action.
|
|
fn report(&self, who: network::PeerId, cost_benefit: i32) {
|
|
let _ = self.sender.unbounded_send(Event::Report(who, cost_benefit));
|
|
}
|
|
|
|
/// Inform peers that a block with given hash should be downloaded.
|
|
fn announce(&self, block: Hash) {
|
|
let _ = self.sender.unbounded_send(Event::Announce(block));
|
|
}
|
|
}
|
|
|
|
impl network_gossip::ValidatorContext<Block> for TestNetwork {
|
|
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
|
|
|
|
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
|
|
|
|
fn send_message(&mut self, who: &network::PeerId, data: Vec<u8>) {
|
|
<Self as super::Network<Block>>::send_message(self, vec![who.clone()], data);
|
|
}
|
|
|
|
fn send_topic(&mut self, _: &network::PeerId, _: Hash, _: bool) { }
|
|
}
|
|
|
|
struct Tester {
|
|
net_handle: super::NetworkBridge<Block, TestNetwork>,
|
|
gossip_validator: Arc<GossipValidator<Block>>,
|
|
events: mpsc::UnboundedReceiver<Event>,
|
|
}
|
|
|
|
impl Tester {
|
|
fn filter_network_events<F>(self, mut pred: F) -> impl Future<Item=Self,Error=()>
|
|
where F: FnMut(Event) -> bool
|
|
{
|
|
let mut s = Some(self);
|
|
futures::future::poll_fn(move || loop {
|
|
match s.as_mut().unwrap().events.poll().expect("concluded early") {
|
|
Async::Ready(None) => panic!("concluded early"),
|
|
Async::Ready(Some(item)) => if pred(item) {
|
|
return Ok(Async::Ready(s.take().unwrap()))
|
|
},
|
|
Async::NotReady => return Ok(Async::NotReady),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// some random config (not really needed)
|
|
fn config() -> crate::Config {
|
|
crate::Config {
|
|
gossip_duration: std::time::Duration::from_millis(10),
|
|
justification_period: 256,
|
|
keystore: None,
|
|
name: None,
|
|
}
|
|
}
|
|
|
|
// dummy voter set state
|
|
fn voter_set_state() -> SharedVoterSetState<Block> {
|
|
use crate::authorities::AuthoritySet;
|
|
use crate::environment::{CompletedRound, CompletedRounds, HasVoted, VoterSetState};
|
|
use grandpa::round::State as RoundState;
|
|
use primitives::H256;
|
|
|
|
let state = RoundState::genesis((H256::zero(), 0));
|
|
let base = state.prevote_ghost.unwrap();
|
|
let voters = AuthoritySet::genesis(Vec::new());
|
|
let set_state = VoterSetState::Live {
|
|
completed_rounds: CompletedRounds::new(
|
|
CompletedRound {
|
|
state,
|
|
number: 0,
|
|
votes: Vec::new(),
|
|
base,
|
|
},
|
|
0,
|
|
&voters,
|
|
),
|
|
current_round: HasVoted::No,
|
|
};
|
|
|
|
set_state.into()
|
|
}
|
|
|
|
// needs to run in a tokio runtime.
|
|
fn make_test_network() -> (
|
|
impl Future<Item=Tester,Error=()>,
|
|
TestNetwork,
|
|
) {
|
|
let (tx, rx) = mpsc::unbounded();
|
|
let net = TestNetwork { sender: tx };
|
|
|
|
#[derive(Clone)]
|
|
struct Exit;
|
|
|
|
impl Future for Exit {
|
|
type Item = ();
|
|
type Error = ();
|
|
|
|
fn poll(&mut self) -> Poll<(), ()> {
|
|
Ok(Async::NotReady)
|
|
}
|
|
}
|
|
|
|
let (bridge, startup_work) = super::NetworkBridge::new(
|
|
net.clone(),
|
|
config(),
|
|
voter_set_state(),
|
|
Exit,
|
|
);
|
|
|
|
(
|
|
startup_work.map(move |()| Tester {
|
|
gossip_validator: bridge.validator.clone(),
|
|
net_handle: bridge,
|
|
events: rx,
|
|
}),
|
|
net,
|
|
)
|
|
}
|
|
|
|
fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(AuthorityId, u64)> {
|
|
keys.iter()
|
|
.map(|key| key.clone().public().into())
|
|
.map(|id| (id, 1))
|
|
.collect()
|
|
}
|
|
|
|
struct NoopContext;
|
|
|
|
impl network_gossip::ValidatorContext<Block> for NoopContext {
|
|
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
|
|
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
|
|
fn send_message(&mut self, _: &network::PeerId, _: Vec<u8>) { }
|
|
fn send_topic(&mut self, _: &network::PeerId, _: Hash, _: bool) { }
|
|
}
|
|
|
|
#[test]
|
|
fn good_commit_leads_to_relay() {
|
|
let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
|
|
let public = make_ids(&private[..]);
|
|
let voter_set = Arc::new(public.iter().cloned().collect::<VoterSet<AuthorityId>>());
|
|
|
|
let round = 0;
|
|
let set_id = 1;
|
|
|
|
let commit = {
|
|
let target_hash: Hash = [1; 32].into();
|
|
let target_number = 500;
|
|
|
|
let precommit = grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
|
let payload = super::localized_payload(
|
|
round, set_id, &grandpa::Message::Precommit(precommit.clone())
|
|
);
|
|
|
|
let mut precommits = Vec::new();
|
|
let mut auth_data = Vec::new();
|
|
|
|
for (i, key) in private.iter().enumerate() {
|
|
precommits.push(precommit.clone());
|
|
|
|
let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..]));
|
|
auth_data.push((signature, public[i].0.clone()))
|
|
}
|
|
|
|
grandpa::CompactCommit {
|
|
target_hash,
|
|
target_number,
|
|
precommits,
|
|
auth_data,
|
|
}
|
|
};
|
|
|
|
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
|
|
round: Round(round),
|
|
set_id: SetId(set_id),
|
|
message: commit,
|
|
}).encode();
|
|
|
|
let id = network::PeerId::random();
|
|
let global_topic = super::global_topic::<Block>(set_id);
|
|
|
|
let test = make_test_network().0
|
|
.and_then(move |tester| {
|
|
// register a peer.
|
|
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL);
|
|
Ok((tester, id))
|
|
})
|
|
.and_then(move |(tester, id)| {
|
|
// start round, dispatch commit, and wait for broadcast.
|
|
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
|
|
|
{
|
|
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
|
match action {
|
|
gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic),
|
|
_ => panic!("wrong expected outcome from initial commit validation"),
|
|
}
|
|
}
|
|
|
|
let commit_to_send = encoded_commit.clone();
|
|
|
|
// asking for global communication will cause the test network
|
|
// to send us an event asking us for a stream. use it to
|
|
// send a message.
|
|
let sender_id = id.clone();
|
|
let send_message = tester.filter_network_events(move |event| match event {
|
|
Event::MessagesFor(topic, sender) => {
|
|
if topic != global_topic { return false }
|
|
let _ = sender.unbounded_send(network_gossip::TopicNotification {
|
|
message: commit_to_send.clone(),
|
|
sender: Some(sender_id.clone()),
|
|
});
|
|
|
|
true
|
|
}
|
|
_ => false,
|
|
});
|
|
|
|
// when the commit comes in, we'll tell the callback it was good.
|
|
let handle_commit = commits_in.into_future()
|
|
.map(|(item, _)| {
|
|
match item.unwrap() {
|
|
grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
|
callback.run(grandpa::voter::CommitProcessingOutcome::good());
|
|
},
|
|
_ => panic!("commit expected"),
|
|
}
|
|
})
|
|
.map_err(|_| panic!("could not process commit"));
|
|
|
|
// once the message is sent and commit is "handled" we should have
|
|
// a repropagation event coming from the network.
|
|
send_message.join(handle_commit).and_then(move |(tester, ())| {
|
|
tester.filter_network_events(move |event| match event {
|
|
Event::GossipMessage(topic, data, false) => {
|
|
if topic == global_topic && data == encoded_commit {
|
|
true
|
|
} else {
|
|
panic!("Trying to gossip something strange")
|
|
}
|
|
}
|
|
_ => false,
|
|
})
|
|
})
|
|
.map_err(|_| panic!("could not watch for gossip message"))
|
|
.map(|_| ())
|
|
});
|
|
|
|
current_thread::block_on_all(test).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn bad_commit_leads_to_report() {
|
|
let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
|
|
let public = make_ids(&private[..]);
|
|
let voter_set = Arc::new(public.iter().cloned().collect::<VoterSet<AuthorityId>>());
|
|
|
|
let round = 0;
|
|
let set_id = 1;
|
|
|
|
let commit = {
|
|
let target_hash: Hash = [1; 32].into();
|
|
let target_number = 500;
|
|
|
|
let precommit = grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
|
let payload = super::localized_payload(
|
|
round, set_id, &grandpa::Message::Precommit(precommit.clone())
|
|
);
|
|
|
|
let mut precommits = Vec::new();
|
|
let mut auth_data = Vec::new();
|
|
|
|
for (i, key) in private.iter().enumerate() {
|
|
precommits.push(precommit.clone());
|
|
|
|
let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..]));
|
|
auth_data.push((signature, public[i].0.clone()))
|
|
}
|
|
|
|
grandpa::CompactCommit {
|
|
target_hash,
|
|
target_number,
|
|
precommits,
|
|
auth_data,
|
|
}
|
|
};
|
|
|
|
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
|
|
round: Round(round),
|
|
set_id: SetId(set_id),
|
|
message: commit,
|
|
}).encode();
|
|
|
|
let id = network::PeerId::random();
|
|
let global_topic = super::global_topic::<Block>(set_id);
|
|
|
|
let test = make_test_network().0
|
|
.and_then(move |tester| {
|
|
// register a peer.
|
|
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL);
|
|
Ok((tester, id))
|
|
})
|
|
.and_then(move |(tester, id)| {
|
|
// start round, dispatch commit, and wait for broadcast.
|
|
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
|
|
|
{
|
|
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
|
match action {
|
|
gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic),
|
|
_ => panic!("wrong expected outcome from initial commit validation"),
|
|
}
|
|
}
|
|
|
|
let commit_to_send = encoded_commit.clone();
|
|
|
|
// asking for global communication will cause the test network
|
|
// to send us an event asking us for a stream. use it to
|
|
// send a message.
|
|
let sender_id = id.clone();
|
|
let send_message = tester.filter_network_events(move |event| match event {
|
|
Event::MessagesFor(topic, sender) => {
|
|
if topic != global_topic { return false }
|
|
let _ = sender.unbounded_send(network_gossip::TopicNotification {
|
|
message: commit_to_send.clone(),
|
|
sender: Some(sender_id.clone()),
|
|
});
|
|
|
|
true
|
|
}
|
|
_ => false,
|
|
});
|
|
|
|
// when the commit comes in, we'll tell the callback it was good.
|
|
let handle_commit = commits_in.into_future()
|
|
.map(|(item, _)| {
|
|
match item.unwrap() {
|
|
grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
|
callback.run(grandpa::voter::CommitProcessingOutcome::bad());
|
|
},
|
|
_ => panic!("commit expected"),
|
|
}
|
|
})
|
|
.map_err(|_| panic!("could not process commit"));
|
|
|
|
// once the message is sent and commit is "handled" we should have
|
|
// a report event coming from the network.
|
|
send_message.join(handle_commit).and_then(move |(tester, ())| {
|
|
tester.filter_network_events(move |event| match event {
|
|
Event::Report(who, cost_benefit) => {
|
|
if who == id && cost_benefit == super::cost::INVALID_COMMIT {
|
|
true
|
|
} else {
|
|
panic!("reported unknown peer or unexpected cost");
|
|
}
|
|
}
|
|
_ => false,
|
|
})
|
|
})
|
|
.map_err(|_| panic!("could not watch for peer report"))
|
|
.map(|_| ())
|
|
});
|
|
|
|
current_thread::block_on_all(test).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn peer_with_higher_view_leads_to_catch_up_request() {
|
|
let id = network::PeerId::random();
|
|
|
|
let (tester, mut net) = make_test_network();
|
|
let test = tester
|
|
.and_then(move |tester| {
|
|
// register a peer.
|
|
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL);
|
|
Ok((tester, id))
|
|
})
|
|
.and_then(move |(tester, id)| {
|
|
// send neighbor message at round 10 and height 50
|
|
let result = tester.gossip_validator.validate(
|
|
&mut net,
|
|
&id,
|
|
&gossip::GossipMessage::<Block>::from(gossip::NeighborPacket {
|
|
set_id: SetId(0),
|
|
round: Round(10),
|
|
commit_finalized_height: 50,
|
|
}).encode(),
|
|
);
|
|
|
|
// neighbor packets are always discard
|
|
match result {
|
|
network_gossip::ValidationResult::Discard => {},
|
|
_ => panic!("wrong expected outcome from neighbor validation"),
|
|
}
|
|
|
|
// a catch up request should be sent to the peer for round - 1
|
|
tester.filter_network_events(move |event| match event {
|
|
Event::SendMessage(peers, message) => {
|
|
assert_eq!(
|
|
peers,
|
|
vec![id.clone()],
|
|
);
|
|
|
|
assert_eq!(
|
|
message,
|
|
gossip::GossipMessage::<Block>::CatchUpRequest(
|
|
gossip::CatchUpRequestMessage {
|
|
set_id: SetId(0),
|
|
round: Round(9),
|
|
}
|
|
).encode(),
|
|
);
|
|
|
|
true
|
|
},
|
|
_ => false,
|
|
})
|
|
.map_err(|_| panic!("could not watch for peer send message"))
|
|
.map(|_| ())
|
|
});
|
|
|
|
current_thread::block_on_all(test).unwrap();
|
|
}
|