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:
@@ -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, ¤t_voters)?
|
||||
};
|
||||
|
||||
let background = voter_state
|
||||
.background_rounds
|
||||
.iter()
|
||||
.map(|(round, round_state)| RoundState::from(*round, round_state, ¤t_voters))
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
Ok(Self { set_id, best, background })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user