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,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(¤t_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(¤t_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)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user