-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix the storage_size/state_getStorageSize RPC call
#13154
Changes from 2 commits
c84d026
83cd8e8
027a718
b83d731
cdbb894
e3f6555
8ed99ed
2868c17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,17 +18,20 @@ | |
|
|
||
| //! State API backend for full nodes. | ||
|
|
||
| use std::{collections::HashMap, marker::PhantomData, sync::Arc}; | ||
| use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; | ||
|
|
||
| use super::{ | ||
| client_err, | ||
| error::{Error, Result}, | ||
| ChildStateBackend, StateBackend, | ||
| }; | ||
| use crate::SubscriptionTaskExecutor; | ||
| use crate::{DenyUnsafe, SubscriptionTaskExecutor}; | ||
|
|
||
| use futures::{future, stream, FutureExt, StreamExt}; | ||
| use jsonrpsee::{core::Error as JsonRpseeError, SubscriptionSink}; | ||
| use jsonrpsee::{ | ||
| core::{async_trait, Error as JsonRpseeError}, | ||
| SubscriptionSink, | ||
| }; | ||
| use sc_client_api::{ | ||
| Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider, | ||
| StorageProvider, | ||
|
|
@@ -48,6 +51,9 @@ use sp_core::{ | |
| use sp_runtime::{generic::BlockId, traits::Block as BlockT}; | ||
| use sp_version::RuntimeVersion; | ||
|
|
||
| /// The maximum time allowed for an RPC call when running without unsafe RPC enabled. | ||
| const MAXIMUM_SAFE_RPC_CALL_TIMEOUT: Duration = Duration::from_secs(30); | ||
|
|
||
| /// Ranges to query in state_queryStorage. | ||
| struct QueryStorageRange<Block: BlockT> { | ||
| /// Hashes of all the blocks in the range. | ||
|
|
@@ -166,6 +172,7 @@ where | |
| } | ||
| } | ||
|
|
||
| #[async_trait] | ||
| impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Client> | ||
| where | ||
| Block: BlockT + 'static, | ||
|
|
@@ -251,33 +258,53 @@ where | |
| .map_err(client_err) | ||
| } | ||
|
|
||
| fn storage_size( | ||
| async fn storage_size( | ||
| &self, | ||
| block: Option<Block::Hash>, | ||
| key: StorageKey, | ||
| deny_unsafe: DenyUnsafe, | ||
| ) -> std::result::Result<Option<u64>, Error> { | ||
| let block = match self.block_or_best(block) { | ||
| Ok(b) => b, | ||
| Err(e) => return Err(client_err(e)), | ||
| }; | ||
|
|
||
| match self.client.storage(block, &key) { | ||
| Ok(Some(d)) => return Ok(Some(d.0.len() as u64)), | ||
| Err(e) => return Err(client_err(e)), | ||
| Ok(None) => {}, | ||
| } | ||
| let client = self.client.clone(); | ||
| let timeout = match deny_unsafe { | ||
| DenyUnsafe::Yes => Some(MAXIMUM_SAFE_RPC_CALL_TIMEOUT), | ||
| DenyUnsafe::No => None, | ||
| }; | ||
|
|
||
| self.client | ||
| .storage_pairs(block, &key) | ||
| .map(|kv| { | ||
| let item_sum = kv.iter().map(|(_, v)| v.0.len() as u64).sum::<u64>(); | ||
| if item_sum > 0 { | ||
| Some(item_sum) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .map_err(client_err) | ||
| super::utils::spawn_blocking_with_timeout(timeout, move |is_cancelled| { | ||
| // Does the key point to a concrete entry in the database? | ||
| match client.storage(block, &key) { | ||
| Ok(Some(d)) => return Ok(Ok(Some(d.0.len() as u64))), | ||
| Err(e) => return Ok(Err(client_err(e))), | ||
| Ok(None) => {}, | ||
| } | ||
|
|
||
| // The key doesn't point to anything, so it's probably a prefix. | ||
|
Member
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. That is a bold take :P I would not want to rely on this.
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. That's how the original RPC works. 🤷
Member
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. Ohh fuck, I have overseen this :P Sorry 🙈 I should go back in time and add my comment there xD |
||
| let iter = match client.storage_keys_iter(block, Some(&key), None).map_err(client_err) { | ||
|
Member
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.
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. Yeah, I think we should. In the next PR I was planning on getting rid of all of the methods that return a |
||
| Ok(iter) => iter, | ||
| Err(e) => return Ok(Err(e)), | ||
| }; | ||
|
|
||
| let mut sum = 0; | ||
| for storage_key in iter { | ||
| let value = client.storage(block, &storage_key).ok().flatten().unwrap_or_default(); | ||
| sum += value.0.len() as u64; | ||
|
|
||
| is_cancelled.check_if_cancelled()?; | ||
| } | ||
|
|
||
| if sum > 0 { | ||
| Ok(Ok(Some(sum))) | ||
| } else { | ||
| Ok(Ok(None)) | ||
| } | ||
| }) | ||
| .await | ||
| .map_err(|error| Error::Client(Box::new(error)))? | ||
| } | ||
|
|
||
| fn storage_hash( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.