mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 03:47:57 +00:00
c6f07d8f31
* Indentation fix. * Prepare request-response for PoV fetching. * Drop old PoV distribution. * WIP: Fetch PoV directly from backing. * Backing compiles. * Runtime access and connection management for PoV distribution. * Get rid of seemingly dead code. * Implement PoV fetching. Backing does not yet use it. * Don't send `ConnectToValidators` for empty list. * Even better - no need to check over and over again. * PoV fetching implemented. + Typechecks + Should work Missing: - Guide - Tests - Do fallback fetching in case fetching from seconding validator fails. * Check PoV hash upon reception. * Implement retry of PoV fetching in backing. * Avoid pointless validation spawning. * Add jaeger span to pov requesting. * Add back tracing. * Review remarks. * Whitespace. * Whitespace again. * Cleanup + fix tests. * Log to log target in overseer. * Fix more tests. * Don't fail if group cannot be found. * Simple test for PoV fetcher. * Handle missing group membership better. * Add test for retry functionality. * Fix flaky test. * Spaces again. * Guide updates. * Spaces.
198 lines
5.7 KiB
Rust
198 lines
5.7 KiB
Rust
// Copyright 2021 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/>.
|
|
|
|
//! Convenient interface to the runtime.
|
|
|
|
use lru::LruCache;
|
|
|
|
use sp_application_crypto::AppKey;
|
|
use sp_core::crypto::Public;
|
|
use sp_keystore::{CryptoStore, SyncCryptoStorePtr};
|
|
|
|
use polkadot_node_subsystem_util::{
|
|
request_session_index_for_child_ctx, request_session_info_ctx,
|
|
};
|
|
use polkadot_primitives::v1::{GroupIndex, Hash, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex};
|
|
use polkadot_subsystem::SubsystemContext;
|
|
|
|
use super::{
|
|
error::recv_runtime,
|
|
Error,
|
|
};
|
|
|
|
/// Caching of session info as needed by availability distribution.
|
|
///
|
|
/// It should be ensured that a cached session stays live in the cache as long as we might need it.
|
|
pub struct Runtime {
|
|
/// Get the session index for a given relay parent.
|
|
///
|
|
/// We query this up to a 100 times per block, so caching it here without roundtrips over the
|
|
/// overseer seems sensible.
|
|
session_index_cache: LruCache<Hash, SessionIndex>,
|
|
|
|
/// Look up cached sessions by SessionIndex.
|
|
session_info_cache: LruCache<SessionIndex, ExtendedSessionInfo>,
|
|
|
|
/// Key store for determining whether we are a validator and what `ValidatorIndex` we have.
|
|
keystore: SyncCryptoStorePtr,
|
|
}
|
|
|
|
/// SessionInfo with additional useful data for validator nodes.
|
|
pub struct ExtendedSessionInfo {
|
|
/// Actual session info as fetched from the runtime.
|
|
pub session_info: SessionInfo,
|
|
/// Contains useful information about ourselves, in case this node is a validator.
|
|
pub validator_info: ValidatorInfo,
|
|
}
|
|
|
|
/// Information about ourself, in case we are an `Authority`.
|
|
///
|
|
/// This data is derived from the `SessionInfo` and our key as found in the keystore.
|
|
pub struct ValidatorInfo {
|
|
/// The index this very validator has in `SessionInfo` vectors, if any.
|
|
pub our_index: Option<ValidatorIndex>,
|
|
/// The group we belong to, if any.
|
|
pub our_group: Option<GroupIndex>,
|
|
}
|
|
|
|
impl Runtime {
|
|
/// Create a new `Runtime` for convenient runtime fetches.
|
|
pub fn new(keystore: SyncCryptoStorePtr) -> Self {
|
|
Self {
|
|
// 5 relatively conservative, 1 to 2 should suffice:
|
|
session_index_cache: LruCache::new(5),
|
|
// We need to cache the current and the last session the most:
|
|
session_info_cache: LruCache::new(2),
|
|
keystore,
|
|
}
|
|
}
|
|
|
|
/// Retrieve the current session index.
|
|
pub async fn get_session_index<Context>(
|
|
&mut self,
|
|
ctx: &mut Context,
|
|
parent: Hash,
|
|
) -> Result<SessionIndex, Error>
|
|
where
|
|
Context: SubsystemContext,
|
|
{
|
|
match self.session_index_cache.get(&parent) {
|
|
Some(index) => Ok(*index),
|
|
None => {
|
|
let index =
|
|
recv_runtime(request_session_index_for_child_ctx(parent, ctx).await)
|
|
.await?;
|
|
self.session_index_cache.put(parent, index);
|
|
Ok(index)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get `ExtendedSessionInfo` by relay parent hash.
|
|
pub async fn get_session_info<'a, Context>(
|
|
&'a mut self,
|
|
ctx: &mut Context,
|
|
parent: Hash,
|
|
) -> Result<&'a ExtendedSessionInfo, Error>
|
|
where
|
|
Context: SubsystemContext,
|
|
{
|
|
let session_index = self.get_session_index(ctx, parent).await?;
|
|
|
|
self.get_session_info_by_index(ctx, parent, session_index).await
|
|
}
|
|
|
|
/// Get `ExtendedSessionInfo` by session index.
|
|
///
|
|
/// `request_session_info_ctx` still requires the parent to be passed in, so we take the parent
|
|
/// in addition to the `SessionIndex`.
|
|
pub async fn get_session_info_by_index<'a, Context>(
|
|
&'a mut self,
|
|
ctx: &mut Context,
|
|
parent: Hash,
|
|
session_index: SessionIndex,
|
|
) -> Result<&'a ExtendedSessionInfo, Error>
|
|
where
|
|
Context: SubsystemContext,
|
|
{
|
|
if !self.session_info_cache.contains(&session_index) {
|
|
let session_info =
|
|
recv_runtime(request_session_info_ctx(parent, session_index, ctx).await)
|
|
.await?
|
|
.ok_or(Error::NoSuchSession(session_index))?;
|
|
let validator_info = self.get_validator_info(&session_info).await?;
|
|
|
|
let full_info = ExtendedSessionInfo {
|
|
session_info,
|
|
validator_info,
|
|
};
|
|
|
|
self.session_info_cache.put(session_index, full_info);
|
|
}
|
|
Ok(
|
|
self.session_info_cache.get(&session_index)
|
|
.expect("We just put the value there. qed.")
|
|
)
|
|
}
|
|
|
|
/// Build `ValidatorInfo` for the current session.
|
|
///
|
|
///
|
|
/// Returns: `None` if not a validator.
|
|
async fn get_validator_info(
|
|
&self,
|
|
session_info: &SessionInfo,
|
|
) -> Result<ValidatorInfo, Error>
|
|
{
|
|
if let Some(our_index) = self.get_our_index(&session_info.validators).await {
|
|
// Get our group index:
|
|
let our_group = session_info.validator_groups
|
|
.iter()
|
|
.enumerate()
|
|
.find_map(|(i, g)| {
|
|
g.iter().find_map(|v| {
|
|
if *v == our_index {
|
|
Some(GroupIndex(i as u32))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
);
|
|
let info = ValidatorInfo {
|
|
our_index: Some(our_index),
|
|
our_group,
|
|
};
|
|
return Ok(info)
|
|
}
|
|
return Ok(ValidatorInfo { our_index: None, our_group: None })
|
|
}
|
|
|
|
/// Get our `ValidatorIndex`.
|
|
///
|
|
/// Returns: None if we are not a validator.
|
|
async fn get_our_index(&self, validators: &[ValidatorId]) -> Option<ValidatorIndex> {
|
|
for (i, v) in validators.iter().enumerate() {
|
|
if CryptoStore::has_keys(&*self.keystore, &[(v.to_raw_vec(), ValidatorId::ID)])
|
|
.await
|
|
{
|
|
return Some(ValidatorIndex(i as u32));
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|