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,37 @@
[package]
name = "bizinikiwi-rpc-client"
version = "0.33.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Shared JSON-RPC client"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
async-trait = { workspace = true }
jsonrpsee = { features = ["ws-client"], workspace = true }
log = { workspace = true, default-features = true }
pezsc-rpc-api = { workspace = true, default-features = true }
serde = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
[dev-dependencies]
pezsp-core = { workspace = true, default-features = true }
tokio = { features = [
"macros",
"rt-multi-thread",
"sync",
], workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"pezsc-rpc-api/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
@@ -0,0 +1,271 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Shared JSON-RPC client related code and abstractions.
//!
//! It exposes a `WebSocket JSON-RPC` client that implements the RPC interface in [`sc-rpc-api`]
//! along with some abstractions.
//!
//! ## Usage
//!
//! ```no_run
//! # use bizinikiwi_rpc_client::{ws_client, StateApi};
//! # use pezsp_core::{H256, storage::StorageKey};
//!
//! #[tokio::main]
//! async fn main() {
//!
//! let client = ws_client("ws://127.0.0.1:9944").await.unwrap();
//! client.storage(StorageKey(vec![]), Some(H256::zero())).await.unwrap();
//!
//! // if all type params are not known you need to provide type params
//! StateApi::<H256>::storage(&client, StorageKey(vec![]), None).await.unwrap();
//! }
//! ```
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use pezsp_runtime::traits::{Block as BlockT, Header as HeaderT};
use std::collections::VecDeque;
pub use jsonrpsee::{
core::{
client::{ClientT, Error, Subscription, SubscriptionClientT},
params::BatchRequestBuilder,
RpcResult,
},
rpc_params,
ws_client::{WsClient, WsClientBuilder},
};
pub use pezsc_rpc_api::{
author::AuthorApiClient as AuthorApi, chain::ChainApiClient as ChainApi,
child_state::ChildStateApiClient as ChildStateApi, dev::DevApiClient as DevApi,
offchain::OffchainApiClient as OffchainApi, state::StateApiClient as StateApi,
system::SystemApiClient as SystemApi,
};
/// Create a new `WebSocket` connection with shared settings.
pub async fn ws_client(uri: impl AsRef<str>) -> Result<WsClient, String> {
WsClientBuilder::default()
.max_request_size(u32::MAX)
.max_response_size(u32::MAX)
.request_timeout(std::time::Duration::from_secs(60 * 10))
.connection_timeout(std::time::Duration::from_secs(60))
.max_buffer_capacity_per_subscription(1024)
.build(uri)
.await
.map_err(|e| format!("`WsClientBuilder` failed to build: {:?}", e))
}
/// Abstraction over RPC calling for headers.
#[async_trait]
pub trait HeaderProvider<Block: BlockT>
where
Block::Header: HeaderT,
{
/// Awaits for the header of the block with hash `hash`.
async fn get_header(&self, hash: Block::Hash) -> Block::Header;
}
#[async_trait]
impl<Block: BlockT> HeaderProvider<Block> for WsClient
where
Block::Header: DeserializeOwned,
{
async fn get_header(&self, hash: Block::Hash) -> Block::Header {
ChainApi::<(), Block::Hash, Block::Header, ()>::header(self, Some(hash))
.await
.unwrap()
.unwrap()
}
}
/// Abstraction over RPC subscription for finalized headers.
#[async_trait]
pub trait HeaderSubscription<Block: BlockT>
where
Block::Header: HeaderT,
{
/// Await for the next finalized header from the subscription.
///
/// Returns `None` if either the subscription has been closed or there was an error when reading
/// an object from the client.
async fn next_header(&mut self) -> Option<Block::Header>;
}
#[async_trait]
impl<Block: BlockT> HeaderSubscription<Block> for Subscription<Block::Header>
where
Block::Header: DeserializeOwned,
{
async fn next_header(&mut self) -> Option<Block::Header> {
match self.next().await {
Some(Ok(header)) => Some(header),
None => {
log::warn!("subscription closed");
None
},
Some(Err(why)) => {
log::warn!("subscription returned error: {:?}. Probably decoding has failed.", why);
None
},
}
}
}
/// Stream of all finalized headers.
///
/// Returned headers are guaranteed to be ordered. There are no missing headers (even if some of
/// them lack justification).
pub struct FinalizedHeaders<
'a,
Block: BlockT,
HP: HeaderProvider<Block>,
HS: HeaderSubscription<Block>,
> {
header_provider: &'a HP,
subscription: HS,
fetched_headers: VecDeque<Block::Header>,
last_returned: Option<<Block::Header as HeaderT>::Hash>,
}
impl<'a, Block: BlockT, HP: HeaderProvider<Block>, HS: HeaderSubscription<Block>>
FinalizedHeaders<'a, Block, HP, HS>
where
<Block as BlockT>::Header: DeserializeOwned,
{
pub fn new(header_provider: &'a HP, subscription: HS) -> Self {
Self {
header_provider,
subscription,
fetched_headers: VecDeque::new(),
last_returned: None,
}
}
/// Reads next finalized header from the subscription. If some headers (without justification)
/// have been skipped, fetches them as well. Returns number of headers that have been fetched.
///
/// All fetched headers are stored in `self.fetched_headers`.
async fn fetch(&mut self) -> usize {
let last_finalized = match self.subscription.next_header().await {
Some(header) => header,
None => return 0,
};
self.fetched_headers.push_front(last_finalized.clone());
let mut last_finalized_parent = *last_finalized.parent_hash();
let last_returned = self.last_returned.unwrap_or(last_finalized_parent);
while last_finalized_parent != last_returned {
let parent_header = self.header_provider.get_header(last_finalized_parent).await;
self.fetched_headers.push_front(parent_header.clone());
last_finalized_parent = *parent_header.parent_hash();
}
self.fetched_headers.len()
}
/// Get the next finalized header.
pub async fn next(&mut self) -> Option<Block::Header> {
if self.fetched_headers.is_empty() {
self.fetch().await;
}
if let Some(header) = self.fetched_headers.pop_front() {
self.last_returned = Some(header.hash());
Some(header)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_runtime::testing::{Block as TBlock, Header, MockCallU64, TestXt, H256};
use std::sync::Arc;
use tokio::sync::Mutex;
type UncheckedXt = TestXt<MockCallU64, ()>;
type Block = TBlock<UncheckedXt>;
type BlockNumber = u64;
type Hash = H256;
struct MockHeaderProvider(pub Arc<Mutex<VecDeque<BlockNumber>>>);
fn headers() -> Vec<Header> {
let mut headers = vec![Header::new_from_number(0)];
for n in 1..11 {
headers.push(Header {
parent_hash: headers.last().unwrap().hash(),
..Header::new_from_number(n)
})
}
headers
}
#[async_trait]
impl HeaderProvider<Block> for MockHeaderProvider {
async fn get_header(&self, _hash: Hash) -> Header {
let height = self.0.lock().await.pop_front().unwrap();
headers()[height as usize].clone()
}
}
struct MockHeaderSubscription(pub VecDeque<BlockNumber>);
#[async_trait]
impl HeaderSubscription<Block> for MockHeaderSubscription {
async fn next_header(&mut self) -> Option<Header> {
self.0.pop_front().map(|h| headers()[h as usize].clone())
}
}
#[tokio::test]
async fn finalized_headers_works_when_every_block_comes_from_subscription() {
let heights = vec![4, 5, 6, 7];
let provider = MockHeaderProvider(Default::default());
let subscription = MockHeaderSubscription(heights.clone().into());
let mut headers = FinalizedHeaders::new(&provider, subscription);
for h in heights {
assert_eq!(h, headers.next().await.unwrap().number);
}
assert_eq!(None, headers.next().await);
}
#[tokio::test]
async fn finalized_headers_come_from_subscription_and_provider_if_in_need() {
let all_heights = 3..11;
let heights_in_subscription = vec![3, 4, 6, 10];
// Consecutive headers will be requested in the reversed order.
let heights_not_in_subscription = vec![5, 9, 8, 7];
let provider = MockHeaderProvider(Arc::new(Mutex::new(heights_not_in_subscription.into())));
let subscription = MockHeaderSubscription(heights_in_subscription.into());
let mut headers = FinalizedHeaders::new(&provider, subscription);
for h in all_heights {
assert_eq!(h, headers.next().await.unwrap().number);
}
assert_eq!(None, headers.next().await);
}
}
@@ -0,0 +1,45 @@
[package]
name = "bizinikiwi-state-trie-migration-rpc"
version = "27.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Node-specific RPC methods for interaction with state trie migration."
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
serde = { features = ["derive"], workspace = true, default-features = true }
pezsp-core = { workspace = true, default-features = true }
pezsp-state-machine = { workspace = true, default-features = true }
pezsp-trie = { workspace = true, default-features = true }
trie-db = { workspace = true, default-features = true }
jsonrpsee = { features = [
"client-core",
"macros",
"server-core",
], workspace = true }
# Bizinikiwi Dependencies
pezsc-client-api = { workspace = true, default-features = true }
pezsc-rpc-api = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"pezsc-client-api/runtime-benchmarks",
"pezsc-rpc-api/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-state-machine/runtime-benchmarks",
"pezsp-trie/runtime-benchmarks",
]
@@ -0,0 +1,3 @@
Node-specific RPC methods for interaction with trie migration.
License: Apache-2.0
@@ -0,0 +1,185 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Rpc for state migration.
use jsonrpsee::{
core::RpcResult,
proc_macros::rpc,
types::error::{ErrorCode, ErrorObject, ErrorObjectOwned},
Extensions,
};
use pezsc_client_api::TrieCacheContext;
use pezsc_rpc_api::check_if_safe;
use serde::{Deserialize, Serialize};
use pezsp_runtime::traits::Block as BlockT;
use std::sync::Arc;
use pezsp_core::{
storage::{ChildInfo, ChildType, PrefixedStorageKey},
Hasher,
};
use pezsp_state_machine::backend::AsTrieBackend;
use pezsp_trie::{
trie_types::{TrieDB, TrieDBBuilder},
KeySpacedDB, Trie,
};
use trie_db::{
node::{NodePlan, ValuePlan},
TrieDBNodeIterator,
};
fn count_migrate<'a, H: Hasher>(
storage: &'a dyn trie_db::HashDBRef<H, Vec<u8>>,
root: &'a H::Out,
) -> std::result::Result<(u64, u64, TrieDB<'a, 'a, H>), String> {
let mut nb = 0u64;
let mut total_nb = 0u64;
let trie = TrieDBBuilder::new(storage, root).build();
let iter_node =
TrieDBNodeIterator::new(&trie).map_err(|e| format!("TrieDB node iterator error: {}", e))?;
for node in iter_node {
let node = node.map_err(|e| format!("TrieDB node iterator error: {}", e))?;
match node.2.node_plan() {
NodePlan::Leaf { value, .. } | NodePlan::NibbledBranch { value: Some(value), .. } => {
total_nb += 1;
if let ValuePlan::Inline(range) = value {
if (range.end - range.start) as u32 >=
pezsp_core::storage::TRIE_VALUE_NODE_THRESHOLD
{
nb += 1;
}
}
},
_ => (),
}
}
Ok((nb, total_nb, trie))
}
/// Check trie migration status.
pub fn migration_status<H, B>(backend: &B) -> std::result::Result<MigrationStatusResult, String>
where
H: Hasher,
H::Out: codec::Codec,
B: AsTrieBackend<H>,
{
let trie_backend = backend.as_trie_backend();
let essence = trie_backend.essence();
let (top_remaining_to_migrate, total_top, trie) = count_migrate(essence, essence.root())?;
let mut child_remaining_to_migrate = 0;
let mut total_child = 0;
let mut child_roots: Vec<(ChildInfo, Vec<u8>)> = Vec::new();
// get all child trie roots
for key_value in trie.iter().map_err(|e| format!("TrieDB node iterator error: {}", e))? {
let (key, value) = key_value.map_err(|e| format!("TrieDB node iterator error: {}", e))?;
if key[..].starts_with(pezsp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX)
{
let prefixed_key = PrefixedStorageKey::new(key);
let (_type, unprefixed) = ChildType::from_prefixed_key(&prefixed_key).unwrap();
child_roots.push((ChildInfo::new_default(unprefixed), value));
}
}
for (child_info, root) in child_roots {
let mut child_root = H::Out::default();
let storage = KeySpacedDB::new(essence, child_info.keyspace());
child_root.as_mut()[..].copy_from_slice(&root[..]);
let (nb, total_top, _) = count_migrate(&storage, &child_root)?;
child_remaining_to_migrate += nb;
total_child += total_top;
}
Ok(MigrationStatusResult {
top_remaining_to_migrate,
child_remaining_to_migrate,
total_top,
total_child,
})
}
/// Current state migration status.
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct MigrationStatusResult {
/// Number of top items that should migrate.
pub top_remaining_to_migrate: u64,
/// Number of child items that should migrate.
pub child_remaining_to_migrate: u64,
/// Number of top items that we will iterate on.
pub total_top: u64,
/// Number of child items that we will iterate on.
pub total_child: u64,
}
/// Migration RPC methods.
#[rpc(server)]
pub trait StateMigrationApi<BlockHash> {
/// Check current migration state.
///
/// This call is performed locally without submitting any transactions. Thus executing this
/// won't change any state. Nonetheless it is a VERY costly call that should be
/// only exposed to trusted peers.
#[method(name = "state_trieMigrationStatus", with_extensions)]
fn call(&self, at: Option<BlockHash>) -> RpcResult<MigrationStatusResult>;
}
/// An implementation of state migration specific RPC methods.
pub struct StateMigration<C, B, BA> {
client: Arc<C>,
backend: Arc<BA>,
_marker: std::marker::PhantomData<(B, BA)>,
}
impl<C, B, BA> StateMigration<C, B, BA> {
/// Create new state migration rpc for the given reference to the client.
pub fn new(client: Arc<C>, backend: Arc<BA>) -> Self {
StateMigration { client, backend, _marker: Default::default() }
}
}
impl<C, B, BA> StateMigrationApiServer<<B as BlockT>::Hash> for StateMigration<C, B, BA>
where
B: BlockT,
C: Send + Sync + 'static + pezsc_client_api::HeaderBackend<B>,
BA: 'static + pezsc_client_api::backend::Backend<B>,
{
fn call(
&self,
ext: &Extensions,
at: Option<<B as BlockT>::Hash>,
) -> RpcResult<MigrationStatusResult> {
check_if_safe(ext)?;
let hash = at.unwrap_or_else(|| self.client.info().best_hash);
let state = self
.backend
.state_at(hash, TrieCacheContext::Untrusted)
.map_err(error_into_rpc_err)?;
migration_status(&state).map_err(error_into_rpc_err)
}
}
fn error_into_rpc_err(err: impl std::fmt::Display) -> ErrorObjectOwned {
ErrorObject::owned(
ErrorCode::InternalError.code(),
"Error while checking migration state",
Some(err.to_string()),
)
}
@@ -0,0 +1,39 @@
[package]
name = "bizinikiwi-frame-rpc-support"
version = "29.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Bizinikiwi RPC for FRAME's support"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true, default-features = true }
pezframe-support = { workspace = true, default-features = true }
jsonrpsee = { features = ["jsonrpsee-types"], workspace = true }
pezsc-rpc-api = { workspace = true, default-features = true }
scale-info = { workspace = true, default-features = true }
serde = { workspace = true, default-features = true }
pezsp-storage = { workspace = true, default-features = true }
[dev-dependencies]
pezframe-system = { workspace = true, default-features = true }
jsonrpsee = { features = ["jsonrpsee-types", "ws-client"], workspace = true }
pezsp-core = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
tokio = { workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsc-rpc-api/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
@@ -0,0 +1,4 @@
Combines [sc_rpc_api::state::StateClient] with [frame_support::storage::generator] traits
to provide strongly typed chain state queries over rpc.
License: Apache-2.0
@@ -0,0 +1,186 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Combines [pezsc_rpc_api::state::StateApiClient] with [pezframe_support::storage::generator] traits
//! to provide strongly typed chain state queries over rpc.
#![warn(missing_docs)]
use codec::{DecodeAll, FullCodec, FullEncode};
use core::marker::PhantomData;
use pezframe_support::storage::generator::{StorageDoubleMap, StorageMap, StorageValue};
use jsonrpsee::core::ClientError as RpcError;
use pezsc_rpc_api::state::StateApiClient;
use serde::{de::DeserializeOwned, Serialize};
use pezsp_storage::{StorageData, StorageKey};
/// A typed query on chain state usable from an RPC client.
///
/// ```no_run
/// # use jsonrpsee::core::ClientError as RpcError;
/// # use jsonrpsee::ws_client::WsClientBuilder;
/// # use codec::Encode;
/// # use pezframe_support::{construct_runtime, derive_impl, traits::ConstU32};
/// # use bizinikiwi_frame_rpc_support::StorageQuery;
/// # use pezsc_rpc_api::state::StateApiClient;
/// # use pezsp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
/// #
/// # construct_runtime!(
/// # pub enum TestRuntime
/// # {
/// # System: pezframe_system,
/// # Test: pezpallet_test,
/// # }
/// # );
/// #
/// # type Hash = pezsp_core::H256;
/// #
/// # #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
/// # impl pezframe_system::Config for TestRuntime {
/// # type BaseCallFilter = ();
/// # type BlockWeights = ();
/// # type BlockLength = ();
/// # type RuntimeOrigin = RuntimeOrigin;
/// # type RuntimeCall = RuntimeCall;
/// # type Nonce = u64;
/// # type Hash = Hash;
/// # type Hashing = BlakeTwo256;
/// # type AccountId = u64;
/// # type Lookup = IdentityLookup<Self::AccountId>;
/// # type Block = pezframe_system::mocking::MockBlock<TestRuntime>;
/// # type RuntimeEvent = RuntimeEvent;
/// # type RuntimeTask = RuntimeTask;
/// # type BlockHashCount = ();
/// # type DbWeight = ();
/// # type Version = ();
/// # type PalletInfo = PalletInfo;
/// # type AccountData = ();
/// # type OnNewAccount = ();
/// # type OnKilledAccount = ();
/// # type SystemWeightInfo = ();
/// # type SS58Prefix = ();
/// # type OnSetCode = ();
/// # type MaxConsumers = ConstU32<16>;
/// # }
/// #
/// # impl pezpallet_test::Config for TestRuntime {}
/// #
///
/// pub type Loc = (i64, i64, i64);
/// pub type Block = u8;
///
/// // Note that all fields are marked pub.
/// pub use self::pezpallet_test::*;
///
/// #[pezframe_support::pallet]
/// mod pezpallet_test {
/// use super::*;
/// use pezframe_support::pezpallet_prelude::*;
///
/// #[pallet::pallet]
/// pub struct Pallet<T>(_);
///
/// #[pallet::config]
/// pub trait Config: pezframe_system::Config {}
///
/// #[pallet::storage]
/// pub type LastActionId<T> = StorageValue<_, u64, ValueQuery>;
///
/// #[pallet::storage]
/// pub type Voxels<T> = StorageMap<_, Blake2_128Concat, Loc, Block>;
///
/// #[pallet::storage]
/// pub type Actions<T> = StorageMap<_, Blake2_128Concat, u64, Loc>;
///
/// #[pallet::storage]
/// pub type Prefab<T> = StorageDoubleMap<
/// _,
/// Blake2_128Concat, u128,
/// Blake2_128Concat, (i8, i8, i8), Block
/// >;
/// }
///
/// #[tokio::main]
/// async fn main() -> Result<(), RpcError> {
/// let cl = WsClientBuilder::default().build("ws://[::1]:9944").await?;
///
/// let q = StorageQuery::value::<LastActionId<TestRuntime>>();
/// let hash = None::<Hash>;
/// let _: Option<u64> = q.get(&cl, hash).await?;
///
/// let q = StorageQuery::map::<Voxels<TestRuntime>, _>((0, 0, 0));
/// let _: Option<Block> = q.get(&cl, hash).await?;
///
/// let q = StorageQuery::map::<Actions<TestRuntime>, _>(12);
/// let _: Option<Loc> = q.get(&cl, hash).await?;
///
/// let q = StorageQuery::double_map::<Prefab<TestRuntime>, _, _>(3, (0, 0, 0));
/// let _: Option<Block> = q.get(&cl, hash).await?;
///
/// Ok(())
/// }
/// ```
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct StorageQuery<V> {
key: StorageKey,
_spook: PhantomData<V>,
}
impl<V: FullCodec> StorageQuery<V> {
/// Create a storage query for a StorageValue.
pub fn value<St: StorageValue<V>>() -> Self {
Self { key: StorageKey(St::storage_value_final_key().to_vec()), _spook: PhantomData }
}
/// Create a storage query for a value in a StorageMap.
pub fn map<St: StorageMap<K, V>, K: FullEncode>(key: K) -> Self {
Self { key: StorageKey(St::storage_map_final_key(key)), _spook: PhantomData }
}
/// Create a storage query for a value in a StorageDoubleMap.
pub fn double_map<St: StorageDoubleMap<K1, K2, V>, K1: FullEncode, K2: FullEncode>(
key1: K1,
key2: K2,
) -> Self {
Self { key: StorageKey(St::storage_double_map_final_key(key1, key2)), _spook: PhantomData }
}
/// Send this query over RPC, await the typed result.
///
/// Hash should be `<YourRuntime as pezframe_system::Config>::Hash`.
///
/// # Arguments
///
/// state_client represents a connection to the RPC server.
///
/// block_index indicates the block for which state will be queried. A value of None indicates
/// the latest block.
pub async fn get<Hash, StateClient>(
self,
state_client: &StateClient,
block_index: Option<Hash>,
) -> Result<Option<V>, RpcError>
where
Hash: Send + Sync + 'static + DeserializeOwned + Serialize,
StateClient: StateApiClient<Hash> + Sync,
{
let opt: Option<StorageData> = state_client.storage(self.key, block_index).await?;
opt.map(|encoded| V::decode_all(&mut &encoded.0[..]))
.transpose()
.map_err(|decode_err| RpcError::Custom(decode_err.to_string()))
}
}
@@ -0,0 +1,55 @@
[package]
name = "bizinikiwi-frame-rpc-system"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME's system exposed over Bizinikiwi RPC"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true, default-features = true }
docify = { workspace = true }
pezframe-system-rpc-runtime-api = { workspace = true, default-features = true }
futures = { workspace = true }
jsonrpsee = { features = [
"client-core",
"macros",
"server-core",
], workspace = true }
log = { workspace = true, default-features = true }
pezsc-rpc-api = { workspace = true, default-features = true }
pezsc-transaction-pool-api = { workspace = true, default-features = true }
pezsp-api = { workspace = true, default-features = true }
pezsp-block-builder = { 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 }
[dev-dependencies]
assert_matches = { workspace = true }
pezsc-transaction-pool = { workspace = true, default-features = true }
pezsp-tracing = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
tokio = { workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"pezframe-system-rpc-runtime-api/runtime-benchmarks",
"pezsc-rpc-api/runtime-benchmarks",
"pezsc-transaction-pool-api/runtime-benchmarks",
"pezsc-transaction-pool/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-block-builder/runtime-benchmarks",
"pezsp-blockchain/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
]
@@ -0,0 +1,3 @@
System FRAME specific RPC methods.
License: Apache-2.0
@@ -0,0 +1,377 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! System FRAME specific RPC methods.
use std::{fmt::Display, sync::Arc};
use codec::{self, Codec, Decode, Encode};
use jsonrpsee::{
core::{async_trait, RpcResult},
proc_macros::rpc,
types::error::ErrorObject,
Extensions,
};
use pezsc_transaction_pool_api::{InPoolTransaction, TransactionPool};
use pezsp_api::ApiExt;
use pezsp_block_builder::BlockBuilder;
use pezsp_blockchain::HeaderBackend;
use pezsp_core::{hexdisplay::HexDisplay, Bytes};
use pezsp_runtime::{legacy, traits};
pub use pezframe_system_rpc_runtime_api::AccountNonceApi;
/// System RPC methods.
#[docify::export]
#[rpc(client, server)]
pub trait SystemApi<BlockHash, AccountId, Nonce> {
/// Returns the next valid index (aka nonce) for given account.
///
/// This method takes into consideration all pending transactions
/// currently in the pool and if no transactions are found in the pool
/// it fallbacks to query the index from the runtime (aka. state nonce).
#[method(name = "system_accountNextIndex", aliases = ["account_nextIndex"])]
async fn nonce(&self, account: AccountId) -> RpcResult<Nonce>;
/// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult.
#[method(name = "system_dryRun", aliases = ["system_dryRunAt"], with_extensions)]
async fn dry_run(&self, extrinsic: Bytes, at: Option<BlockHash>) -> RpcResult<Bytes>;
}
/// Error type of this RPC api.
pub enum Error {
/// The transaction was not decodable.
DecodeError,
/// The call to runtime failed.
RuntimeError,
}
impl From<Error> for i32 {
fn from(e: Error) -> i32 {
match e {
Error::RuntimeError => 1,
Error::DecodeError => 2,
}
}
}
/// An implementation of System-specific RPC methods on full client.
pub struct System<P: TransactionPool, C, B> {
client: Arc<C>,
pool: Arc<P>,
_marker: std::marker::PhantomData<B>,
}
impl<P: TransactionPool, C, B> System<P, C, B> {
/// Create new `FullSystem` given client and transaction pool.
pub fn new(client: Arc<C>, pool: Arc<P>) -> Self {
Self { client, pool, _marker: Default::default() }
}
}
#[async_trait]
impl<P, C, Block, AccountId, Nonce>
SystemApiServer<<Block as traits::Block>::Hash, AccountId, Nonce> for System<P, C, Block>
where
C: pezsp_api::ProvideRuntimeApi<Block>,
C: HeaderBackend<Block>,
C: Send + Sync + 'static,
C::Api: AccountNonceApi<Block, AccountId, Nonce>,
C::Api: BlockBuilder<Block>,
P: TransactionPool + 'static,
Block: traits::Block,
AccountId: Clone + Display + Codec + Send + 'static,
Nonce: Clone + Display + Codec + Send + traits::AtLeast32Bit + 'static,
{
async fn nonce(&self, account: AccountId) -> RpcResult<Nonce> {
let api = self.client.runtime_api();
let best = self.client.info().best_hash;
let nonce = api.account_nonce(best, account.clone()).map_err(|e| {
ErrorObject::owned(
Error::RuntimeError.into(),
"Unable to query nonce.",
Some(e.to_string()),
)
})?;
Ok(adjust_nonce(&*self.pool, account, nonce))
}
async fn dry_run(
&self,
ext: &Extensions,
extrinsic: Bytes,
at: Option<<Block as traits::Block>::Hash>,
) -> RpcResult<Bytes> {
pezsc_rpc_api::check_if_safe(ext)?;
let api = self.client.runtime_api();
let best_hash = at.unwrap_or_else(||
// If the block hash is not supplied assume the best block.
self.client.info().best_hash);
let uxt: <Block as traits::Block>::Extrinsic =
Decode::decode(&mut &*extrinsic).map_err(|e| {
ErrorObject::owned(
Error::DecodeError.into(),
"Unable to dry run extrinsic",
Some(e.to_string()),
)
})?;
let api_version = api
.api_version::<dyn BlockBuilder<Block>>(best_hash)
.map_err(|e| {
ErrorObject::owned(
Error::RuntimeError.into(),
"Unable to dry run extrinsic.",
Some(e.to_string()),
)
})?
.ok_or_else(|| {
ErrorObject::owned(
Error::RuntimeError.into(),
"Unable to dry run extrinsic.",
Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", best_hash)),
)
})?;
let result = if api_version < 6 {
#[allow(deprecated)]
api.apply_extrinsic_before_version_6(best_hash, uxt)
.map(legacy::byte_sized_error::convert_to_latest)
.map_err(|e| {
ErrorObject::owned(
Error::RuntimeError.into(),
"Unable to dry run extrinsic.",
Some(e.to_string()),
)
})?
} else {
api.apply_extrinsic(best_hash, uxt).map_err(|e| {
ErrorObject::owned(
Error::RuntimeError.into(),
"Unable to dry run extrinsic.",
Some(e.to_string()),
)
})?
};
Ok(Encode::encode(&result).into())
}
}
/// Adjust account nonce from state, so that tx with the nonce will be
/// placed after all ready txpool transactions.
fn adjust_nonce<P, AccountId, Nonce>(pool: &P, account: AccountId, nonce: Nonce) -> Nonce
where
P: TransactionPool,
AccountId: Clone + std::fmt::Display + Encode,
Nonce: Clone + std::fmt::Display + Encode + traits::AtLeast32Bit + 'static,
{
log::debug!(target: "rpc", "State nonce for {}: {}", account, nonce);
// Now we need to query the transaction pool
// and find transactions originating from the same sender.
//
// Since extrinsics are opaque to us, we look for them using
// `provides` tag. And increment the nonce if we find a transaction
// that matches the current one.
let mut current_nonce = nonce.clone();
let mut current_tag = (account.clone(), nonce).encode();
for tx in pool.ready() {
log::debug!(
target: "rpc",
"Current nonce to {}, checking {} vs {:?}",
current_nonce,
HexDisplay::from(&current_tag),
tx.provides().iter().map(|x| format!("{}", HexDisplay::from(x))).collect::<Vec<_>>(),
);
// since transactions in `ready()` need to be ordered by nonce
// it's fine to continue with current iterator.
if tx.provides().get(0) == Some(&current_tag) {
current_nonce += traits::One::one();
current_tag = (account.clone(), current_nonce.clone()).encode();
}
}
current_nonce
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use futures::executor::block_on;
use pezsc_rpc_api::DenyUnsafe;
use pezsc_transaction_pool::BasicPool;
use pezsp_runtime::{
transaction_validity::{InvalidTransaction, TransactionValidityError},
ApplyExtrinsicResult,
};
use bizinikiwi_test_runtime_client::{runtime::Transfer, Sr25519Keyring};
fn deny_unsafe() -> Extensions {
let mut ext = Extensions::new();
ext.insert(DenyUnsafe::Yes);
ext
}
fn allow_unsafe() -> Extensions {
let mut ext = Extensions::new();
ext.insert(DenyUnsafe::No);
ext
}
#[tokio::test]
async fn should_return_next_nonce_for_some_account() {
pezsp_tracing::try_init_simple();
// given
let client = Arc::new(bizinikiwi_test_runtime_client::new());
let spawner = pezsp_core::testing::TaskExecutor::new();
let pool = Arc::from(BasicPool::new_full(
Default::default(),
true.into(),
None,
spawner,
client.clone(),
));
let source = pezsp_runtime::transaction_validity::TransactionSource::External;
let new_transaction = |nonce: u64| {
let t = Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Bob.into(),
amount: 5,
nonce,
};
t.into_unchecked_extrinsic()
};
let hash_of_block0 = client.info().genesis_hash;
// Populate the pool
let ext0 = new_transaction(0);
block_on(pool.submit_one(hash_of_block0, source, ext0)).unwrap();
let ext1 = new_transaction(1);
block_on(pool.submit_one(hash_of_block0, source, ext1)).unwrap();
let accounts = System::new(client, pool);
// when
let nonce = accounts.nonce(Sr25519Keyring::Alice.into()).await;
// then
assert_eq!(nonce.unwrap(), 2);
}
#[tokio::test]
async fn dry_run_should_deny_unsafe() {
pezsp_tracing::try_init_simple();
// given
let client = Arc::new(bizinikiwi_test_runtime_client::new());
let spawner = pezsp_core::testing::TaskExecutor::new();
let pool = Arc::from(BasicPool::new_full(
Default::default(),
true.into(),
None,
spawner,
client.clone(),
));
let accounts = System::new(client, pool);
// when
let res = accounts.dry_run(&deny_unsafe(), vec![].into(), None).await;
assert_matches!(res, Err(e) => {
assert!(e.message().contains("RPC call is unsafe to be called externally"));
});
}
#[tokio::test]
async fn dry_run_should_work() {
pezsp_tracing::try_init_simple();
// given
let client = Arc::new(bizinikiwi_test_runtime_client::new());
let spawner = pezsp_core::testing::TaskExecutor::new();
let pool = Arc::from(BasicPool::new_full(
Default::default(),
true.into(),
None,
spawner,
client.clone(),
));
let accounts = System::new(client, pool);
let tx = Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Bob.into(),
amount: 5,
nonce: 0,
}
.into_unchecked_extrinsic();
// when
let bytes = accounts
.dry_run(&allow_unsafe(), tx.encode().into(), None)
.await
.expect("Call is successful");
// then
let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
assert_eq!(apply_res, Ok(Ok(())));
}
#[tokio::test]
async fn dry_run_should_indicate_error() {
pezsp_tracing::try_init_simple();
// given
let client = Arc::new(bizinikiwi_test_runtime_client::new());
let spawner = pezsp_core::testing::TaskExecutor::new();
let pool = Arc::from(BasicPool::new_full(
Default::default(),
true.into(),
None,
spawner,
client.clone(),
));
let accounts = System::new(client, pool);
let tx = Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Bob.into(),
amount: 5,
nonce: 100,
}
.into_unchecked_extrinsic();
// when
let bytes = accounts
.dry_run(&allow_unsafe(), tx.encode().into(), None)
.await
.expect("Call is successful");
// then
let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Future)));
}
}