mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-18 12:55:41 +00:00
rpc: Use the blocks pinning API for chainHead methods (#13233)
* rpc/chain_head: Add backend to subscription management Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Pin blocks internally and adjust testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client/in_mem: Reference for the number of pinned blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/tests: Check in-memory references to pinned blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Remove unused comment Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Place subscription handle under `Arc` and unpin blocks on drop Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/tests: Check all pinned blocks are unpinned on drop Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Bastian Köcher <git@kchr.de> * rpc/tests: Retry fetching the pinned references for CI correctness Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client/service: Use 512 as maximum number of pinned blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head: Fix merging conflicts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Adjust subscriptions to use pinning API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head/tests: Test subscription management Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Adjust chain_head follow to the new API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Adjust chain_head.rs to the new API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head/tests: Adjust test.rs to the new API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client/builder: Use new chainHead API Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Fix documentation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc/chain_head: Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client/in_mem: ChainHead no longer uses `in_mem::children` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert <skunert49@gmail.com> * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert <skunert49@gmail.com> * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert <skunert49@gmail.com> * Update client/rpc-spec-v2/src/chain_head/subscription.rs Co-authored-by: Sebastian Kunert <skunert49@gmail.com> * chain_head: Add block state machine Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Address feedback Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Use new_native_or_wasm_executor Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head: Remove 'static on Backend Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head: Add documentation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head: Lock blocks before async blocks Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head_follower: Remove static on backend Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update client/service/src/builder.rs Co-authored-by: Davide Galassi <davxy@datawok.net> * Update client/service/src/builder.rs Co-authored-by: Davide Galassi <davxy@datawok.net> * chain_head: Add BlockHeaderAbsent to the PartialEq impl Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client: Add better documentation around pinning constants Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * chain_head: Move subscription to dedicated module Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subscription: Rename global pin / unpin functions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: parity-processbot <> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Davide Galassi <davxy@datawok.net>
This commit is contained in:
@@ -24,7 +24,7 @@ use crate::{
|
||||
chain_head_follow::ChainHeadFollower,
|
||||
error::Error as ChainHeadRpcError,
|
||||
event::{ChainHeadEvent, ChainHeadResult, ErrorEvent, FollowEvent, NetworkConfig},
|
||||
subscription::SubscriptionManagement,
|
||||
subscription::{SubscriptionManagement, SubscriptionManagementError},
|
||||
},
|
||||
SubscriptionTaskExecutor,
|
||||
};
|
||||
@@ -44,12 +44,12 @@ use sp_api::CallApiAt;
|
||||
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
|
||||
use sp_core::{hexdisplay::HexDisplay, storage::well_known_keys, traits::CallContext, Bytes};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
use std::{marker::PhantomData, sync::Arc, time::Duration};
|
||||
|
||||
pub(crate) const LOG_TARGET: &str = "rpc-spec-v2";
|
||||
|
||||
/// An API for chain head RPC calls.
|
||||
pub struct ChainHead<BE, Block: BlockT, Client> {
|
||||
pub struct ChainHead<BE: Backend<Block>, Block: BlockT, Client> {
|
||||
/// Substrate client.
|
||||
client: Arc<Client>,
|
||||
/// Backend of the chain.
|
||||
@@ -57,16 +57,14 @@ pub struct ChainHead<BE, Block: BlockT, Client> {
|
||||
/// Executor to spawn subscriptions.
|
||||
executor: SubscriptionTaskExecutor,
|
||||
/// Keep track of the pinned blocks for each subscription.
|
||||
subscriptions: Arc<SubscriptionManagement<Block>>,
|
||||
subscriptions: Arc<SubscriptionManagement<Block, BE>>,
|
||||
/// The hexadecimal encoded hash of the genesis block.
|
||||
genesis_hash: String,
|
||||
/// The maximum number of pinned blocks allowed per connection.
|
||||
max_pinned_blocks: usize,
|
||||
/// Phantom member to pin the block type.
|
||||
_phantom: PhantomData<Block>,
|
||||
}
|
||||
|
||||
impl<BE, Block: BlockT, Client> ChainHead<BE, Block, Client> {
|
||||
impl<BE: Backend<Block>, Block: BlockT, Client> ChainHead<BE, Block, Client> {
|
||||
/// Create a new [`ChainHead`].
|
||||
pub fn new<GenesisHash: AsRef<[u8]>>(
|
||||
client: Arc<Client>,
|
||||
@@ -74,16 +72,20 @@ impl<BE, Block: BlockT, Client> ChainHead<BE, Block, Client> {
|
||||
executor: SubscriptionTaskExecutor,
|
||||
genesis_hash: GenesisHash,
|
||||
max_pinned_blocks: usize,
|
||||
max_pinned_duration: Duration,
|
||||
) -> Self {
|
||||
let genesis_hash = format!("0x{:?}", HexDisplay::from(&genesis_hash.as_ref()));
|
||||
|
||||
Self {
|
||||
client,
|
||||
backend,
|
||||
backend: backend.clone(),
|
||||
executor,
|
||||
subscriptions: Arc::new(SubscriptionManagement::new()),
|
||||
subscriptions: Arc::new(SubscriptionManagement::new(
|
||||
max_pinned_blocks,
|
||||
max_pinned_duration,
|
||||
backend,
|
||||
)),
|
||||
genesis_hash,
|
||||
max_pinned_blocks,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -159,9 +161,8 @@ where
|
||||
return Err(err)
|
||||
},
|
||||
};
|
||||
|
||||
// Keep track of the subscription.
|
||||
let Some((rx_stop, sub_handle)) = self.subscriptions.insert_subscription(sub_id.clone(), runtime_updates, self.max_pinned_blocks) else {
|
||||
let Some(rx_stop) = self.subscriptions.insert_subscription(sub_id.clone(), runtime_updates) else {
|
||||
// Inserting the subscription can only fail if the JsonRPSee
|
||||
// generated a duplicate subscription ID.
|
||||
debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id);
|
||||
@@ -177,7 +178,7 @@ where
|
||||
let mut chain_head_follow = ChainHeadFollower::new(
|
||||
client,
|
||||
backend,
|
||||
sub_handle,
|
||||
subscriptions.clone(),
|
||||
runtime_updates,
|
||||
sub_id.clone(),
|
||||
);
|
||||
@@ -202,19 +203,28 @@ where
|
||||
let client = self.client.clone();
|
||||
let subscriptions = self.subscriptions.clone();
|
||||
|
||||
let fut = async move {
|
||||
let Some(handle) = subscriptions.get_subscription(&follow_subscription) else {
|
||||
let block_guard = match subscriptions.lock_block(&follow_subscription, hash) {
|
||||
Ok(block) => block,
|
||||
Err(SubscriptionManagementError::SubscriptionAbsent) => {
|
||||
// Invalid invalid subscription ID.
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Disjoint);
|
||||
return
|
||||
};
|
||||
|
||||
// Block is not part of the subscription.
|
||||
if !handle.contains_block(&hash) {
|
||||
return Ok(())
|
||||
},
|
||||
Err(SubscriptionManagementError::BlockHashAbsent) => {
|
||||
// Block is not part of the subscription.
|
||||
let _ = sink.reject(ChainHeadRpcError::InvalidBlock);
|
||||
return
|
||||
}
|
||||
return Ok(())
|
||||
},
|
||||
Err(error) => {
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent {
|
||||
error: error.to_string(),
|
||||
}));
|
||||
return Ok(())
|
||||
},
|
||||
};
|
||||
|
||||
let fut = async move {
|
||||
let _block_guard = block_guard;
|
||||
let event = match client.block(hash) {
|
||||
Ok(Some(signed_block)) => {
|
||||
let extrinsics = signed_block.block.extrinsics();
|
||||
@@ -226,10 +236,10 @@ where
|
||||
debug!(
|
||||
target: LOG_TARGET,
|
||||
"[body][id={:?}] Stopping subscription because hash={:?} was pruned",
|
||||
follow_subscription,
|
||||
&follow_subscription,
|
||||
hash
|
||||
);
|
||||
handle.stop();
|
||||
subscriptions.remove_subscription(&follow_subscription);
|
||||
ChainHeadEvent::<String>::Disjoint
|
||||
},
|
||||
Err(error) => ChainHeadEvent::Error(ErrorEvent { error: error.to_string() }),
|
||||
@@ -246,16 +256,19 @@ where
|
||||
follow_subscription: String,
|
||||
hash: Block::Hash,
|
||||
) -> RpcResult<Option<String>> {
|
||||
let Some(handle) = self.subscriptions.get_subscription(&follow_subscription) else {
|
||||
// Invalid invalid subscription ID.
|
||||
return Ok(None)
|
||||
let _block_guard = match self.subscriptions.lock_block(&follow_subscription, hash) {
|
||||
Ok(block) => block,
|
||||
Err(SubscriptionManagementError::SubscriptionAbsent) => {
|
||||
// Invalid invalid subscription ID.
|
||||
return Ok(None)
|
||||
},
|
||||
Err(SubscriptionManagementError::BlockHashAbsent) => {
|
||||
// Block is not part of the subscription.
|
||||
return Err(ChainHeadRpcError::InvalidBlock.into())
|
||||
},
|
||||
Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()),
|
||||
};
|
||||
|
||||
// Block is not part of the subscription.
|
||||
if !handle.contains_block(&hash) {
|
||||
return Err(ChainHeadRpcError::InvalidBlock.into())
|
||||
}
|
||||
|
||||
self.client
|
||||
.header(hash)
|
||||
.map(|opt_header| opt_header.map(|h| format!("0x{:?}", HexDisplay::from(&h.encode()))))
|
||||
@@ -286,19 +299,28 @@ where
|
||||
let client = self.client.clone();
|
||||
let subscriptions = self.subscriptions.clone();
|
||||
|
||||
let fut = async move {
|
||||
let Some(handle) = subscriptions.get_subscription(&follow_subscription) else {
|
||||
let block_guard = match subscriptions.lock_block(&follow_subscription, hash) {
|
||||
Ok(block) => block,
|
||||
Err(SubscriptionManagementError::SubscriptionAbsent) => {
|
||||
// Invalid invalid subscription ID.
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Disjoint);
|
||||
return
|
||||
};
|
||||
|
||||
// Block is not part of the subscription.
|
||||
if !handle.contains_block(&hash) {
|
||||
return Ok(())
|
||||
},
|
||||
Err(SubscriptionManagementError::BlockHashAbsent) => {
|
||||
// Block is not part of the subscription.
|
||||
let _ = sink.reject(ChainHeadRpcError::InvalidBlock);
|
||||
return
|
||||
}
|
||||
return Ok(())
|
||||
},
|
||||
Err(error) => {
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent {
|
||||
error: error.to_string(),
|
||||
}));
|
||||
return Ok(())
|
||||
},
|
||||
};
|
||||
|
||||
let fut = async move {
|
||||
let _block_guard = block_guard;
|
||||
// The child key is provided, use the key to query the child trie.
|
||||
if let Some(child_key) = child_key {
|
||||
// The child key must not be prefixed with ":child_storage:" nor
|
||||
@@ -367,21 +389,29 @@ where
|
||||
let client = self.client.clone();
|
||||
let subscriptions = self.subscriptions.clone();
|
||||
|
||||
let fut = async move {
|
||||
let Some(handle) = subscriptions.get_subscription(&follow_subscription) else {
|
||||
let block_guard = match subscriptions.lock_block(&follow_subscription, hash) {
|
||||
Ok(block) => block,
|
||||
Err(SubscriptionManagementError::SubscriptionAbsent) => {
|
||||
// Invalid invalid subscription ID.
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Disjoint);
|
||||
return
|
||||
};
|
||||
|
||||
// Block is not part of the subscription.
|
||||
if !handle.contains_block(&hash) {
|
||||
return Ok(())
|
||||
},
|
||||
Err(SubscriptionManagementError::BlockHashAbsent) => {
|
||||
// Block is not part of the subscription.
|
||||
let _ = sink.reject(ChainHeadRpcError::InvalidBlock);
|
||||
return
|
||||
}
|
||||
return Ok(())
|
||||
},
|
||||
Err(error) => {
|
||||
let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent {
|
||||
error: error.to_string(),
|
||||
}));
|
||||
return Ok(())
|
||||
},
|
||||
};
|
||||
|
||||
let fut = async move {
|
||||
// Reject subscription if runtime_updates is false.
|
||||
if !handle.has_runtime_updates() {
|
||||
if !block_guard.has_runtime_updates() {
|
||||
let _ = sink.reject(ChainHeadRpcError::InvalidParam(
|
||||
"The runtime updates flag must be set".into(),
|
||||
));
|
||||
@@ -417,15 +447,17 @@ where
|
||||
follow_subscription: String,
|
||||
hash: Block::Hash,
|
||||
) -> RpcResult<()> {
|
||||
let Some(handle) = self.subscriptions.get_subscription(&follow_subscription) else {
|
||||
// Invalid invalid subscription ID.
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
if !handle.unpin_block(&hash) {
|
||||
return Err(ChainHeadRpcError::InvalidBlock.into())
|
||||
match self.subscriptions.unpin_block(&follow_subscription, hash) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(SubscriptionManagementError::SubscriptionAbsent) => {
|
||||
// Invalid invalid subscription ID.
|
||||
Ok(())
|
||||
},
|
||||
Err(SubscriptionManagementError::BlockHashAbsent) => {
|
||||
// Block is not part of the subscription.
|
||||
Err(ChainHeadRpcError::InvalidBlock.into())
|
||||
},
|
||||
Err(_) => Err(ChainHeadRpcError::InvalidBlock.into()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user