diff --git a/substrate/core/client/db/src/storage_cache.rs b/substrate/core/client/db/src/storage_cache.rs index 27d5cec088769aa2d9e8180331512fd9fbfe8eef..3411987d48cdc4db997096567ffeb39cccdafd92 100644 --- a/substrate/core/client/db/src/storage_cache.rs +++ b/substrate/core/client/db/src/storage_cache.rs @@ -346,6 +346,10 @@ impl<H: Hasher, S: StateBackend<H>, B:Block> StateBackend<H> for CachingState<H, self.state.pairs() } + fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>> { + self.state.keys(prefix) + } + fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> { self.state.try_into_trie_backend() } diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index f02e25f78c07ecdff74f88726d1c95d37d65bf6a..8cd0d95f67ea6fd12a1a4845d66be4201e5c1774 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -265,6 +265,12 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where &self.backend } + /// Return storage entry keys in state in a block of given hash with given prefix. + pub fn storage_keys(&self, id: &BlockId<Block>, key_prefix: &StorageKey) -> error::Result<Vec<StorageKey>> { + let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect(); + Ok(keys) + } + /// Return single storage entry of contract under given address in state in a block of given hash. pub fn storage(&self, id: &BlockId<Block>, key: &StorageKey) -> error::Result<Option<StorageData>> { Ok(self.state_at(id)? diff --git a/substrate/core/client/src/light/backend.rs b/substrate/core/client/src/light/backend.rs index 6beaf1271bf4bab4ce49f4edd86e58374c83c06d..924a474acc62a2af96681d66a48ac1870e13b179 100644 --- a/substrate/core/client/src/light/backend.rs +++ b/substrate/core/client/src/light/backend.rs @@ -279,6 +279,11 @@ where Vec::new() } + fn keys(&self, _prefix: &Vec<u8>) -> Vec<Vec<u8>> { + // whole state is not available on light node + Vec::new() + } + fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> { None } diff --git a/substrate/core/rpc/src/state/mod.rs b/substrate/core/rpc/src/state/mod.rs index 209fe5bb3a504e2f8d4abea85cc0e7971f6f0f9d..72175ca5df9acac6304e7df23a50dcc5f318d1ea 100644 --- a/substrate/core/rpc/src/state/mod.rs +++ b/substrate/core/rpc/src/state/mod.rs @@ -51,6 +51,10 @@ build_rpc_trait! { #[rpc(name = "state_call", alias = ["state_callAt", ])] fn call(&self, String, Bytes, Trailing<Hash>) -> Result<Bytes>; + /// Returns the keys with prefix, leave empty to get all the keys + #[rpc(name = "state_getKeys")] + fn storage_keys(&self, StorageKey, Trailing<Hash>) -> Result<Vec<StorageKey>>; + /// Returns a storage entry at a specific block's state. #[rpc(name = "state_getStorage", alias = ["state_getStorageAt", ])] fn storage(&self, StorageKey, Trailing<Hash>) -> Result<Option<StorageData>>; @@ -148,6 +152,12 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where Ok(Bytes(return_data)) } + fn storage_keys(&self, key_prefix: StorageKey, block: Trailing<Block::Hash>) -> Result<Vec<StorageKey>> { + let block = self.unwrap_or_best(block)?; + trace!(target: "rpc", "Querying storage keys at {:?}", block); + Ok(self.client.storage_keys(&BlockId::Hash(block), &key_prefix)?) + } + fn storage(&self, key: StorageKey, block: Trailing<Block::Hash>) -> Result<Option<StorageData>> { let block = self.unwrap_or_best(block)?; trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0)); diff --git a/substrate/core/state-machine/src/backend.rs b/substrate/core/state-machine/src/backend.rs index ea1d19859ea420f378502e738adbd7374e2b19ca..a032ab11957aa2de520e966fbb98b42f0635912c 100644 --- a/substrate/core/state-machine/src/backend.rs +++ b/substrate/core/state-machine/src/backend.rs @@ -86,6 +86,9 @@ pub trait Backend<H: Hasher> { /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)>; + /// Get all keys with given prefix + fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>>; + /// Try convert into trie backend. fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>>; } @@ -283,6 +286,10 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf { self.inner.get(&None).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))).collect() } + fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>> { + self.inner.get(&None).into_iter().flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()).collect() + } + fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> { let mut mdb = MemoryDB::default(); // TODO: should be more correct and use ::new() let mut root = None; diff --git a/substrate/core/state-machine/src/proving_backend.rs b/substrate/core/state-machine/src/proving_backend.rs index 85afc0b3dc24e01640f987cdb60a8c4906f01c75..2e9d2e5ff22d49bd896849cae63e55cec29119c3 100644 --- a/substrate/core/state-machine/src/proving_backend.rs +++ b/substrate/core/state-machine/src/proving_backend.rs @@ -146,6 +146,10 @@ impl<'a, S, H> Backend<H> for ProvingBackend<'a, S, H> self.backend.pairs() } + fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>> { + self.backend.keys(prefix) + } + fn storage_root<I>(&self, delta: I) -> (H::Out, MemoryDB<H>) where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> { diff --git a/substrate/core/state-machine/src/trie_backend.rs b/substrate/core/state-machine/src/trie_backend.rs index f29608b5d136d1b92ed4910a3f9f213a5f197ac4..543f8a2e04fcde3d4539e6700c59fdb6bff668b4 100644 --- a/substrate/core/state-machine/src/trie_backend.rs +++ b/substrate/core/state-machine/src/trie_backend.rs @@ -105,6 +105,26 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where } } + fn keys(&self, prefix: &Vec<u8>) -> Vec<Vec<u8>> { + let mut read_overlay = MemoryDB::default(); // TODO: use new for correctness + let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay); + + let collect_all = || -> Result<_, Box<TrieError<H::Out>>> { + let trie = TrieDB::<H>::new(&eph, self.essence.root())?; + let mut v = Vec::new(); + for x in trie.iter()? { + let (key, _) = x?; + if key.starts_with(prefix) { + v.push(key.to_vec()); + } + } + + Ok(v) + }; + + collect_all().map_err(|e| debug!(target: "trie", "Error extracting trie keys: {}", e)).unwrap_or_default() + } + fn storage_root<I>(&self, delta: I) -> (H::Out, MemoryDB<H>) where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> {