mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 19:01:08 +00:00
grandpa: report equivocations (#3868)
* session: runtime api for generating session membership proofs * grandpa: add runtime api for creating equivocation report txs * grandpa: submit signed equivocation report transactions * grandpa: use proper equivocation report type * grandpa: report equivocations * grandpa: validate equivocation proof * grandpa: update to finality-grandpa 0.9.1 * grandpa: fix encoding of session membership proof * grandpa: initialize set id session mapping for genesis session * grandpa: fix bug in set_id session validation * fix compilation * cleanup from merge conflicts * cleanup crate tomls * grandpa: refactor equivocation handling to separate trait * node-template: fix compilation * fix test compilation * bump finality-grandpa to v0.10.2 * rpc: fix runtime version test * CHERRY-PICK #4200: Add documentation to SubmitSignedTransaction and actually make it work Squashed commit of the following: commit 4f2cb0b1c588a06f2f3b478bb4b28b5cb29d54b9 Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 16:29:33 2019 +0100 Split the method to avoid confusing type error message. commit c5bf24eeaaf902add89ed1b046b22c4a4aaeb2cd Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 16:19:55 2019 +0100 Make accounts optional, fix logic. commit 97db1ef556e023cf6847e5ffdb036c0e3ea6fb0a Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 10:06:20 2019 +0100 Remove warning. commit 535f5c116d1a2e826eaf90c3f7e6798e443d61d8 Merge: 5162572170f1a5f651Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 07:08:05 2019 +0100 Merge branch 'master' into td-signed-transactions commit 516257217bac89fcebd083712f4ea68b7b23b55a Merge: ac98248c62e68c80c2Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Dec 2 13:57:25 2019 +0100 Merge branch 'master' into td-signed-transactions commit ac98248c6c56cff381130645a82a13d29933cf83 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:34:52 2019 +0100 Forgotten import. commit 67a3c19031506c28e31c6bc4a90fff62d467dd58 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:32:10 2019 +0100 Fix naming and bounds. commit 93e768ea9df97a4629fca1f9bc4b108fdb33f876 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:01:05 2019 +0100 Add documentation to signed transactions and actually make them work. * grandpa: skip block initialization on report submission method * primitives: allow transaction pool access by default for offchain calls * grandpa: unused parameters * grandpa: remove unused method * grandpa: enable equivocation reporting * grandpa: add workaround for parameter encoding * grandpa: fix localized_payload calls in tests * fix submit_report_equivocation_extrinsic in runtimes * node: fix submit transaction test compilation * node: bump spec_version * rpc: fix api version test * grandpa: allow custom equivocation offence type * grandpa: add test for authorities::next_change_height * grandpa: cleanup report_equivocation function * node: move reporting app crypto to node-primitives * grandpa: move equivocation traits to own module * grandpa: rename app-crypto crate import * grandpa: export equivocation types * node: bump spec_version * grandpa: rename EquivocationReport to EquivocationProof * grandpa: add missing docs to primitives * grandpa: add missing docs to equivocation * node: fix compilation * grandpa: add missing docs to pallet * node: bump spec_version * fix whitespace * grandpa: return error on offence reporting * grandpa: expose session and validator count in proofs through traits * grandpa: use strong key in module KeyOwnerProofSystem * grandpa: move key ownership proof to grandpa runtime api * grandpa: remove unnecessary cloning when checking equivocation proof * grandpa: make report_equivocation a method in Environment * support: implement KeyOwnerProofSystem for () * grandpa: move KeyOwnerProofSystem to module trait * test-utils: fix runtime compilation * grandpa: fix test compilation * grandpa: fix test compilation after merge * grandpa: simplify transaction submission types * grandpa: validate equivocation report in signed extension * client: fix test * node: use ValidateEquivocationReport signed extension * grandpa: expose key ownership proof under opaque type * grandpa: better docs on key ownership proofs * grandpa: add note about signed extension * grandpa: add ValidateEquivocationReport::new * grandpa: remove skip_initialize_block from runtime api * grandpa: use new offchain transaction submission API * grandpa: take set_id in generate_key_ownership_proof * grandpa: update to finality-grandpa v0.12.2 * grandpa: cleanup usages of AuthoritySet::current * grandpa: fix test * grandpa: add mocking utilities for equivocation reporting * grandpa: add test for equivocation reporting * grandpa: move SetIdSession initialization * grandpa: add more tests * node: enable historical session manager * node: bump spec_version * node: use strong key types in KeyOwnerProofSystem definitions * grandpa: export GrandpaEquivocationOffence type
This commit is contained in:
@@ -110,8 +110,11 @@ impl ExecutionContext {
|
||||
match self {
|
||||
Importing | Syncing | BlockConstruction =>
|
||||
offchain::Capabilities::none(),
|
||||
// Enable keystore by default for offchain calls. CC @bkchr
|
||||
OffchainCall(None) => [offchain::Capability::Keystore][..].into(),
|
||||
// Enable keystore and transaction pool by default for offchain calls.
|
||||
OffchainCall(None) => [
|
||||
offchain::Capability::Keystore,
|
||||
offchain::Capability::TransactionPool,
|
||||
][..].into(),
|
||||
OffchainCall(Some((_, capabilities))) => *capabilities,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,24 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
[dependencies]
|
||||
sp-application-crypto = { version = "2.0.0-dev", default-features = false, path = "../application-crypto" }
|
||||
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
|
||||
sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" }
|
||||
grandpa = { package = "finality-grandpa", version = "0.12.2", default-features = false, features = ["derive-codec"] }
|
||||
log = { version = "0.4.8", optional = true }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
sp-api = { version = "2.0.0-dev", default-features = false, path = "../api" }
|
||||
sp-core = { version = "2.0.0-dev", default-features = false, path = "../core" }
|
||||
sp-runtime = { version = "2.0.0-dev", default-features = false, path = "../runtime" }
|
||||
sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"sp-application-crypto/std",
|
||||
"codec/std",
|
||||
"sp-std/std",
|
||||
"grandpa/std",
|
||||
"log",
|
||||
"serde",
|
||||
"sp-api/std",
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
|
||||
@@ -23,11 +23,18 @@ extern crate alloc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::Serialize;
|
||||
|
||||
use codec::{Encode, Decode, Input, Codec};
|
||||
use sp_runtime::{ConsensusEngineId, RuntimeDebug};
|
||||
use sp_runtime::{ConsensusEngineId, RuntimeDebug, traits::NumberFor};
|
||||
use sp_std::borrow::Cow;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use log::debug;
|
||||
|
||||
/// Key type for GRANDPA module.
|
||||
pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::GRANDPA;
|
||||
|
||||
mod app {
|
||||
use sp_application_crypto::{app_crypto, key_types::GRANDPA, ed25519};
|
||||
app_crypto!(ed25519, GRANDPA);
|
||||
@@ -157,6 +164,242 @@ impl<N: Codec> ConsensusLog<N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Proof of voter misbehavior on a given set id. Misbehavior/equivocation in
|
||||
/// GRANDPA happens when a voter votes on the same round (either at prevote or
|
||||
/// precommit stage) for different blocks. Proving is achieved by collecting the
|
||||
/// signed messages of conflicting votes.
|
||||
#[derive(Clone, Debug, Decode, Encode, PartialEq)]
|
||||
pub struct EquivocationProof<H, N> {
|
||||
set_id: SetId,
|
||||
equivocation: Equivocation<H, N>,
|
||||
}
|
||||
|
||||
impl<H, N> EquivocationProof<H, N> {
|
||||
/// Create a new `EquivocationProof` for the given set id and using the
|
||||
/// given equivocation as proof.
|
||||
pub fn new(set_id: SetId, equivocation: Equivocation<H, N>) -> Self {
|
||||
EquivocationProof {
|
||||
set_id,
|
||||
equivocation,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the set id at which the equivocation occurred.
|
||||
pub fn set_id(&self) -> SetId {
|
||||
self.set_id
|
||||
}
|
||||
|
||||
/// Returns the round number at which the equivocation occurred.
|
||||
pub fn round(&self) -> RoundNumber {
|
||||
match self.equivocation {
|
||||
Equivocation::Prevote(ref equivocation) => equivocation.round_number,
|
||||
Equivocation::Precommit(ref equivocation) => equivocation.round_number,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the authority id of the equivocator.
|
||||
pub fn offender(&self) -> &AuthorityId {
|
||||
self.equivocation.offender()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper object for GRANDPA equivocation proofs, useful for unifying prevote
|
||||
/// and precommit equivocations under a common type.
|
||||
#[derive(Clone, Debug, Decode, Encode, PartialEq)]
|
||||
pub enum Equivocation<H, N> {
|
||||
/// Proof of equivocation at prevote stage.
|
||||
Prevote(grandpa::Equivocation<AuthorityId, grandpa::Prevote<H, N>, AuthoritySignature>),
|
||||
/// Proof of equivocation at precommit stage.
|
||||
Precommit(grandpa::Equivocation<AuthorityId, grandpa::Precommit<H, N>, AuthoritySignature>),
|
||||
}
|
||||
|
||||
impl<H, N> From<grandpa::Equivocation<AuthorityId, grandpa::Prevote<H, N>, AuthoritySignature>>
|
||||
for Equivocation<H, N>
|
||||
{
|
||||
fn from(
|
||||
equivocation: grandpa::Equivocation<
|
||||
AuthorityId,
|
||||
grandpa::Prevote<H, N>,
|
||||
AuthoritySignature,
|
||||
>,
|
||||
) -> Self {
|
||||
Equivocation::Prevote(equivocation)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, N> From<grandpa::Equivocation<AuthorityId, grandpa::Precommit<H, N>, AuthoritySignature>>
|
||||
for Equivocation<H, N>
|
||||
{
|
||||
fn from(
|
||||
equivocation: grandpa::Equivocation<
|
||||
AuthorityId,
|
||||
grandpa::Precommit<H, N>,
|
||||
AuthoritySignature,
|
||||
>,
|
||||
) -> Self {
|
||||
Equivocation::Precommit(equivocation)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, N> Equivocation<H, N> {
|
||||
/// Returns the authority id of the equivocator.
|
||||
pub fn offender(&self) -> &AuthorityId {
|
||||
match self {
|
||||
Equivocation::Prevote(ref equivocation) => &equivocation.identity,
|
||||
Equivocation::Precommit(ref equivocation) => &equivocation.identity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the equivocation proof by making sure that both votes target
|
||||
/// different blocks and that its signatures are valid.
|
||||
pub fn check_equivocation_proof<H, N>(report: EquivocationProof<H, N>) -> Result<(), ()>
|
||||
where
|
||||
H: Clone + Encode + PartialEq,
|
||||
N: Clone + Encode + PartialEq,
|
||||
{
|
||||
// NOTE: the bare `Prevote` and `Precommit` types don't share any trait,
|
||||
// this is implemented as a macro to avoid duplication.
|
||||
macro_rules! check {
|
||||
( $equivocation:expr, $message:expr ) => {
|
||||
// if both votes have the same target the equivocation is invalid.
|
||||
if $equivocation.first.0.target_hash == $equivocation.second.0.target_hash &&
|
||||
$equivocation.first.0.target_number == $equivocation.second.0.target_number
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// check signatures on both votes are valid
|
||||
check_message_signature(
|
||||
&$message($equivocation.first.0),
|
||||
&$equivocation.identity,
|
||||
&$equivocation.first.1,
|
||||
$equivocation.round_number,
|
||||
report.set_id,
|
||||
)?;
|
||||
|
||||
check_message_signature(
|
||||
&$message($equivocation.second.0),
|
||||
&$equivocation.identity,
|
||||
&$equivocation.second.1,
|
||||
$equivocation.round_number,
|
||||
report.set_id,
|
||||
)?;
|
||||
|
||||
return Ok(());
|
||||
};
|
||||
}
|
||||
|
||||
match report.equivocation {
|
||||
Equivocation::Prevote(equivocation) => {
|
||||
check!(equivocation, grandpa::Message::Prevote);
|
||||
}
|
||||
Equivocation::Precommit(equivocation) => {
|
||||
check!(equivocation, grandpa::Message::Precommit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode round message localized to a given round and set id.
|
||||
pub fn localized_payload<E: Encode>(round: RoundNumber, set_id: SetId, message: &E) -> Vec<u8> {
|
||||
let mut buf = Vec::new();
|
||||
localized_payload_with_buffer(round, set_id, message, &mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Encode round message localized to a given round and set id using the given
|
||||
/// buffer. The given buffer will be cleared and the resulting encoded payload
|
||||
/// will always be written to the start of the buffer.
|
||||
pub fn localized_payload_with_buffer<E: Encode>(
|
||||
round: RoundNumber,
|
||||
set_id: SetId,
|
||||
message: &E,
|
||||
buf: &mut Vec<u8>,
|
||||
) {
|
||||
buf.clear();
|
||||
(message, round, set_id).encode_to(buf)
|
||||
}
|
||||
|
||||
/// Check a message signature by encoding the message as a localized payload and
|
||||
/// verifying the provided signature using the expected authority id.
|
||||
pub fn check_message_signature<H, N>(
|
||||
message: &grandpa::Message<H, N>,
|
||||
id: &AuthorityId,
|
||||
signature: &AuthoritySignature,
|
||||
round: RoundNumber,
|
||||
set_id: SetId,
|
||||
) -> Result<(), ()>
|
||||
where
|
||||
H: Encode,
|
||||
N: Encode,
|
||||
{
|
||||
check_message_signature_with_buffer(message, id, signature, round, set_id, &mut Vec::new())
|
||||
}
|
||||
|
||||
/// Check a message signature by encoding the message as a localized payload and
|
||||
/// verifying the provided signature using the expected authority id.
|
||||
/// The encoding necessary to verify the signature will be done using the given
|
||||
/// buffer, the original content of the buffer will be cleared.
|
||||
pub fn check_message_signature_with_buffer<H, N>(
|
||||
message: &grandpa::Message<H, N>,
|
||||
id: &AuthorityId,
|
||||
signature: &AuthoritySignature,
|
||||
round: RoundNumber,
|
||||
set_id: SetId,
|
||||
buf: &mut Vec<u8>,
|
||||
) -> Result<(), ()>
|
||||
where
|
||||
H: Encode,
|
||||
N: Encode,
|
||||
{
|
||||
localized_payload_with_buffer(round, set_id, message, buf);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let verify = || {
|
||||
use sp_application_crypto::RuntimeAppPublic;
|
||||
id.verify(&buf, signature)
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
let verify = || {
|
||||
use sp_application_crypto::Pair;
|
||||
AuthorityPair::verify(signature, &buf, &id)
|
||||
};
|
||||
|
||||
if verify() {
|
||||
Ok(())
|
||||
} else {
|
||||
#[cfg(feature = "std")]
|
||||
debug!(target: "afg", "Bad signature on message from {:?}", id);
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Localizes the message to the given set and round and signs the payload.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn sign_message<H, N>(
|
||||
message: grandpa::Message<H, N>,
|
||||
pair: &AuthorityPair,
|
||||
round: RoundNumber,
|
||||
set_id: SetId,
|
||||
) -> grandpa::SignedMessage<H, N, AuthoritySignature, AuthorityId>
|
||||
where
|
||||
H: Encode,
|
||||
N: Encode,
|
||||
{
|
||||
use sp_core::Pair;
|
||||
|
||||
let encoded = localized_payload(round, set_id, &message);
|
||||
let signature = pair.sign(&encoded[..]);
|
||||
|
||||
grandpa::SignedMessage {
|
||||
message,
|
||||
signature,
|
||||
id: pair.public(),
|
||||
}
|
||||
}
|
||||
|
||||
/// WASM function call to check for pending changes.
|
||||
pub const PENDING_CHANGE_CALL: &str = "grandpa_pending_change";
|
||||
/// WASM function call to get current GRANDPA authorities.
|
||||
@@ -211,6 +454,29 @@ impl<'a> Decode for VersionedAuthorityList<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque type used to represent the key ownership proof at the runtime API
|
||||
/// boundary. The inner value is an encoded representation of the actual key
|
||||
/// ownership proof which will be parameterized when defining the runtime. At
|
||||
/// the runtime API boundary this type is unknown and as such we keep this
|
||||
/// opaque representation, implementors of the runtime API will have to make
|
||||
/// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type.
|
||||
#[derive(Decode, Encode, PartialEq)]
|
||||
pub struct OpaqueKeyOwnershipProof(Vec<u8>);
|
||||
|
||||
impl OpaqueKeyOwnershipProof {
|
||||
/// Create a new `OpaqueKeyOwnershipProof` using the given encoded
|
||||
/// representation.
|
||||
pub fn new(inner: Vec<u8>) -> OpaqueKeyOwnershipProof {
|
||||
OpaqueKeyOwnershipProof(inner)
|
||||
}
|
||||
|
||||
/// Try to decode this `OpaqueKeyOwnershipProof` into the given concrete key
|
||||
/// ownership proof type.
|
||||
pub fn decode<T: Decode>(self) -> Option<T> {
|
||||
codec::Decode::decode(&mut &self.0[..]).ok()
|
||||
}
|
||||
}
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
/// APIs for integrating the GRANDPA finality gadget into runtimes.
|
||||
/// This should be implemented on the runtime side.
|
||||
@@ -230,5 +496,32 @@ sp_api::decl_runtime_apis! {
|
||||
/// used to finalize descendants of this block (B+1, B+2, ...). The block B itself
|
||||
/// is finalized by the authorities from block B-1.
|
||||
fn grandpa_authorities() -> AuthorityList;
|
||||
|
||||
/// Submits an extrinsic to report an equivocation. The caller must
|
||||
/// provide the equivocation proof and a key ownership proof (should be
|
||||
/// obtained using `generate_key_ownership_proof`). This method will
|
||||
/// sign the extrinsic with any reporting keys available in the keystore
|
||||
/// and will push the transaction to the pool.
|
||||
/// Only useful in an offchain context.
|
||||
fn submit_report_equivocation_extrinsic(
|
||||
equivocation_proof: EquivocationProof<Block::Hash, NumberFor<Block>>,
|
||||
key_owner_proof: OpaqueKeyOwnershipProof,
|
||||
) -> Option<()>;
|
||||
|
||||
/// Generates a proof of key ownership for the given authority in the
|
||||
/// given set. An example usage of this module is coupled with the
|
||||
/// session historical module to prove that a given authority key is
|
||||
/// tied to a given staking identity during a specific session. Proofs
|
||||
/// of key ownership are necessary for submitting equivocation reports.
|
||||
/// NOTE: even though the API takes a `set_id` as parameter the current
|
||||
/// implementations ignore this parameter and instead rely on this
|
||||
/// method being called at the correct block height, i.e. any point at
|
||||
/// which the given set id is live on-chain. Future implementations will
|
||||
/// instead use indexed data through an offchain worker, not requiring
|
||||
/// older states to be available.
|
||||
fn generate_key_ownership_proof(
|
||||
set_id: SetId,
|
||||
authority_id: AuthorityId,
|
||||
) -> Option<OpaqueKeyOwnershipProof>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,20 @@ description = "Primitives for sessions"
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
|
||||
sp-api = { version = "2.0.0-dev", default-features = false, path = "../api" }
|
||||
sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" }
|
||||
sp-core = { version = "2.0.0-dev", default-features = false, path = "../core" }
|
||||
sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" }
|
||||
sp-staking = { version = "2.0.0-dev", default-features = false, path = "../staking" }
|
||||
sp-runtime = { version = "2.0.0-dev", optional = true, path = "../runtime" }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [ "sp-api/std", "sp-std/std", "sp-runtime", "sp-core/std" ]
|
||||
std = [
|
||||
"codec/std",
|
||||
"sp-api/std",
|
||||
"sp-core/std",
|
||||
"sp-std/std",
|
||||
"sp-staking/std",
|
||||
"sp-runtime/std",
|
||||
]
|
||||
|
||||
@@ -18,14 +18,17 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
|
||||
use sp_core::RuntimeDebug;
|
||||
use sp_core::crypto::KeyTypeId;
|
||||
use sp_staking::SessionIndex;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
/// Session keys runtime api.
|
||||
@@ -46,6 +49,32 @@ sp_api::decl_runtime_apis! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of validators in a given session.
|
||||
pub type ValidatorCount = u32;
|
||||
|
||||
/// Proof of membership of a specific key in a given session.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, RuntimeDebug)]
|
||||
pub struct MembershipProof {
|
||||
/// The session index on which the specific key is a member.
|
||||
pub session: SessionIndex,
|
||||
/// Trie nodes of a merkle proof of session membership.
|
||||
pub trie_nodes: Vec<Vec<u8>>,
|
||||
/// The validator count of the session on which the specific key is a member.
|
||||
pub validator_count: ValidatorCount,
|
||||
}
|
||||
|
||||
impl MembershipProof {
|
||||
/// Returns a session this proof was generated for.
|
||||
pub fn session(&self) -> SessionIndex {
|
||||
self.session
|
||||
}
|
||||
|
||||
/// Returns the validator count of the session this proof was generated for.
|
||||
pub fn validator_count(&self) -> ValidatorCount {
|
||||
self.validator_count
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the initial session keys with the given seeds, at the given block and store them in
|
||||
/// the client's keystore.
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
Reference in New Issue
Block a user