chainHead: Support multiple hashes for chainHead_unpin method (#2295)

This PR adds support for multiple hashes being passed to the
`chainHeda_unpin` parameters.

The `hash` parameter is renamed to `hash_or_hashes` per
https://github.com/paritytech/json-rpc-interface-spec/pull/111.

While at it, a new integration test is added to check the unpinning of
multiple hashes. The API is checked against a hash or a vector of
hashes.

cc @paritytech/subxt-team

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
Alexandru Vasile
2023-11-14 16:54:36 +02:00
committed by GitHub
parent b70d418f89
commit cfe5e62626
7 changed files with 224 additions and 29 deletions
@@ -1591,14 +1591,17 @@ async fn follow_with_unpin() {
// Unpin an invalid subscription ID must return Ok(()).
let invalid_hash = hex_string(&INVALID_HASH);
let _res: () = api
.call("chainHead_unstable_unpin", ["invalid_sub_id", &invalid_hash])
.call("chainHead_unstable_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
.await
.unwrap();
// Valid subscription with invalid block hash.
let invalid_hash = hex_string(&INVALID_HASH);
let err = api
.call::<_, serde_json::Value>("chainHead_unstable_unpin", [&sub_id, &invalid_hash])
.call::<_, serde_json::Value>(
"chainHead_unstable_unpin",
rpc_params![&sub_id, &invalid_hash],
)
.await
.unwrap_err();
assert_matches!(err,
@@ -1606,7 +1609,10 @@ async fn follow_with_unpin() {
);
// 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();
let _res: () = api
.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_hash])
.await
.unwrap();
// Block tree:
// finalized_block -> block -> block2
@@ -1645,6 +1651,160 @@ async fn follow_with_unpin() {
assert!(sub.next::<FollowEvent<String>>().await.is_none());
}
#[tokio::test]
async fn follow_with_multiple_unpin_hashes() {
let builder = TestClientBuilder::new();
let backend = builder.backend();
let mut client = Arc::new(builder.build());
let api = ChainHead::new(
client.clone(),
backend,
Arc::new(TaskExecutor::default()),
CHAIN_GENESIS,
ChainHeadConfig {
global_max_pinned_blocks: MAX_PINNED_BLOCKS,
subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS),
subscription_max_ongoing_operations: MAX_OPERATIONS,
operation_max_storage_items: MAX_PAGINATION_LIMIT,
},
)
.into_rpc();
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();
// Import 3 blocks.
let block_1 = BlockBuilderBuilder::new(&*client)
.on_parent_block(client.chain_info().genesis_hash)
.with_parent_block_number(0)
.build()
.unwrap()
.build()
.unwrap()
.block;
let block_1_hash = block_1.header.hash();
client.import(BlockOrigin::Own, block_1.clone()).await.unwrap();
let block_2 = BlockBuilderBuilder::new(&*client)
.on_parent_block(block_1.hash())
.with_parent_block_number(1)
.build()
.unwrap()
.build()
.unwrap()
.block;
let block_2_hash = block_2.header.hash();
client.import(BlockOrigin::Own, block_2.clone()).await.unwrap();
let block_3 = BlockBuilderBuilder::new(&*client)
.on_parent_block(block_2.hash())
.with_parent_block_number(2)
.build()
.unwrap()
.build()
.unwrap()
.block;
let block_3_hash = block_3.header.hash();
client.import(BlockOrigin::Own, block_3.clone()).await.unwrap();
// Ensure the imported block is propagated and pinned 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(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::NewBlock(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::BestBlockChanged(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::NewBlock(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::BestBlockChanged(_)
);
// Unpin an invalid subscription ID must return Ok(()).
let invalid_hash = hex_string(&INVALID_HASH);
let _res: () = api
.call("chainHead_unstable_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
.await
.unwrap();
// Valid subscription with invalid block hash.
let err = api
.call::<_, serde_json::Value>(
"chainHead_unstable_unpin",
rpc_params![&sub_id, &invalid_hash],
)
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
);
let _res: () = api
.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_1_hash])
.await
.unwrap();
// One block hash is invalid. Block 1 is already unpinned.
let err = api
.call::<_, serde_json::Value>(
"chainHead_unstable_unpin",
rpc_params![&sub_id, vec![&block_1_hash, &block_2_hash, &block_3_hash]],
)
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
);
// Unpin multiple blocks.
let _res: () = api
.call("chainHead_unstable_unpin", rpc_params![&sub_id, vec![&block_2_hash, &block_3_hash]])
.await
.unwrap();
// Check block 2 and 3 are unpinned.
let err = api
.call::<_, serde_json::Value>(
"chainHead_unstable_unpin",
rpc_params![&sub_id, &block_2_hash],
)
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
);
let err = api
.call::<_, serde_json::Value>(
"chainHead_unstable_unpin",
rpc_params![&sub_id, &block_3_hash],
)
.await
.unwrap_err();
assert_matches!(err,
Error::Call(CallError::Custom(ref err)) if err.code() == 2001 && err.message() == "Invalid block hash"
);
}
#[tokio::test]
async fn follow_prune_best_block() {
let builder = TestClientBuilder::new();
@@ -1828,7 +1988,7 @@ async fn follow_prune_best_block() {
let sub_id = sub.subscription_id();
let sub_id = serde_json::to_string(&sub_id).unwrap();
let hash = format!("{:?}", block_2_hash);
let _res: () = api.call("chainHead_unstable_unpin", [&sub_id, &hash]).await.unwrap();
let _res: () = api.call("chainHead_unstable_unpin", rpc_params![&sub_id, &hash]).await.unwrap();
}
#[tokio::test]
@@ -2305,7 +2465,10 @@ async fn pin_block_references() {
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();
let _res: () = api
.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_hash])
.await
.unwrap();
// Make sure unpin clears out the reference.
let refs = backend.pin_refs(&hash).unwrap();