feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,57 @@
[package]
name = "pezsc-consensus-grandpa-rpc"
version = "0.19.0"
authors.workspace = true
description = "RPC extensions for the GRANDPA finality gadget"
repository.workspace = true
edition.workspace = true
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
readme = "README.md"
homepage.workspace = true
[lints]
workspace = true
[dependencies]
codec = { features = ["derive"], workspace = true, default-features = true }
finality-grandpa = { features = [
"derive-codec",
], workspace = true, default-features = true }
futures = { workspace = true }
jsonrpsee = { features = [
"client-core",
"macros",
"server-core",
], workspace = true }
log = { workspace = true, default-features = true }
pezsc-client-api = { workspace = true, default-features = true }
pezsc-consensus-grandpa = { workspace = true, default-features = true }
pezsc-rpc = { workspace = true, default-features = true }
serde = { features = ["derive"], workspace = true, default-features = true }
pezsp-blockchain = { workspace = true, default-features = true }
pezsp-core = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
thiserror = { workspace = true }
[dev-dependencies]
pezsc-block-builder = { workspace = true, default-features = true }
pezsc-rpc = { features = [
"test-helpers",
], workspace = true, default-features = true }
pezsp-consensus-grandpa = { workspace = true, default-features = true }
pezsp-keyring = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
tokio = { features = ["macros"], workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"pezsc-block-builder/runtime-benchmarks",
"pezsc-client-api/runtime-benchmarks",
"pezsc-consensus-grandpa/runtime-benchmarks",
"pezsc-rpc/runtime-benchmarks",
"pezsp-blockchain/runtime-benchmarks",
"pezsp-consensus-grandpa/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
]
@@ -0,0 +1,3 @@
RPC API for GRANDPA.
License: GPL-3.0-or-later WITH Classpath-exception-2.0
@@ -0,0 +1,73 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use jsonrpsee::types::error::{ErrorObject, ErrorObjectOwned};
#[derive(Debug, thiserror::Error)]
/// Top-level error type for the RPC handler
pub enum Error {
/// The GRANDPA RPC endpoint is not ready.
#[error("GRANDPA RPC endpoint not ready")]
EndpointNotReady,
/// GRANDPA reports the authority set id to be larger than 32-bits.
#[error("GRANDPA reports authority set id unreasonably large")]
AuthoritySetIdReportedAsUnreasonablyLarge,
/// GRANDPA reports voter state with round id or weights larger than 32-bits.
#[error("GRANDPA reports voter state as unreasonably large")]
VoterStateReportsUnreasonablyLargeNumbers,
/// GRANDPA prove finality failed.
#[error("GRANDPA prove finality rpc failed: {0}")]
ProveFinalityFailed(#[from] pezsc_consensus_grandpa::FinalityProofError),
}
/// The error codes returned by jsonrpc.
pub enum ErrorCode {
/// Returned when Grandpa RPC endpoint is not ready.
NotReady = 1,
/// Authority set ID is larger than 32-bits.
AuthoritySetTooLarge,
/// Voter state with round id or weights larger than 32-bits.
VoterStateTooLarge,
/// Failed to prove finality.
ProveFinality,
}
impl From<Error> for ErrorCode {
fn from(error: Error) -> Self {
match error {
Error::EndpointNotReady => ErrorCode::NotReady,
Error::AuthoritySetIdReportedAsUnreasonablyLarge => ErrorCode::AuthoritySetTooLarge,
Error::VoterStateReportsUnreasonablyLargeNumbers => ErrorCode::VoterStateTooLarge,
Error::ProveFinalityFailed(_) => ErrorCode::ProveFinality,
}
}
}
impl From<Error> for ErrorObjectOwned {
fn from(error: Error) -> Self {
let message = error.to_string();
let code = ErrorCode::from(error);
ErrorObject::owned(code as i32, message, None::<()>)
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(_error: std::num::TryFromIntError) -> Self {
Error::VoterStateReportsUnreasonablyLargeNumbers
}
}
@@ -0,0 +1,49 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use serde::{Deserialize, Serialize};
use pezsc_consensus_grandpa::FinalityProofProvider;
use pezsp_runtime::traits::{Block as BlockT, NumberFor};
#[derive(Clone, Serialize, Deserialize)]
pub struct EncodedFinalityProof(pub pezsp_core::Bytes);
/// Local trait mainly to allow mocking in tests.
pub trait RpcFinalityProofProvider<Block: BlockT> {
/// Prove finality for the given block number by returning a Justification for the last block of
/// the authority set.
fn rpc_prove_finality(
&self,
block: NumberFor<Block>,
) -> Result<Option<EncodedFinalityProof>, pezsc_consensus_grandpa::FinalityProofError>;
}
impl<B, Block> RpcFinalityProofProvider<Block> for FinalityProofProvider<B, Block>
where
Block: BlockT,
NumberFor<Block>: finality_grandpa::BlockNumberOps,
B: pezsc_client_api::backend::Backend<Block> + Send + Sync + 'static,
{
fn rpc_prove_finality(
&self,
block: NumberFor<Block>,
) -> Result<Option<EncodedFinalityProof>, pezsc_consensus_grandpa::FinalityProofError> {
self.prove_finality(block).map(|x| x.map(|y| EncodedFinalityProof(y.into())))
}
}
@@ -0,0 +1,415 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
//! RPC API for GRANDPA.
#![warn(missing_docs)]
use futures::StreamExt;
use log::warn;
use std::sync::Arc;
use jsonrpsee::{
core::{async_trait, server::PendingSubscriptionSink},
proc_macros::rpc,
};
mod error;
mod finality;
mod notification;
mod report;
use error::Error;
use finality::{EncodedFinalityProof, RpcFinalityProofProvider};
use notification::JustificationNotification;
use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
use pezsc_consensus_grandpa::GrandpaJustificationStream;
use pezsc_rpc::{
utils::{BoundedVecDeque, PendingSubscription},
SubscriptionTaskExecutor,
};
use pezsp_runtime::traits::{Block as BlockT, NumberFor};
/// Provides RPC methods for interacting with GRANDPA.
#[rpc(client, server)]
pub trait GrandpaApi<Notification, Hash, Number> {
/// Returns the state of the current best round state as well as the
/// ongoing background rounds.
#[method(name = "grandpa_roundState")]
async fn round_state(&self) -> Result<ReportedRoundStates, Error>;
/// Returns the block most recently finalized by Grandpa, alongside
/// side its justification.
#[subscription(
name = "grandpa_subscribeJustifications" => "grandpa_justifications",
unsubscribe = "grandpa_unsubscribeJustifications",
item = Notification
)]
fn subscribe_justifications(&self);
/// Prove finality for the given block number by returning the Justification for the last block
/// in the set and all the intermediary headers to link them together.
#[method(name = "grandpa_proveFinality")]
async fn prove_finality(&self, block: Number) -> Result<Option<EncodedFinalityProof>, Error>;
}
/// Provides RPC methods for interacting with GRANDPA.
pub struct Grandpa<AuthoritySet, VoterState, Block: BlockT, ProofProvider> {
executor: SubscriptionTaskExecutor,
authority_set: AuthoritySet,
voter_state: VoterState,
justification_stream: GrandpaJustificationStream<Block>,
finality_proof_provider: Arc<ProofProvider>,
}
impl<AuthoritySet, VoterState, Block: BlockT, ProofProvider>
Grandpa<AuthoritySet, VoterState, Block, ProofProvider>
{
/// Prepare a new [`Grandpa`] Rpc handler.
pub fn new(
executor: SubscriptionTaskExecutor,
authority_set: AuthoritySet,
voter_state: VoterState,
justification_stream: GrandpaJustificationStream<Block>,
finality_proof_provider: Arc<ProofProvider>,
) -> Self {
Self { executor, authority_set, voter_state, justification_stream, finality_proof_provider }
}
}
#[async_trait]
impl<AuthoritySet, VoterState, Block, ProofProvider>
GrandpaApiServer<JustificationNotification, Block::Hash, NumberFor<Block>>
for Grandpa<AuthoritySet, VoterState, Block, ProofProvider>
where
VoterState: ReportVoterState + Send + Sync + 'static,
AuthoritySet: ReportAuthoritySet + Send + Sync + 'static,
Block: BlockT,
ProofProvider: RpcFinalityProofProvider<Block> + Send + Sync + 'static,
{
async fn round_state(&self) -> Result<ReportedRoundStates, Error> {
ReportedRoundStates::from(&self.authority_set, &self.voter_state)
}
fn subscribe_justifications(&self, pending: PendingSubscriptionSink) {
let stream = self.justification_stream.subscribe(100_000).map(
|x: pezsc_consensus_grandpa::GrandpaJustification<Block>| {
JustificationNotification::from(x)
},
);
pezsc_rpc::utils::spawn_subscription_task(
&self.executor,
PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()),
);
}
async fn prove_finality(
&self,
block: NumberFor<Block>,
) -> Result<Option<EncodedFinalityProof>, Error> {
self.finality_proof_provider.rpc_prove_finality(block).map_err(|e| {
warn!("Error proving finality: {}", e);
error::Error::ProveFinalityFailed(e)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{collections::HashSet, sync::Arc};
use codec::{Decode, Encode};
use jsonrpsee::{core::EmptyServerParams as EmptyParams, types::SubscriptionId, RpcModule};
use pezsc_block_builder::BlockBuilderBuilder;
use pezsc_consensus_grandpa::{
report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender,
};
use pezsc_rpc::testing::test_executor;
use pezsp_blockchain::HeaderBackend;
use pezsp_core::crypto::ByteArray;
use pezsp_keyring::Ed25519Keyring;
use pezsp_runtime::traits::{Block as BlockT, Header as HeaderT};
use bizinikiwi_test_runtime_client::{
runtime::{Block, Header, H256},
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
};
struct TestAuthoritySet;
struct TestVoterState;
struct EmptyVoterState;
struct TestFinalityProofProvider {
finality_proof: Option<FinalityProof<Header>>,
}
fn voters() -> HashSet<AuthorityId> {
let voter_id_1 = AuthorityId::from_slice(&[1; 32]).unwrap();
let voter_id_2 = AuthorityId::from_slice(&[2; 32]).unwrap();
vec![voter_id_1, voter_id_2].into_iter().collect()
}
impl ReportAuthoritySet for TestAuthoritySet {
fn get(&self) -> (u64, HashSet<AuthorityId>) {
(1, voters())
}
}
impl ReportVoterState for EmptyVoterState {
fn get(&self) -> Option<report::VoterState<AuthorityId>> {
None
}
}
fn header(number: u64) -> Header {
let parent_hash = match number {
0 => Default::default(),
_ => header(number - 1).hash(),
};
Header::new(
number,
H256::from_low_u64_be(0),
H256::from_low_u64_be(0),
parent_hash,
Default::default(),
)
}
impl<Block: BlockT> RpcFinalityProofProvider<Block> for TestFinalityProofProvider {
fn rpc_prove_finality(
&self,
_block: NumberFor<Block>,
) -> Result<Option<EncodedFinalityProof>, pezsc_consensus_grandpa::FinalityProofError> {
Ok(Some(EncodedFinalityProof(
self.finality_proof
.as_ref()
.expect("Don't call rpc_prove_finality without setting the FinalityProof")
.encode()
.into(),
)))
}
}
impl ReportVoterState for TestVoterState {
fn get(&self) -> Option<report::VoterState<AuthorityId>> {
let voter_id_1 = AuthorityId::from_slice(&[1; 32]).unwrap();
let voters_best: HashSet<_> = vec![voter_id_1].into_iter().collect();
let best_round_state = pezsc_consensus_grandpa::report::RoundState {
total_weight: 100_u64.try_into().unwrap(),
threshold_weight: 67_u64.try_into().unwrap(),
prevote_current_weight: 50.into(),
prevote_ids: voters_best,
precommit_current_weight: 0.into(),
precommit_ids: HashSet::new(),
};
let past_round_state = pezsc_consensus_grandpa::report::RoundState {
total_weight: 100_u64.try_into().unwrap(),
threshold_weight: 67_u64.try_into().unwrap(),
prevote_current_weight: 100.into(),
prevote_ids: voters(),
precommit_current_weight: 100.into(),
precommit_ids: voters(),
};
let background_rounds = vec![(1, past_round_state)].into_iter().collect();
Some(report::VoterState { background_rounds, best_round: (2, best_round_state) })
}
}
fn setup_io_handler<VoterState>(
voter_state: VoterState,
) -> (
RpcModule<Grandpa<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
GrandpaJustificationSender<Block>,
)
where
VoterState: ReportVoterState + Send + Sync + 'static,
{
setup_io_handler_with_finality_proofs(voter_state, None)
}
fn setup_io_handler_with_finality_proofs<VoterState>(
voter_state: VoterState,
finality_proof: Option<FinalityProof<Header>>,
) -> (
RpcModule<Grandpa<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
GrandpaJustificationSender<Block>,
)
where
VoterState: ReportVoterState + Send + Sync + 'static,
{
let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proof });
let executor = test_executor();
let rpc = Grandpa::new(
executor,
TestAuthoritySet,
voter_state,
justification_stream,
finality_proof_provider,
)
.into_rpc();
(rpc, justification_sender)
}
#[tokio::test]
async fn uninitialized_rpc_handler() {
let (rpc, _) = setup_io_handler(EmptyVoterState);
let expected_response = r#"{"jsonrpc":"2.0","id":0,"error":{"code":1,"message":"GRANDPA RPC endpoint not ready"}}"#.to_string();
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
assert_eq!(expected_response, response);
}
#[tokio::test]
async fn working_rpc_handler() {
let (rpc, _) = setup_io_handler(TestVoterState);
let expected_response = "{\"jsonrpc\":\"2.0\",\"id\":0,\"result\":{\
\"setId\":1,\
\"best\":{\
\"round\":2,\"totalWeight\":100,\"thresholdWeight\":67,\
\"prevotes\":{\"currentWeight\":50,\"missing\":[\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]},\
\"precommits\":{\"currentWeight\":0,\"missing\":[\"5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT\",\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]}\
},\
\"background\":[{\
\"round\":1,\"totalWeight\":100,\"thresholdWeight\":67,\
\"prevotes\":{\"currentWeight\":100,\"missing\":[]},\
\"precommits\":{\"currentWeight\":100,\"missing\":[]}\
}]\
}}".to_string();
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
assert_eq!(expected_response, response);
}
#[tokio::test]
async fn subscribe_and_unsubscribe_with_wrong_id() {
let (rpc, _) = setup_io_handler(TestVoterState);
// Subscribe call.
let _sub = rpc
.subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
// Unsubscribe with wrong ID
let (response, _) = rpc
.raw_json_request(
r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#,
1,
)
.await
.unwrap();
let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#;
assert_eq!(response, expected);
}
fn create_justification() -> GrandpaJustification<Block> {
let peers = &[Ed25519Keyring::Alice];
let builder = TestClientBuilder::new();
let client = builder.build();
let client = Arc::new(client);
let built_block = BlockBuilderBuilder::new(&*client)
.on_parent_block(client.info().best_hash)
.with_parent_block_number(client.info().best_number)
.build()
.unwrap()
.build()
.unwrap();
let block = built_block.block;
let block_hash = block.hash();
let justification = {
let round = 1;
let set_id = 0;
let precommit = finality_grandpa::Precommit {
target_hash: block_hash,
target_number: *block.header.number(),
};
let msg = finality_grandpa::Message::Precommit(precommit.clone());
let encoded = pezsp_consensus_grandpa::localized_payload(round, set_id, &msg);
let signature = peers[0].sign(&encoded[..]).into();
let precommit = finality_grandpa::SignedPrecommit {
precommit,
signature,
id: peers[0].public().into(),
};
let commit = finality_grandpa::Commit {
target_hash: block_hash,
target_number: *block.header.number(),
precommits: vec![precommit],
};
GrandpaJustification::from_commit(&client, round, commit).unwrap()
};
justification
}
#[tokio::test]
async fn subscribe_and_listen_to_one_justification() {
let (rpc, justification_sender) = setup_io_handler(TestVoterState);
let mut sub = rpc
.subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
.await
.unwrap();
// Notify with a header and justification
let justification = create_justification();
justification_sender.notify(|| Ok::<_, ()>(justification.clone())).unwrap();
// Inspect what we received
let (recv_justification, recv_sub_id): (pezsp_core::Bytes, SubscriptionId) =
sub.next().await.unwrap().unwrap();
let recv_justification: GrandpaJustification<Block> =
Decode::decode(&mut &recv_justification[..]).unwrap();
assert_eq!(&recv_sub_id, sub.subscription_id());
assert_eq!(recv_justification, justification);
}
#[tokio::test]
async fn prove_finality_with_test_finality_proof_provider() {
let finality_proof = FinalityProof {
block: header(42).hash(),
justification: create_justification().encode(),
unknown_headers: vec![header(2)],
};
let (rpc, _) =
setup_io_handler_with_finality_proofs(TestVoterState, Some(finality_proof.clone()));
let bytes: pezsp_core::Bytes = rpc.call("grandpa_proveFinality", [42]).await.unwrap();
let finality_proof_rpc: FinalityProof<Header> = Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(finality_proof_rpc, finality_proof);
}
}
@@ -0,0 +1,32 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use codec::Encode;
use pezsc_consensus_grandpa::GrandpaJustification;
use serde::{Deserialize, Serialize};
use pezsp_runtime::traits::Block as BlockT;
/// An encoded justification proving that the given header has been finalized
#[derive(Clone, Serialize, Deserialize)]
pub struct JustificationNotification(pezsp_core::Bytes);
impl<Block: BlockT> From<GrandpaJustification<Block>> for JustificationNotification {
fn from(notification: GrandpaJustification<Block>) -> Self {
JustificationNotification(notification.encode().into())
}
}
@@ -0,0 +1,150 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
use std::{
collections::{BTreeSet, HashSet},
fmt::Debug,
ops::Add,
};
use serde::{Deserialize, Serialize};
use pezsc_consensus_grandpa::{report, AuthorityId, SharedAuthoritySet, SharedVoterState};
use crate::error::Error;
/// Utility trait to get reporting data for the current GRANDPA authority set.
pub trait ReportAuthoritySet {
fn get(&self) -> (u64, HashSet<AuthorityId>);
}
/// Utility trait to get reporting data for the current GRANDPA voter state.
pub trait ReportVoterState {
fn get(&self) -> Option<report::VoterState<AuthorityId>>;
}
impl<H, N> ReportAuthoritySet for SharedAuthoritySet<H, N>
where
N: Add<Output = N> + Ord + Clone + Debug,
H: Clone + Debug + Eq,
{
fn get(&self) -> (u64, HashSet<AuthorityId>) {
let current_voters: HashSet<AuthorityId> =
self.current_authorities().iter().map(|p| p.0.clone()).collect();
(self.set_id(), current_voters)
}
}
impl ReportVoterState for SharedVoterState {
fn get(&self) -> Option<report::VoterState<AuthorityId>> {
self.voter_state()
}
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Prevotes {
current_weight: u32,
missing: BTreeSet<AuthorityId>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Precommits {
current_weight: u32,
missing: BTreeSet<AuthorityId>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
round: u32,
total_weight: u32,
threshold_weight: u32,
prevotes: Prevotes,
precommits: Precommits,
}
impl RoundState {
fn from(
round: u64,
round_state: &report::RoundState<AuthorityId>,
voters: &HashSet<AuthorityId>,
) -> Result<Self, Error> {
let prevotes = &round_state.prevote_ids;
let missing_prevotes = voters.difference(prevotes).cloned().collect();
let precommits = &round_state.precommit_ids;
let missing_precommits = voters.difference(precommits).cloned().collect();
Ok(Self {
round: round.try_into()?,
total_weight: round_state.total_weight.get().try_into()?,
threshold_weight: round_state.threshold_weight.get().try_into()?,
prevotes: Prevotes {
current_weight: round_state.prevote_current_weight.0.try_into()?,
missing: missing_prevotes,
},
precommits: Precommits {
current_weight: round_state.precommit_current_weight.0.try_into()?,
missing: missing_precommits,
},
})
}
}
/// The state of the current best round, as well as the background rounds in a
/// form suitable for serialization.
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReportedRoundStates {
set_id: u32,
best: RoundState,
background: Vec<RoundState>,
}
impl ReportedRoundStates {
pub fn from<AuthoritySet, VoterState>(
authority_set: &AuthoritySet,
voter_state: &VoterState,
) -> Result<Self, Error>
where
AuthoritySet: ReportAuthoritySet,
VoterState: ReportVoterState,
{
let voter_state = voter_state.get().ok_or(Error::EndpointNotReady)?;
let (set_id, current_voters) = authority_set.get();
let set_id =
u32::try_from(set_id).map_err(|_| Error::AuthoritySetIdReportedAsUnreasonablyLarge)?;
let best = {
let (round, round_state) = voter_state.best_round;
RoundState::from(round, &round_state, &current_voters)?
};
let background = voter_state
.background_rounds
.iter()
.map(|(round, round_state)| RoundState::from(*round, round_state, &current_voters))
.collect::<Result<Vec<_>, Error>>()?;
Ok(Self { set_id, best, background })
}
}