-
Notifications
You must be signed in to change notification settings - Fork 2.6k
rpc: Use the blocks pinning API for chainHead methods #13233
Changes from 34 commits
a72b8a8
d672047
c9a0d7e
3536c94
bdfdb7d
43d6825
6bc2e87
9015061
09773d3
4a38133
e509399
3c2cc6c
c6bfb6f
09fe732
b7dcacd
d67c6ed
fa5323f
852c302
cdd1428
ad9e921
866bb87
b2e08ce
b63f102
c46d7d4
7da0406
5561e56
d36f748
ff3c9d5
9781584
cc31043
efb14c4
10b61d5
c5266cc
f5a911f
7bf9da0
6c054ba
6a3f9e5
d287533
6073ae2
871dcb8
083e3c3
1bb4b00
04a167c
cc24ad1
b3ca736
3b1764f
9cd53f1
8b0eaff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,46 +44,48 @@ 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> + 'static, Block: BlockT, Client> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a nit; nowadays I'm leaning towards not putting any trait bounds on generic params in structs unless they are needed for the definition of the struct (though I've been back and forth on this myself), so I guess I'd lean towards removing the bounds if possible (but curious on what others think!)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I do like that approach. One nice thing about not having bounds on the structure is that we do not contaminate every structure containing that type. For soundness reasons I presume, the compiler requires the bounds for the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for this ; TIL
lexnv marked this conversation as resolved.
Outdated
|
||
| /// Substrate client. | ||
| client: Arc<Client>, | ||
| /// Backend of the chain. | ||
| backend: Arc<BE>, | ||
| /// 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> + 'static, Block: BlockT, Client> ChainHead<BE, Block, Client> { | ||
| /// Create a new [`ChainHead`]. | ||
| pub fn new<GenesisHash: AsRef<[u8]>>( | ||
| client: Arc<Client>, | ||
| backend: Arc<BE>, | ||
| 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(), | ||
| ); | ||
|
|
@@ -203,18 +204,26 @@ where | |
| let subscriptions = self.subscriptions.clone(); | ||
|
|
||
| let fut = async move { | ||
| let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { | ||
| // Invalid invalid subscription ID. | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Disjoint); | ||
| return | ||
| let _block_guard = match subscriptions.lock_block(&follow_subscription, hash) { | ||
|
skunert marked this conversation as resolved.
Outdated
lexnv marked this conversation as resolved.
Outdated
|
||
| Ok(block) => block, | ||
|
lexnv marked this conversation as resolved.
Outdated
|
||
| Err(SubscriptionManagementError::SubscriptionAbsent) => { | ||
| // Invalid invalid subscription ID. | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Disjoint); | ||
| return | ||
| }, | ||
| Err(SubscriptionManagementError::BlockHashAbsent) => { | ||
| // Block is not part of the subscription. | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| }, | ||
| Err(error) => { | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent { | ||
| error: error.to_string(), | ||
| })); | ||
| return | ||
| }, | ||
| }; | ||
|
|
||
| // Block is not part of the subscription. | ||
| if !handle.contains_block(&hash) { | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| } | ||
|
|
||
| let event = match client.block(hash) { | ||
| Ok(Some(signed_block)) => { | ||
| let extrinsics = signed_block.block.extrinsics(); | ||
|
|
@@ -226,10 +235,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 +255,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())))) | ||
|
|
@@ -287,18 +299,26 @@ where | |
| let subscriptions = self.subscriptions.clone(); | ||
|
|
||
| let fut = async move { | ||
| let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { | ||
| // Invalid invalid subscription ID. | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Disjoint); | ||
| return | ||
| let _block_guard = match subscriptions.lock_block(&follow_subscription, hash) { | ||
|
lexnv marked this conversation as resolved.
Outdated
|
||
| Ok(block) => block, | ||
| Err(SubscriptionManagementError::SubscriptionAbsent) => { | ||
| // Invalid invalid subscription ID. | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Disjoint); | ||
| return | ||
| }, | ||
| Err(SubscriptionManagementError::BlockHashAbsent) => { | ||
| // Block is not part of the subscription. | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| }, | ||
| Err(error) => { | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent { | ||
| error: error.to_string(), | ||
| })); | ||
| return | ||
| }, | ||
| }; | ||
|
|
||
| // Block is not part of the subscription. | ||
| if !handle.contains_block(&hash) { | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| } | ||
|
|
||
| // 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 | ||
|
|
@@ -368,20 +388,28 @@ where | |
| let subscriptions = self.subscriptions.clone(); | ||
|
|
||
| let fut = async move { | ||
| let Some(handle) = subscriptions.get_subscription(&follow_subscription) else { | ||
| // Invalid invalid subscription ID. | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Disjoint); | ||
| return | ||
| 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 | ||
| }, | ||
| Err(SubscriptionManagementError::BlockHashAbsent) => { | ||
| // Block is not part of the subscription. | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| }, | ||
| Err(error) => { | ||
| let _ = sink.send(&ChainHeadEvent::<String>::Error(ErrorEvent { | ||
| error: error.to_string(), | ||
| })); | ||
| return | ||
| }, | ||
| }; | ||
|
|
||
| // Block is not part of the subscription. | ||
| if !handle.contains_block(&hash) { | ||
| let _ = sink.reject(ChainHeadRpcError::InvalidBlock); | ||
| return | ||
| } | ||
|
|
||
| // 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 +445,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(()) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.