mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 23:21:06 +00:00
archive: Fetch body, genesisHash and header (#1560)
This PR lays the foundation for implementing the archive RPC methods. The methods implemented by this PR: - archive_unstable_body: Fetch the block's body (a vector of hex-encoded scale-encoded extrinsics) from a given block hash - archive_unstable_genesisHash: Fetch the genesis hash - archive_unstable_header: Fetch the header from a given block hash Added unit tests for the methods. This PR is implementing the methods without exposing them to the RPC layer; which are to be exposed by a follow-up PR. Closes: https://github.com/paritytech/polkadot-sdk/issues/1509 Closes: https://github.com/paritytech/polkadot-sdk/issues/1514 @paritytech/subxt-team --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! API trait of the archive methods.
|
||||
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
|
||||
#[rpc(client, server)]
|
||||
pub trait ArchiveApi<Hash> {
|
||||
/// Retrieves the body (list of transactions) of a given block hash.
|
||||
///
|
||||
/// Returns an array of strings containing the hexadecimal-encoded SCALE-codec-encoded
|
||||
/// transactions in that block. If no block with that hash is found, null.
|
||||
///
|
||||
/// # Unstable
|
||||
///
|
||||
/// This method is unstable and subject to change in the future.
|
||||
#[method(name = "archive_unstable_body")]
|
||||
fn archive_unstable_body(&self, hash: Hash) -> RpcResult<Option<Vec<String>>>;
|
||||
|
||||
/// Get the chain's genesis hash.
|
||||
///
|
||||
/// Returns a string containing the hexadecimal-encoded hash of the genesis block of the chain.
|
||||
///
|
||||
/// # Unstable
|
||||
///
|
||||
/// This method is unstable and subject to change in the future.
|
||||
#[method(name = "archive_unstable_genesisHash")]
|
||||
fn archive_unstable_genesis_hash(&self) -> RpcResult<String>;
|
||||
|
||||
/// Get the block's header.
|
||||
///
|
||||
/// Returns a string containing the hexadecimal-encoded SCALE-codec encoding header of the
|
||||
/// block.
|
||||
///
|
||||
/// # Unstable
|
||||
///
|
||||
/// This method is unstable and subject to change in the future.
|
||||
#[method(name = "archive_unstable_header")]
|
||||
fn archive_unstable_header(&self, hash: Hash) -> RpcResult<Option<String>>;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! API implementation for `archive`.
|
||||
|
||||
use super::ArchiveApiServer;
|
||||
use crate::chain_head::hex_string;
|
||||
use codec::Encode;
|
||||
use jsonrpsee::core::{async_trait, RpcResult};
|
||||
use sc_client_api::{Backend, BlockBackend, BlockchainEvents, ExecutorProvider, StorageProvider};
|
||||
use sp_api::CallApiAt;
|
||||
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
/// An API for archive RPC calls.
|
||||
pub struct Archive<BE: Backend<Block>, Block: BlockT, Client> {
|
||||
/// Substrate client.
|
||||
client: Arc<Client>,
|
||||
/// The hexadecimal encoded hash of the genesis block.
|
||||
genesis_hash: String,
|
||||
/// Phantom member to pin the block type.
|
||||
_phantom: PhantomData<(Block, BE)>,
|
||||
}
|
||||
|
||||
impl<BE: Backend<Block>, Block: BlockT, Client> Archive<BE, Block, Client> {
|
||||
/// Create a new [`Archive`].
|
||||
pub fn new<GenesisHash: AsRef<[u8]>>(client: Arc<Client>, genesis_hash: GenesisHash) -> Self {
|
||||
let genesis_hash = hex_string(&genesis_hash.as_ref());
|
||||
Self { client, genesis_hash, _phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<BE, Block, Client> ArchiveApiServer<Block::Hash> for Archive<BE, Block, Client>
|
||||
where
|
||||
Block: BlockT + 'static,
|
||||
Block::Header: Unpin,
|
||||
BE: Backend<Block> + 'static,
|
||||
Client: BlockBackend<Block>
|
||||
+ ExecutorProvider<Block>
|
||||
+ HeaderBackend<Block>
|
||||
+ HeaderMetadata<Block, Error = BlockChainError>
|
||||
+ BlockchainEvents<Block>
|
||||
+ CallApiAt<Block>
|
||||
+ StorageProvider<Block, BE>
|
||||
+ 'static,
|
||||
{
|
||||
fn archive_unstable_body(&self, hash: Block::Hash) -> RpcResult<Option<Vec<String>>> {
|
||||
let Ok(Some(signed_block)) = self.client.block(hash) else { return Ok(None) };
|
||||
|
||||
let extrinsics = signed_block
|
||||
.block
|
||||
.extrinsics()
|
||||
.iter()
|
||||
.map(|extrinsic| hex_string(&extrinsic.encode()))
|
||||
.collect();
|
||||
|
||||
Ok(Some(extrinsics))
|
||||
}
|
||||
|
||||
fn archive_unstable_genesis_hash(&self) -> RpcResult<String> {
|
||||
Ok(self.genesis_hash.clone())
|
||||
}
|
||||
|
||||
fn archive_unstable_header(&self, hash: Block::Hash) -> RpcResult<Option<String>> {
|
||||
let Ok(Some(header)) = self.client.header(hash) else { return Ok(None) };
|
||||
|
||||
Ok(Some(hex_string(&header.encode())))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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/>.
|
||||
|
||||
//! Substrate archive API.
|
||||
//!
|
||||
//! # Note
|
||||
//!
|
||||
//! Methods are prefixed by `archive`.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod api;
|
||||
pub mod archive;
|
||||
|
||||
pub use api::ArchiveApiServer;
|
||||
@@ -0,0 +1,113 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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 crate::chain_head::hex_string;
|
||||
|
||||
use super::{archive::Archive, *};
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use jsonrpsee::{types::EmptyServerParams as EmptyParams, RpcModule};
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
|
||||
use sp_consensus::BlockOrigin;
|
||||
use std::sync::Arc;
|
||||
use substrate_test_runtime_client::{
|
||||
prelude::*, runtime, Backend, BlockBuilderExt, Client, ClientBlockImportExt,
|
||||
};
|
||||
|
||||
const CHAIN_GENESIS: [u8; 32] = [0; 32];
|
||||
const INVALID_HASH: [u8; 32] = [1; 32];
|
||||
|
||||
type Header = substrate_test_runtime_client::runtime::Header;
|
||||
type Block = substrate_test_runtime_client::runtime::Block;
|
||||
|
||||
fn setup_api() -> (Arc<Client<Backend>>, RpcModule<Archive<Backend, Block, Client<Backend>>>) {
|
||||
let builder = TestClientBuilder::new();
|
||||
let client = Arc::new(builder.build());
|
||||
|
||||
let api = Archive::new(client.clone(), CHAIN_GENESIS).into_rpc();
|
||||
|
||||
(client, api)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn archive_genesis() {
|
||||
let (_client, api) = setup_api();
|
||||
|
||||
let genesis: String =
|
||||
api.call("archive_unstable_genesisHash", EmptyParams::new()).await.unwrap();
|
||||
assert_eq!(genesis, hex_string(&CHAIN_GENESIS));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn archive_body() {
|
||||
let (mut client, api) = setup_api();
|
||||
|
||||
// Invalid block hash.
|
||||
let invalid_hash = hex_string(&INVALID_HASH);
|
||||
let res: Option<Vec<String>> = api.call("archive_unstable_body", [invalid_hash]).await.unwrap();
|
||||
assert!(res.is_none());
|
||||
|
||||
// Import a new block with an extrinsic.
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder
|
||||
.push_transfer(runtime::Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 42,
|
||||
nonce: 0,
|
||||
})
|
||||
.unwrap();
|
||||
let block = builder.build().unwrap().block;
|
||||
let block_hash = format!("{:?}", block.header.hash());
|
||||
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
|
||||
|
||||
let expected_tx = hex_string(&block.extrinsics[0].encode());
|
||||
|
||||
let body: Vec<String> = api.call("archive_unstable_body", [block_hash]).await.unwrap();
|
||||
assert_eq!(vec![expected_tx], body);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn archive_header() {
|
||||
let (mut client, api) = setup_api();
|
||||
|
||||
// Invalid block hash.
|
||||
let invalid_hash = hex_string(&INVALID_HASH);
|
||||
let res: Option<String> = api.call("archive_unstable_header", [invalid_hash]).await.unwrap();
|
||||
assert!(res.is_none());
|
||||
|
||||
// Import a new block with an extrinsic.
|
||||
let mut builder = client.new_block(Default::default()).unwrap();
|
||||
builder
|
||||
.push_transfer(runtime::Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 42,
|
||||
nonce: 0,
|
||||
})
|
||||
.unwrap();
|
||||
let block = builder.build().unwrap().block;
|
||||
let block_hash = format!("{:?}", block.header.hash());
|
||||
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
|
||||
|
||||
let header: String = api.call("archive_unstable_header", [block_hash]).await.unwrap();
|
||||
let bytes = array_bytes::hex2bytes(&header).unwrap();
|
||||
let header: Header = Decode::decode(&mut &bytes[..]).unwrap();
|
||||
assert_eq!(header, block.header);
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
pub mod archive;
|
||||
pub mod chain_head;
|
||||
pub mod chain_spec;
|
||||
pub mod transaction;
|
||||
|
||||
Reference in New Issue
Block a user