mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 10:11:03 +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:
@@ -11,6 +11,8 @@ use jsonrpsee::{
|
||||
};
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::ChildInfo;
|
||||
use sc_service::client::new_in_mem;
|
||||
use sp_api::BlockT;
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_consensus::BlockOrigin;
|
||||
use sp_core::{
|
||||
@@ -19,15 +21,17 @@ use sp_core::{
|
||||
testing::TaskExecutor,
|
||||
};
|
||||
use sp_version::RuntimeVersion;
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use substrate_test_runtime::Transfer;
|
||||
use substrate_test_runtime_client::{
|
||||
prelude::*, runtime, Backend, BlockBuilderExt, Client, ClientBlockImportExt,
|
||||
prelude::*, runtime, runtime::RuntimeApi, Backend, BlockBuilderExt, Client,
|
||||
ClientBlockImportExt, GenesisInit,
|
||||
};
|
||||
|
||||
type Header = substrate_test_runtime_client::runtime::Header;
|
||||
type Block = substrate_test_runtime_client::runtime::Block;
|
||||
const MAX_PINNED_BLOCKS: usize = 32;
|
||||
const MAX_PINNED_SECS: u64 = 60;
|
||||
const CHAIN_GENESIS: [u8; 32] = [0; 32];
|
||||
const INVALID_HASH: [u8; 32] = [1; 32];
|
||||
const KEY: &[u8] = b":mock";
|
||||
@@ -72,6 +76,7 @@ async fn setup_api() -> (
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -111,6 +116,7 @@ async fn follow_subscription_produces_blocks() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -168,6 +174,7 @@ async fn follow_with_runtime() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -273,6 +280,7 @@ async fn get_genesis() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -457,6 +465,7 @@ async fn call_runtime_without_flag() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -631,6 +640,7 @@ async fn follow_generates_initial_blocks() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -758,6 +768,7 @@ async fn follow_exceeding_pinned_blocks() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
2,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -808,6 +819,7 @@ async fn follow_with_unpin() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
2,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -888,6 +900,7 @@ async fn follow_prune_best_block() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -1044,6 +1057,7 @@ async fn follow_forks_pruned_block() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -1157,6 +1171,7 @@ async fn follow_report_multiple_pruned_block() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
@@ -1327,6 +1342,137 @@ async fn follow_report_multiple_pruned_block() {
|
||||
assert_eq!(event, expected);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn pin_block_references() {
|
||||
// Manually construct an in-memory backend and client.
|
||||
let backend = Arc::new(sc_client_api::in_mem::Backend::new());
|
||||
let executor = substrate_test_runtime_client::new_native_or_wasm_executor();
|
||||
let client_config = sc_service::ClientConfig::default();
|
||||
|
||||
let genesis_block_builder = sc_service::GenesisBlockBuilder::new(
|
||||
&substrate_test_runtime_client::GenesisParameters::default().genesis_storage(),
|
||||
!client_config.no_genesis,
|
||||
backend.clone(),
|
||||
executor.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut client = Arc::new(
|
||||
new_in_mem::<_, Block, _, RuntimeApi>(
|
||||
backend.clone(),
|
||||
executor,
|
||||
genesis_block_builder,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Box::new(TaskExecutor::new()),
|
||||
client_config,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let api = ChainHead::new(
|
||||
client.clone(),
|
||||
backend.clone(),
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
3,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
async fn wait_pinned_references<Block: BlockT>(
|
||||
backend: &Arc<sc_client_api::in_mem::Backend<Block>>,
|
||||
hash: &Block::Hash,
|
||||
target: i64,
|
||||
) {
|
||||
// Retry for at most 2 minutes.
|
||||
let mut retries = 120;
|
||||
while backend.pin_refs(hash).unwrap() != target {
|
||||
if retries == 0 {
|
||||
panic!("Expected target={} pinned references for hash={:?}", target, hash);
|
||||
}
|
||||
retries -= 1;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap();
|
||||
let sub_id = sub.subscription_id();
|
||||
let sub_id = serde_json::to_string(&sub_id).unwrap();
|
||||
|
||||
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||
let hash = block.header.hash();
|
||||
let block_hash = format!("{:?}", hash);
|
||||
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
|
||||
|
||||
// Ensure the imported block is propagated for this subscription.
|
||||
assert_matches!(
|
||||
get_next_event::<FollowEvent<String>>(&mut sub).await,
|
||||
FollowEvent::Initialized(_)
|
||||
);
|
||||
assert_matches!(
|
||||
get_next_event::<FollowEvent<String>>(&mut sub).await,
|
||||
FollowEvent::NewBlock(_)
|
||||
);
|
||||
assert_matches!(
|
||||
get_next_event::<FollowEvent<String>>(&mut sub).await,
|
||||
FollowEvent::BestBlockChanged(_)
|
||||
);
|
||||
|
||||
// We need to wait a bit for:
|
||||
// 1. `NewBlock` and `BestBlockChanged` notifications to propagate to the chainHead
|
||||
// subscription. (pin_refs == 2)
|
||||
// 2. The chainHead to call `pin_blocks` only once for the `NewBlock`
|
||||
// notification (pin_refs == 3)
|
||||
// 3. Both notifications to go out of scope (pin_refs == 1 (total 3 - dropped 2)).
|
||||
wait_pinned_references(&backend, &hash, 1).await;
|
||||
|
||||
// To not exceed the number of pinned blocks, we need to unpin before the next import.
|
||||
let _res: () = api.call("chainHead_unstable_unpin", [&sub_id, &block_hash]).await.unwrap();
|
||||
|
||||
// Make sure unpin clears out the reference.
|
||||
let refs = backend.pin_refs(&hash).unwrap();
|
||||
assert_eq!(refs, 0);
|
||||
|
||||
// Add another 2 blocks and make sure we drop the subscription with the blocks pinned.
|
||||
let mut hashes = Vec::new();
|
||||
for _ in 0..2 {
|
||||
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||
let hash = block.header.hash();
|
||||
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
|
||||
|
||||
// Ensure the imported block is propagated for this subscription.
|
||||
assert_matches!(
|
||||
get_next_event::<FollowEvent<String>>(&mut sub).await,
|
||||
FollowEvent::NewBlock(_)
|
||||
);
|
||||
assert_matches!(
|
||||
get_next_event::<FollowEvent<String>>(&mut sub).await,
|
||||
FollowEvent::BestBlockChanged(_)
|
||||
);
|
||||
|
||||
hashes.push(hash);
|
||||
}
|
||||
|
||||
// Make sure the pin was propagated.
|
||||
for hash in &hashes {
|
||||
wait_pinned_references(&backend, hash, 1).await;
|
||||
}
|
||||
|
||||
// Drop the subscription and expect the pinned blocks to be released.
|
||||
drop(sub);
|
||||
// The `chainHead` detects the subscription was terminated when it tries
|
||||
// to send another block.
|
||||
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
|
||||
|
||||
for hash in &hashes {
|
||||
wait_pinned_references(&backend, &hash, 0).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn follow_finalized_before_new_block() {
|
||||
let builder = TestClientBuilder::new();
|
||||
@@ -1341,6 +1487,7 @@ async fn follow_finalized_before_new_block() {
|
||||
Arc::new(TaskExecutor::default()),
|
||||
CHAIN_GENESIS,
|
||||
MAX_PINNED_BLOCKS,
|
||||
Duration::from_secs(MAX_PINNED_SECS),
|
||||
)
|
||||
.into_rpc();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user