From 0e569e6bf248a580900ea47d5fe3b45a98c58237 Mon Sep 17 00:00:00 2001 From: Xiliang Chen Date: Sat, 12 Jan 2019 00:51:19 +1300 Subject: [PATCH] new state api state_getKeys to expose storage keys (#1380) --- substrate/core/client/db/src/storage_cache.rs | 4 ++++ substrate/core/client/src/client.rs | 6 ++++++ substrate/core/client/src/light/backend.rs | 5 +++++ substrate/core/rpc/src/state/mod.rs | 10 ++++++++++ substrate/core/state-machine/src/backend.rs | 7 +++++++ .../core/state-machine/src/proving_backend.rs | 4 ++++ .../core/state-machine/src/trie_backend.rs | 20 +++++++++++++++++++ 7 files changed, 56 insertions(+) diff --git a/substrate/core/client/db/src/storage_cache.rs b/substrate/core/client/db/src/storage_cache.rs index 27d5cec088..3411987d48 100644 --- a/substrate/core/client/db/src/storage_cache.rs +++ b/substrate/core/client/db/src/storage_cache.rs @@ -346,6 +346,10 @@ impl, B:Block> StateBackend for CachingState) -> Vec> { + self.state.keys(prefix) + } + fn try_into_trie_backend(self) -> Option> { self.state.try_into_trie_backend() } diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index f02e25f78c..8cd0d95f67 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -265,6 +265,12 @@ impl Client 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, key_prefix: &StorageKey) -> error::Result> { + 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, key: &StorageKey) -> error::Result> { Ok(self.state_at(id)? diff --git a/substrate/core/client/src/light/backend.rs b/substrate/core/client/src/light/backend.rs index 6beaf1271b..924a474acc 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) -> Vec> { + // whole state is not available on light node + Vec::new() + } + fn try_into_trie_backend(self) -> Option> { None } diff --git a/substrate/core/rpc/src/state/mod.rs b/substrate/core/rpc/src/state/mod.rs index 209fe5bb3a..72175ca5df 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) -> Result; + /// Returns the keys with prefix, leave empty to get all the keys + #[rpc(name = "state_getKeys")] + fn storage_keys(&self, StorageKey, Trailing) -> Result>; + /// Returns a storage entry at a specific block's state. #[rpc(name = "state_getStorage", alias = ["state_getStorageAt", ])] fn storage(&self, StorageKey, Trailing) -> Result>; @@ -148,6 +152,12 @@ impl StateApi for State where Ok(Bytes(return_data)) } + fn storage_keys(&self, key_prefix: StorageKey, block: Trailing) -> Result> { + 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) -> Result> { 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 ea1d19859e..a032ab1195 100644 --- a/substrate/core/state-machine/src/backend.rs +++ b/substrate/core/state-machine/src/backend.rs @@ -86,6 +86,9 @@ pub trait Backend { /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(Vec, Vec)>; + /// Get all keys with given prefix + fn keys(&self, prefix: &Vec) -> Vec>; + /// Try convert into trie backend. fn try_into_trie_backend(self) -> Option>; } @@ -283,6 +286,10 @@ impl Backend for InMemory 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) -> Vec> { + 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> { 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 85afc0b3dc..2e9d2e5ff2 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 for ProvingBackend<'a, S, H> self.backend.pairs() } + fn keys(&self, prefix: &Vec) -> Vec> { + self.backend.keys(prefix) + } + fn storage_root(&self, delta: I) -> (H::Out, MemoryDB) where I: IntoIterator, Option>)> { diff --git a/substrate/core/state-machine/src/trie_backend.rs b/substrate/core/state-machine/src/trie_backend.rs index f29608b5d1..543f8a2e04 100644 --- a/substrate/core/state-machine/src/trie_backend.rs +++ b/substrate/core/state-machine/src/trie_backend.rs @@ -105,6 +105,26 @@ impl, H: Hasher> Backend for TrieBackend where } } + fn keys(&self, prefix: &Vec) -> Vec> { + 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>> { + let trie = TrieDB::::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(&self, delta: I) -> (H::Out, MemoryDB) where I: IntoIterator, Option>)> {