Skip to content
Snippets Groups Projects
Unverified Commit f3061c14 authored by Alexandru Vasile's avatar Alexandru Vasile Committed by GitHub
Browse files

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: default avatarAlexandru Vasile <alexandru.vasile@parity.io>
parent 6079b6dd
Branches
No related merge requests found
Pipeline #392079 canceled with stages
in 16 minutes and 44 seconds
// 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>>;
}
// 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())))
}
}
// 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;
// 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;
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment