mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-21 17:55:42 +00:00
grandpa: round catchup messages (#2801)
* grandpa: initial structure for catch up messages * grandpa: answer catch up requests * grandpa: inject catch up messages into global stream * grandpa: keep track of pending catch up request * grandpa: block catchup until all referenced blocks are imported * grandpa: unify catch up and commit streams * grandpa: simplify communication stream/sink types * grandpa: note gossip validator on catch up message import * grandpa: fix cost on catch up message validation * grandpa: check signatures on catch up messages * grandpa: clean up catch up request handling state * grandpa: adjust costs on invalid catch up requests * grandpa: release lock before pushing catch up message * grandpa: validate catch up request against peer view * grandpa: catch up docs * grandpa: fix tests * grandpa: until_imported: add tests for catch up messages * grandpa: add tests for catch up message gossip validation * grandpa: integrate HistoricalVotes changes * grandpa: add test for neighbor packet triggering catch up * grandpa: add test for full voter catch up * grandpa: depend on finality-grandpa 0.8 from crates * granda: use finality-grandpa test helpers * grandpa: add PSM cost for answering catch up requests * grandpa: code style fixes Co-Authored-By: Robert Habermeier <rphmeier@gmail.com> * grandpa: more trailing commas * grandpa: lower cost of invalid catch up requests near set change * grandpa: process catch up sending on import of neighbor message * grandpa: add comments on HistoricalVotes * grandpa: use finality-grandpa v0.8.1 from crates.io * grandpa: fix test compilation
This commit is contained in:
@@ -26,6 +26,7 @@ use std::sync::Arc;
|
||||
use keyring::AuthorityKeyring;
|
||||
use parity_codec::Encode;
|
||||
|
||||
use crate::environment::SharedVoterSetState;
|
||||
use super::gossip::{self, GossipValidator};
|
||||
use super::{AuthorityId, VoterSet, Round, SetId};
|
||||
|
||||
@@ -92,6 +93,18 @@ impl super::Network<Block> for TestNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
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>>,
|
||||
@@ -125,8 +138,38 @@ fn config() -> crate::Config {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 substrate_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=()> {
|
||||
fn make_test_network() -> (
|
||||
impl Future<Item=Tester,Error=()>,
|
||||
TestNetwork,
|
||||
) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let net = TestNetwork { sender: tx };
|
||||
|
||||
@@ -145,15 +188,18 @@ fn make_test_network() -> impl Future<Item=Tester,Error=()> {
|
||||
let (bridge, startup_work) = super::NetworkBridge::new(
|
||||
net.clone(),
|
||||
config(),
|
||||
None,
|
||||
voter_set_state(),
|
||||
Exit,
|
||||
);
|
||||
|
||||
startup_work.map(move |()| Tester {
|
||||
gossip_validator: bridge.validator.clone(),
|
||||
net_handle: bridge,
|
||||
events: rx,
|
||||
})
|
||||
(
|
||||
startup_work.map(move |()| Tester {
|
||||
gossip_validator: bridge.validator.clone(),
|
||||
net_handle: bridge,
|
||||
events: rx,
|
||||
}),
|
||||
net,
|
||||
)
|
||||
}
|
||||
|
||||
fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(AuthorityId, u64)> {
|
||||
@@ -217,7 +263,7 @@ fn good_commit_leads_to_relay() {
|
||||
let id = network::PeerId::random();
|
||||
let global_topic = super::global_topic::<Block>(set_id);
|
||||
|
||||
let test = make_test_network()
|
||||
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);
|
||||
@@ -228,7 +274,7 @@ fn good_commit_leads_to_relay() {
|
||||
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
|
||||
{
|
||||
let (action, _) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
||||
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"),
|
||||
@@ -257,8 +303,12 @@ fn good_commit_leads_to_relay() {
|
||||
// when the commit comes in, we'll tell the callback it was good.
|
||||
let handle_commit = commits_in.into_future()
|
||||
.map(|(item, _)| {
|
||||
let (_, _, mut callback) = item.unwrap();
|
||||
(callback)(super::CommitProcessingOutcome::Good);
|
||||
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"));
|
||||
|
||||
@@ -328,7 +378,7 @@ fn bad_commit_leads_to_report() {
|
||||
let id = network::PeerId::random();
|
||||
let global_topic = super::global_topic::<Block>(set_id);
|
||||
|
||||
let test = make_test_network()
|
||||
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);
|
||||
@@ -339,7 +389,7 @@ fn bad_commit_leads_to_report() {
|
||||
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
|
||||
{
|
||||
let (action, _) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
||||
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"),
|
||||
@@ -368,8 +418,12 @@ fn bad_commit_leads_to_report() {
|
||||
// when the commit comes in, we'll tell the callback it was good.
|
||||
let handle_commit = commits_in.into_future()
|
||||
.map(|(item, _)| {
|
||||
let (_, _, mut callback) = item.unwrap();
|
||||
(callback)(super::CommitProcessingOutcome::Bad);
|
||||
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"));
|
||||
|
||||
@@ -393,3 +447,61 @@ fn bad_commit_leads_to_report() {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user