Newer
Older
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot 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.
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! The "validation session" networking code built on top of the base network service.
//! This fulfills the `polkadot_validation::Network` trait, providing a hook to be called
//! each time a validation session begins on a new chain head.
use crate::gossip::GossipMessage;
asynchronous rob
committed
use sr_primitives::traits::ProvideRuntimeApi;
use substrate_network::{PeerId, Context as NetContext};
use substrate_network::consensus_gossip::{
self, TopicNotification, MessageRecipient as GossipMessageRecipient, ConsensusMessage,
};
use polkadot_validation::{Network as ParachainNetwork, SharedTable, Collators, Statement, GenericStatement};
asynchronous rob
committed
use polkadot_primitives::{Block, BlockId, Hash, SessionKey};
use polkadot_primitives::parachain::{
Id as ParaId, Collation, Extrinsic, ParachainHost, CandidateReceipt, CollatorId,
ValidatorId, PoVBlock, ValidatorIndex,
};
use futures::prelude::*;
use futures::future::{self, Executor as FutureExecutor};
use futures::sync::mpsc;
use futures::sync::oneshot::{self, Receiver};
use std::collections::hash_map::{HashMap, Entry};
use std::io;
use std::sync::Arc;
use arrayvec::ArrayVec;
use tokio::runtime::TaskExecutor;
use parking_lot::Mutex;
use log::{debug, warn};
use crate::router::Router;
use crate::gossip::{POLKADOT_ENGINE_ID, RegisteredMessageValidator, MessageValidationData};
pub use polkadot_validation::Incoming;
use parity_codec::{Encode, Decode};
/// An executor suitable for dispatching async consensus tasks.
pub trait Executor {
fn spawn<F: Future<Item=(),Error=()> + Send + 'static>(&self, f: F);
}
/// A wrapped futures::future::Executor.
pub struct WrappedExecutor<T>(pub T);
impl<T> Executor for WrappedExecutor<T>
where T: FutureExecutor<Box<dyn Future<Item=(),Error=()> + Send + 'static>>
{
fn spawn<F: Future<Item=(),Error=()> + Send + 'static>(&self, f: F) {
if let Err(e) = self.0.execute(Box::new(f)) {
warn!(target: "validation", "could not spawn consensus task: {:?}", e);
}
}
}
impl Executor for TaskExecutor {
fn spawn<F: Future<Item=(),Error=()> + Send + 'static>(&self, f: F) {
TaskExecutor::spawn(self, f)
}
}
asynchronous rob
committed
/// A gossip network subservice.
pub trait GossipService {
fn send_message(&mut self, ctx: &mut dyn NetContext<Block>, who: &PeerId, message: ConsensusMessage);
asynchronous rob
committed
}
impl GossipService for consensus_gossip::ConsensusGossip<Block> {
fn send_message(&mut self, ctx: &mut dyn NetContext<Block>, who: &PeerId, message: ConsensusMessage) {
asynchronous rob
committed
consensus_gossip::ConsensusGossip::send_message(self, ctx, who, message)
}
}
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/// A stream of gossip messages and an optional sender for a topic.
pub struct GossipMessageStream {
topic_stream: mpsc::UnboundedReceiver<TopicNotification>,
}
impl GossipMessageStream {
/// Create a new instance with the given topic stream.
pub fn new(topic_stream: mpsc::UnboundedReceiver<TopicNotification>) -> Self {
Self {
topic_stream
}
}
}
impl Stream for GossipMessageStream {
type Item = (GossipMessage, Option<PeerId>);
type Error = ();
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
loop {
let msg = match futures::try_ready!(self.topic_stream.poll()) {
Some(msg) => msg,
None => return Ok(Async::Ready(None)),
};
debug!(target: "validation", "Processing statement for live validation session");
if let Some(gmsg) = GossipMessage::decode(&mut &msg.message[..]) {
return Ok(Async::Ready(Some((gmsg, msg.sender))))
}
}
}
}
/// Basic functionality that a network has to fulfill.
pub trait NetworkService: Send + Sync + 'static {
/// Get a stream of gossip messages for a given hash.
fn gossip_messages_for(&self, topic: Hash) -> GossipMessageStream;
/// Gossip a message on given topic.
fn gossip_message(&self, topic: Hash, message: GossipMessage);
asynchronous rob
committed
/// Execute a closure with the gossip service.
fn with_gossip<F: Send + 'static>(&self, with: F)
where F: FnOnce(&mut dyn GossipService, &mut dyn NetContext<Block>);
/// Execute a closure with the polkadot protocol.
fn with_spec<F: Send + 'static>(&self, with: F)
where F: FnOnce(&mut PolkadotProtocol, &mut dyn NetContext<Block>);
}
impl NetworkService for super::NetworkService {
fn gossip_messages_for(&self, topic: Hash) -> GossipMessageStream {
let (tx, rx) = std::sync::mpsc::channel();
asynchronous rob
committed
super::NetworkService::with_gossip(self, move |gossip, _| {
let inner_rx = gossip.messages_for(POLKADOT_ENGINE_ID, topic);
let _ = tx.send(inner_rx);
});
let topic_stream = match rx.recv() {
Ok(rx) => rx,
Err(_) => mpsc::unbounded().1, // return empty channel.
};
GossipMessageStream::new(topic_stream)
fn gossip_message(&self, topic: Hash, message: GossipMessage) {
self.gossip_consensus_message(
topic,
POLKADOT_ENGINE_ID,
message.encode(),
GossipMessageRecipient::BroadcastToAll,
);
asynchronous rob
committed
fn with_gossip<F: Send + 'static>(&self, with: F)
where F: FnOnce(&mut dyn GossipService, &mut dyn NetContext<Block>)
asynchronous rob
committed
{
super::NetworkService::with_gossip(self, move |gossip, ctx| with(gossip, ctx))
}
fn with_spec<F: Send + 'static>(&self, with: F)
where F: FnOnce(&mut PolkadotProtocol, &mut dyn NetContext<Block>)
{
super::NetworkService::with_spec(self, with)
}
}
/// Params to a current validation session.
pub struct SessionParams {
/// The local session key.
pub local_session_key: Option<SessionKey>,
/// The parent hash.
pub parent_hash: Hash,
/// The authorities.
pub authorities: Vec<SessionKey>,
}
/// Wrapper around the network service
pub struct ValidationNetwork<P, E, N, T> {
api: Arc<P>,
message_validator: RegisteredMessageValidator,
}
impl<P, E, N, T> ValidationNetwork<P, E, N, T> {
/// Create a new consensus networking object.
pub fn new(
network: Arc<N>,
exit: E,
message_validator: RegisteredMessageValidator,
api: Arc<P>,
executor: T,
) -> Self {
ValidationNetwork { network, exit, message_validator, api, executor }
}
}
impl<P, E: Clone, N, T: Clone> Clone for ValidationNetwork<P, E, N, T> {
fn clone(&self) -> Self {
ValidationNetwork {
network: self.network.clone(),
exit: self.exit.clone(),
api: self.api.clone(),
message_validator: self.message_validator.clone(),
}
}
}
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
impl<P, E, N, T> ValidationNetwork<P, E, N, T> where
P: ProvideRuntimeApi + Send + Sync + 'static,
P::Api: ParachainHost<Block>,
E: Clone + Future<Item=(),Error=()> + Send + Sync + 'static,
N: NetworkService,
T: Clone + Executor + Send + Sync + 'static,
{
/// Instantiate session data fetcher at a parent hash.
///
/// If the used session key is new, it will be broadcast to peers.
/// If a validation session was already instantiated at this parent hash,
/// the underlying instance will be shared.
///
/// If there was already a validation session instantiated and a different
/// session key was set, then the new key will be ignored.
///
/// This implies that there can be multiple services intantiating validation
/// session instances safely, but they should all be coordinated on which session keys
/// are being used.
pub fn instantiate_session(&self, params: SessionParams)
-> oneshot::Receiver<SessionDataFetcher<P, E, N, T>>
{
let parent_hash = params.parent_hash;
let network = self.network.clone();
let api = self.api.clone();
let task_executor = self.executor.clone();
let exit = self.exit.clone();
let message_validator = self.message_validator.clone();
let index_mapping = params.authorities
.iter()
.enumerate()
.map(|(i, k)| (i as ValidatorIndex, k.clone()))
.collect();
let (tx, rx) = oneshot::channel();
asynchronous rob
committed
{
let message_validator = self.message_validator.clone();
let authorities = params.authorities.clone();
self.network.with_gossip(move |gossip, ctx| {
message_validator.note_session(
parent_hash,
MessageValidationData { authorities, index_mapping },
|peer_id, message| gossip.send_message(ctx, peer_id, message),
);
});
}
self.network.with_spec(move |spec, ctx| {
let session = spec.new_validation_session(ctx, params);
let _ = tx.send(SessionDataFetcher {
network,
api,
task_executor,
parent_hash,
knowledge: session.knowledge().clone(),
exit,
message_validator,
});
});
rx
}
}
/// A long-lived network which can create parachain statement routing processes on demand.
impl<P, E, N, T> ParachainNetwork for ValidationNetwork<P, E, N, T> where
P: ProvideRuntimeApi + Send + Sync + 'static,
P::Api: ParachainHost<Block>,
E: Clone + Future<Item=(),Error=()> + Send + Sync + 'static,
T: Clone + Executor + Send + Sync + 'static,
type Error = String;
type TableRouter = Router<P, E, N, T>;
type BuildTableRouter = Box<dyn Future<Item=Self::TableRouter, Error=String> + Send>;
fn communication_for(
&self,
table: Arc<SharedTable>,
) -> Self::BuildTableRouter {
let parent_hash = table.consensus_parent_hash().clone();
let local_session_key = table.session_key();
let build_fetcher = self.instantiate_session(SessionParams {
local_session_key: Some(local_session_key),
parent_hash,
authorities: authorities.to_vec(),
});
let message_validator = self.message_validator.clone();
let executor = self.executor.clone();
let work = build_fetcher
.map_err(|e| format!("{:?}", e))
.map(move |fetcher| {
let table_router = Router::new(
table,
fetcher,
message_validator,
let table_router_clone = table_router.clone();
let work = table_router.checked_statements()
.for_each(move |msg| { table_router_clone.import_statement(msg); Ok(()) });
executor.spawn(work);
table_router
});
Box::new(work)
}
}
/// Error when the network appears to be down.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NetworkDown;
/// A future that resolves when a collation is received.
pub struct AwaitingCollation {
outer: ::futures::sync::oneshot::Receiver<::futures::sync::oneshot::Receiver<Collation>>,
inner: Option<::futures::sync::oneshot::Receiver<Collation>>
}
impl Future for AwaitingCollation {
type Item = Collation;
type Error = NetworkDown;
fn poll(&mut self) -> Poll<Collation, NetworkDown> {
if let Some(ref mut inner) = self.inner {
return inner
.poll()
.map_err(|_| NetworkDown)
}
match self.outer.poll() {
Ok(futures::Async::Ready(inner)) => {
self.inner = Some(inner);
self.poll()
},
Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
Err(_) => Err(NetworkDown)
impl<P, E: Clone, N, T: Clone> Collators for ValidationNetwork<P, E, N, T> where
P: ProvideRuntimeApi + Send + Sync + 'static,
P::Api: ParachainHost<Block>,
N: NetworkService,
type Error = NetworkDown;
type Collation = AwaitingCollation;
fn collate(&self, parachain: ParaId, relay_parent: Hash) -> Self::Collation {
let (tx, rx) = ::futures::sync::oneshot::channel();
self.network.with_spec(move |spec, _| {
let collation = spec.await_collation(relay_parent, parachain);
let _ = tx.send(collation);
});
AwaitingCollation{outer: rx, inner: None}
}
fn note_bad_collator(&self, collator: CollatorId) {
self.network.with_spec(move |spec, ctx| spec.disconnect_bad_collator(ctx, collator));
}
#[derive(Default)]
struct KnowledgeEntry {
knows_block_data: Vec<ValidatorId>,
knows_extrinsic: Vec<ValidatorId>,
asynchronous rob
committed
pov: Option<PoVBlock>,
extrinsic: Option<Extrinsic>,
}
/// Tracks knowledge of peers.
pub(crate) struct Knowledge {
candidates: HashMap<Hash, KnowledgeEntry>,
}
impl Knowledge {
/// Create a new knowledge instance.
pub(crate) fn new() -> Self {
Knowledge {
candidates: HashMap::new(),
}
}
/// Note a statement seen from another validator.
pub(crate) fn note_statement(&mut self, from: ValidatorId, statement: &Statement) {
// those proposing the candidate or declaring it valid know everything.
// those claiming it invalid do not have the extrinsic data as it is
// generated by valid execution.
match *statement {
GenericStatement::Candidate(ref c) => {
let entry = self.candidates.entry(c.hash()).or_insert_with(Default::default);
entry.knows_extrinsic.push(from);
}
GenericStatement::Valid(ref hash) => {
let entry = self.candidates.entry(*hash).or_insert_with(Default::default);
entry.knows_extrinsic.push(from);
}
GenericStatement::Invalid(ref hash) => self.candidates.entry(*hash)
.or_insert_with(Default::default)
.knows_block_data
.push(from),
}
}
/// Note a candidate collated or seen locally.
asynchronous rob
committed
pub(crate) fn note_candidate(&mut self, hash: Hash, pov: Option<PoVBlock>, extrinsic: Option<Extrinsic>) {
let entry = self.candidates.entry(hash).or_insert_with(Default::default);
asynchronous rob
committed
entry.pov = entry.pov.take().or(pov);
entry.extrinsic = entry.extrinsic.take().or(extrinsic);
}
}
/// receiver for incoming data.
#[derive(Clone)]
pub struct IncomingReceiver {
inner: future::Shared<Receiver<Incoming>>
}
impl Future for IncomingReceiver {
type Item = Incoming;
type Error = io::Error;
fn poll(&mut self) -> Poll<Incoming, io::Error> {
match self.inner.poll() {
Ok(Async::NotReady) => Ok(Async::NotReady),
Ok(Async::Ready(i)) => Ok(Async::Ready(Incoming::clone(&*i))),
Err(_) => Err(io::Error::new(
io::ErrorKind::Other,
"Sending end of channel hung up",
)),
}
}
}
/// A current validation session instance.
pub(crate) struct ValidationSession {
knowledge: Arc<Mutex<Knowledge>>,
local_session_key: Option<ValidatorId>,
}
impl ValidationSession {
/// Create a new validation session instance. Needs to be attached to the
pub(crate) fn new(params: SessionParams) -> Self {
ValidationSession {
parent_hash: params.parent_hash,
knowledge: Arc::new(Mutex::new(Knowledge::new())),
local_session_key: params.local_session_key,
}
}
/// Get a handle to the shared knowledge relative to this consensus
/// instance.
pub(crate) fn knowledge(&self) -> &Arc<Mutex<Knowledge>> {
&self.knowledge
}
asynchronous rob
committed
// execute a closure with locally stored proof-of-validation for a candidate, or a slice of session identities
// we believe should have the data.
asynchronous rob
committed
fn with_pov_block<F, U>(&self, hash: &Hash, f: F) -> U
where F: FnOnce(Result<&PoVBlock, &[ValidatorId]>) -> U
{
let knowledge = self.knowledge.lock();
let res = knowledge.candidates.get(hash)
.ok_or(&[] as &_)
asynchronous rob
committed
.and_then(|entry| entry.pov.as_ref().ok_or(&entry.knows_block_data[..]));
f(res)
}
}
// 3 is chosen because sessions change infrequently and usually
// only the last 2 (current session and "last" session) are relevant.
// the extra is an error boundary.
const RECENT_SESSIONS: usize = 3;
/// Result when inserting recent session key.
#[derive(PartialEq, Eq)]
pub(crate) enum InsertedRecentKey {
/// Key was already known.
AlreadyKnown,
/// Key was new and pushed out optional old item.
}
/// Wrapper for managing recent session keys.
#[derive(Default)]
pub(crate) struct RecentValidatorIds {
inner: ArrayVec<[ValidatorId; RECENT_SESSIONS]>,
}
/// Insert a new session key. This returns one to be pushed out if the
/// set is full.
pub(crate) fn insert(&mut self, key: ValidatorId) -> InsertedRecentKey {
if self.inner.contains(&key) { return InsertedRecentKey::AlreadyKnown }
let old = if self.inner.len() == RECENT_SESSIONS {
Some(self.inner.remove(0))
} else {
None
};
self.inner.push(key);
InsertedRecentKey::New(old)
}
/// As a slice.
pub(crate) fn as_slice(&self) -> &[ValidatorId] {
&*self.inner
}
self.inner.retain(|k| k != key)
}
}
/// Manages requests and keys for live validation session instances.
pub(crate) struct LiveValidationSessions {
// recent local session keys.
// live validation session instances, on `parent_hash`. refcount retained alongside.
live_instances: HashMap<Hash, (usize, ValidationSession)>,
}
impl LiveValidationSessions {
/// Create a new `LiveValidationSessions`
pub(crate) fn new() -> Self {
LiveValidationSessions {
recent: Default::default(),
live_instances: HashMap::new(),
}
}
/// Note new validation session. If the used session key is new,
/// it returns it to be broadcasted to peers.
///
/// If there was already a validation session instantiated and a different
/// session key was set, then the new key will be ignored.
pub(crate) fn new_validation_session(
&mut self,
params: SessionParams,
) -> (ValidationSession, Option<ValidatorId>) {
let parent_hash = params.parent_hash.clone();
let key = params.local_session_key.clone();
let recent = &mut self.recent;
let mut check_new_key = || {
let inserted_key = key.clone().map(|key| recent.insert(key));
if let Some(InsertedRecentKey::New(_)) = inserted_key {
key.clone()
} else {
None
}
};
if let Some(&mut (ref mut rc, ref mut prev)) = self.live_instances.get_mut(&parent_hash) {
let maybe_new = if prev.local_session_key.is_none() {
prev.local_session_key = key.clone();
check_new_key()
} else {
None
};
*rc += 1;
return (prev.clone(), maybe_new)
}
let session = ValidationSession::new(params);
self.live_instances.insert(parent_hash, (1, session.clone()));
(session, check_new_key())
}
/// Remove validation session. true indicates that it was actually removed.
pub(crate) fn remove(&mut self, parent_hash: Hash) -> bool {
let maybe_removed = if let Entry::Occupied(mut entry) = self.live_instances.entry(parent_hash) {
entry.get_mut().0 -= 1;
if entry.get().0 == 0 {
let (_, session) = entry.remove();
Some(session)
} else {
None
}
} else {
None
};
let session = match maybe_removed {
None => return false,
Some(s) => s,
};
if let Some(ref key) = session.local_session_key {
let key_still_used = self.live_instances.values()
.any(|c| c.1.local_session_key.as_ref() == Some(key));
if !key_still_used {
self.recent.remove(key)
}
}
}
/// Recent session keys as a slice.
pub(crate) fn recent_keys(&self) -> &[ValidatorId] {
self.recent.as_slice()
}
asynchronous rob
committed
/// Call a closure with pov-data from validation session at parent hash for a given
/// candidate-receipt hash.
///
/// This calls the closure with `Some(data)` where the session and data are live,
/// `Err(Some(keys))` when the session is live but the data unknown, with a list of keys
/// who have the data, and `Err(None)` where the session is unknown.
asynchronous rob
committed
pub(crate) fn with_pov_block<F, U>(&self, parent_hash: &Hash, c_hash: &Hash, f: F) -> U
where F: FnOnce(Result<&PoVBlock, Option<&[ValidatorId]>>) -> U
{
match self.live_instances.get(parent_hash) {
asynchronous rob
committed
Some(c) => c.1.with_pov_block(c_hash, |res| f(res.map_err(Some))),
None => f(Err(None))
}
}
}
/// Receiver for block data.
asynchronous rob
committed
pub struct PoVReceiver {
outer: Receiver<Receiver<PoVBlock>>,
inner: Option<Receiver<PoVBlock>>
asynchronous rob
committed
impl Future for PoVReceiver {
type Item = PoVBlock;
type Error = io::Error;
asynchronous rob
committed
fn poll(&mut self) -> Poll<PoVBlock, io::Error> {
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
let map_err = |_| io::Error::new(
io::ErrorKind::Other,
"Sending end of channel hung up",
);
if let Some(ref mut inner) = self.inner {
return inner.poll().map_err(map_err);
}
match self.outer.poll().map_err(map_err)? {
Async::Ready(inner) => {
self.inner = Some(inner);
self.poll()
}
Async::NotReady => Ok(Async::NotReady),
}
}
}
/// Can fetch data for a given validation session
pub struct SessionDataFetcher<P, E, N: NetworkService, T> {
network: Arc<N>,
api: Arc<P>,
exit: E,
task_executor: T,
knowledge: Arc<Mutex<Knowledge>>,
parent_hash: Hash,
message_validator: RegisteredMessageValidator,
}
impl<P, E, N: NetworkService, T> SessionDataFetcher<P, E, N, T> {
/// Get the parent hash.
pub(crate) fn parent_hash(&self) -> Hash {
self.parent_hash.clone()
}
/// Get the shared knowledge.
pub(crate) fn knowledge(&self) -> &Arc<Mutex<Knowledge>> {
&self.knowledge
}
/// Get the exit future.
pub(crate) fn exit(&self) -> &E {
&self.exit
}
/// Get the network service.
pub(crate) fn network(&self) -> &Arc<N> {
&self.network
}
/// Get the executor.
pub(crate) fn executor(&self) -> &T {
&self.task_executor
}
/// Get the runtime API.
pub(crate) fn api(&self) -> &Arc<P> {
&self.api
}
}
impl<P, E: Clone, N: NetworkService, T: Clone> Clone for SessionDataFetcher<P, E, N, T> {
fn clone(&self) -> Self {
SessionDataFetcher {
network: self.network.clone(),
api: self.api.clone(),
task_executor: self.task_executor.clone(),
parent_hash: self.parent_hash.clone(),
knowledge: self.knowledge.clone(),
exit: self.exit.clone(),
message_validator: self.message_validator.clone(),
}
}
}
impl<P: ProvideRuntimeApi + Send, E, N, T> SessionDataFetcher<P, E, N, T> where
P::Api: ParachainHost<Block>,
N: NetworkService,
T: Clone + Executor + Send + 'static,
E: Future<Item=(),Error=()> + Clone + Send + 'static,
{
asynchronous rob
committed
/// Fetch PoV block for the given candidate receipt.
pub fn fetch_pov_block(&self, candidate: &CandidateReceipt) -> PoVReceiver {
let parachain = candidate.parachain_index.clone();
let parent_hash = self.parent_hash;
asynchronous rob
committed
let canon_roots = self.api.runtime_api().ingress(&BlockId::hash(parent_hash), parachain)
.map_err(|e|
format!(
"Cannot fetch ingress for parachain {:?} at {:?}: {:?}",
parachain,
parent_hash,
e,
)
);
let candidate = candidate.clone();
let (tx, rx) = ::futures::sync::oneshot::channel();
self.network.with_spec(move |spec, ctx| {
asynchronous rob
committed
if let Ok(Some(canon_roots)) = canon_roots {
let inner_rx = spec.fetch_pov_block(ctx, &candidate, parent_hash, canon_roots);
let _ = tx.send(inner_rx);
}
asynchronous rob
committed
PoVReceiver { outer: rx, inner: None }
#[cfg(test)]
mod tests {
use super::*;
use substrate_primitives::crypto::UncheckedInto;
#[test]
fn last_keys_works() {
let a: ValidatorId = [1; 32].unchecked_into();
let b: ValidatorId = [2; 32].unchecked_into();
let c: ValidatorId = [3; 32].unchecked_into();
let d: ValidatorId = [4; 32].unchecked_into();
let mut recent = RecentValidatorIds::default();
InsertedRecentKey::New(None) => {},
_ => panic!("is new, not at capacity"),
}
InsertedRecentKey::AlreadyKnown => {},
_ => panic!("not new"),
}
InsertedRecentKey::New(None) => {},
_ => panic!("is new, not at capacity"),
}
match recent.insert(b) {
InsertedRecentKey::AlreadyKnown => {},
_ => panic!("not new"),
}
InsertedRecentKey::New(None) => {},
_ => panic!("is new, not at capacity"),
}
match recent.insert(c) {
InsertedRecentKey::AlreadyKnown => {},
_ => panic!("not new"),
}
InsertedRecentKey::New(Some(old)) => assert_eq!(old, a),
_ => panic!("is new, and at capacity"),
}
match recent.insert(d) {
InsertedRecentKey::AlreadyKnown => {},
_ => panic!("not new"),
}
}
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
#[test]
fn add_new_sessions_works() {
let mut live_sessions = LiveValidationSessions::new();
let key_a: ValidatorId = [0; 32].unchecked_into();
let key_b: ValidatorId = [1; 32].unchecked_into();
let parent_hash = [0xff; 32].into();
let (session, new_key) = live_sessions.new_validation_session(SessionParams {
parent_hash,
local_session_key: None,
authorities: Vec::new(),
});
let knowledge = session.knowledge().clone();
assert!(new_key.is_none());
let (session, new_key) = live_sessions.new_validation_session(SessionParams {
parent_hash,
local_session_key: Some(key_a.clone()),
authorities: Vec::new(),
});
// check that knowledge points to the same place.
assert_eq!(&**session.knowledge() as *const _, &*knowledge as *const _);
assert_eq!(new_key, Some(key_a.clone()));
let (session, new_key) = live_sessions.new_validation_session(SessionParams {
parent_hash,
local_session_key: Some(key_b.clone()),
authorities: Vec::new(),
});
assert_eq!(&**session.knowledge() as *const _, &*knowledge as *const _);
assert!(new_key.is_none());
}