mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Multiple storage root support (#902)
* Implement a non generic version of child delta trie * Use delta_trie_root in state_machine * Expand InMemory backend to support multi-storage * Create Consolidate trait * Fix all crate compile and remove unused OverlayedChanges::drain * Implement child storage root support and overlay changes * Add child storage reader * Add child storage writer * Implement child storage cleaning * Fix light backend compile * Add all required ext functions for wasm executor * Add ext def to io * Add all io functions * Fix nostd compile * Add simple test * Remove unnecessary vec copy in child_storage_root_transaction * Use values_mut/for_each to make it shorter * Use extend to shorter a for loop * Move record_all_keys to trie so it's easier to generic them later * space -> tab * Remove to_owned in debug format * Clean out all to_owned * Break debug_trace to multiple lines * Remove 0.. * UserError copy/paste typo * Replace Vec::from_raw_parts by slice::from_raw_parts * Use iter::empty() * Wrap some long lines * Wrap a missing line * Remove unnecessary map https://github.com/paritytech/substrate/pull/856#discussion_r226222663 * Call ext_free after from_raw_parts * Fix tests in other crates
This commit is contained in:
@@ -89,7 +89,7 @@ pub fn build_proof<Header, Hasher, I>(
|
||||
{
|
||||
let transaction = build_pairs::<Header, I>(cht_size, cht_num, hashes)?
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Some(v)))
|
||||
.map(|(k, v)| (None, k, Some(v)))
|
||||
.collect::<Vec<_>>();
|
||||
let storage = InMemoryState::<Hasher>::default().update(transaction);
|
||||
let (value, proof) = prove_read(storage, &encode_cht_key(block_num)).ok()?;
|
||||
@@ -205,7 +205,7 @@ pub fn decode_cht_value(value: &[u8]) -> Option<H256> {
|
||||
32 => Some(H256::from_slice(&value[0..32])),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -201,7 +201,7 @@ impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
|
||||
S: BlockchainStorage<Block>,
|
||||
F: Fetcher<Block>,
|
||||
H: Hasher,
|
||||
|
||||
|
||||
{
|
||||
type Error = ClientError;
|
||||
type Transaction = ();
|
||||
@@ -227,15 +227,32 @@ impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
|
||||
.into_future().wait()
|
||||
}
|
||||
|
||||
fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
||||
Err(ClientErrorKind::NotAvailableOnLightClient.into())
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, _prefix: &[u8], _action: A) {
|
||||
// whole state is not available on light node
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, _storage_key: &[u8], _action: A) {
|
||||
// whole state is not available on light node
|
||||
}
|
||||
|
||||
fn storage_root<I>(&self, _delta: I) -> (H::Out, Self::Transaction)
|
||||
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
|
||||
{
|
||||
(H::Out::default(), ())
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, _key: &[u8], _delta: I) -> (Vec<u8>, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
|
||||
{
|
||||
(H::Out::default().as_ref().to_vec(), ())
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
// whole state is not available on light node
|
||||
Vec::new()
|
||||
|
||||
@@ -181,6 +181,46 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
this.ext.set_storage(key, value);
|
||||
Ok(())
|
||||
},
|
||||
ext_set_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => {
|
||||
let storage_key = this.memory.get(storage_key_data, storage_key_len as usize).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_set_child_storage"))?;
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_set_child_storage"))?;
|
||||
let value = this.memory.get(value_data, value_len as usize).map_err(|_| UserError("Invalid attempt to determine value in ext_set_child_storage"))?;
|
||||
if let Some(_preimage) = this.hash_lookup.get(&key) {
|
||||
debug_trace!(
|
||||
target: "wasm-trace", "*** Setting child storage: {} -> %{} -> {} [k={}]",
|
||||
::primitives::hexdisplay::ascii_format(&storage_key),
|
||||
::primitives::hexdisplay::ascii_format(&_preimage),
|
||||
HexDisplay::from(&value),
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
} else {
|
||||
debug_trace!(
|
||||
target: "wasm-trace", "*** Setting child storage: {} -> {} -> {} [k={}]",
|
||||
::primitives::hexdisplay::ascii_format(&storage_key),
|
||||
::primitives::hexdisplay::ascii_format(&key),
|
||||
HexDisplay::from(&value),
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
}
|
||||
this.ext.set_child_storage(storage_key, key, value);
|
||||
Ok(())
|
||||
},
|
||||
ext_clear_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) => {
|
||||
let storage_key = this.memory.get(
|
||||
storage_key_data,
|
||||
storage_key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_clear_child_storage"))?;
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_child_storage"))?;
|
||||
debug_trace!(target: "wasm-trace", "*** Clearing child storage: {} -> {} [k={}]",
|
||||
::primitives::hexdisplay::ascii_format(&storage_key),
|
||||
if let Some(_preimage) = this.hash_lookup.get(&key) {
|
||||
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
|
||||
} else {
|
||||
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
|
||||
}, HexDisplay::from(&key));
|
||||
this.ext.clear_child_storage(&storage_key, &key);
|
||||
Ok(())
|
||||
},
|
||||
ext_clear_storage(key_data: *const u8, key_len: u32) => {
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_clear_storage"))?;
|
||||
debug_trace!(target: "wasm-trace", "*** Clearing storage: {} [k={}]",
|
||||
@@ -196,14 +236,33 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_storage"))?;
|
||||
Ok(if this.ext.exists_storage(&key) { 1 } else { 0 })
|
||||
},
|
||||
ext_exists_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) -> u32 => {
|
||||
let storage_key = this.memory.get(
|
||||
storage_key_data,
|
||||
storage_key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_exists_child_storage"))?;
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_exists_child_storage"))?;
|
||||
Ok(if this.ext.exists_child_storage(&storage_key, &key) { 1 } else { 0 })
|
||||
},
|
||||
ext_clear_prefix(prefix_data: *const u8, prefix_len: u32) => {
|
||||
let prefix = this.memory.get(prefix_data, prefix_len as usize).map_err(|_| UserError("Invalid attempt to determine prefix in ext_clear_prefix"))?;
|
||||
this.ext.clear_prefix(&prefix);
|
||||
Ok(())
|
||||
},
|
||||
ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32) => {
|
||||
let storage_key = this.memory.get(
|
||||
storage_key_data,
|
||||
storage_key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_kill_child_storage"))?;
|
||||
this.ext.kill_child_storage(&storage_key);
|
||||
Ok(())
|
||||
},
|
||||
// return 0 and place u32::max_value() into written_out if no value exists for the key.
|
||||
ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?;
|
||||
let key = this.memory.get(
|
||||
key_data,
|
||||
key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_storage"))?;
|
||||
let maybe_value = this.ext.storage(&key);
|
||||
|
||||
debug_trace!(target: "wasm-trace", "*** Getting storage: {} == {} [k={}]",
|
||||
@@ -213,9 +272,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
|
||||
},
|
||||
if let Some(ref b) = maybe_value {
|
||||
format!("{}", HexDisplay::from(b))
|
||||
&format!("{}", HexDisplay::from(b))
|
||||
} else {
|
||||
"<empty>".to_owned()
|
||||
"<empty>"
|
||||
},
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
@@ -232,6 +291,45 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
// return 0 and place u32::max_value() into written_out if no value exists for the key.
|
||||
ext_get_allocated_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
|
||||
let storage_key = this.memory.get(
|
||||
storage_key_data,
|
||||
storage_key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_get_allocated_child_storage"))?;
|
||||
let key = this.memory.get(
|
||||
key_data,
|
||||
key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine key in ext_get_allocated_child_storage"))?;
|
||||
let maybe_value = this.ext.child_storage(&storage_key, &key);
|
||||
|
||||
debug_trace!(target: "wasm-trace", "*** Getting child storage: {} -> {} == {} [k={}]",
|
||||
::primitives::hexdisplay::ascii_format(&storage_key),
|
||||
if let Some(_preimage) = this.hash_lookup.get(&key) {
|
||||
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
|
||||
} else {
|
||||
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
|
||||
},
|
||||
if let Some(ref b) = maybe_value {
|
||||
&format!("{}", HexDisplay::from(b))
|
||||
} else {
|
||||
"<empty>"
|
||||
},
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
|
||||
if let Some(value) = maybe_value {
|
||||
let offset = this.heap.allocate(value.len() as u32) as u32;
|
||||
this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_get_allocated_child_storage"))?;
|
||||
this.memory.write_primitive(written_out, value.len() as u32)
|
||||
.map_err(|_| UserError("Invalid attempt to write written_out in ext_get_allocated_child_storage"))?;
|
||||
Ok(offset)
|
||||
} else {
|
||||
this.memory.write_primitive(written_out, u32::max_value())
|
||||
.map_err(|_| UserError("Invalid attempt to write failed written_out in ext_get_allocated_child_storage"))?;
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
// return u32::max_value() if no value exists for the key.
|
||||
ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => {
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_get_storage_into"))?;
|
||||
@@ -243,9 +341,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
|
||||
},
|
||||
if let Some(ref b) = maybe_value {
|
||||
format!("{}", HexDisplay::from(b))
|
||||
&format!("{}", HexDisplay::from(b))
|
||||
} else {
|
||||
"<empty>".to_owned()
|
||||
"<empty>"
|
||||
},
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
@@ -259,11 +357,61 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
Ok(u32::max_value())
|
||||
}
|
||||
},
|
||||
// return u32::max_value() if no value exists for the key.
|
||||
ext_get_child_storage_into(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => {
|
||||
let storage_key = this.memory.get(
|
||||
storage_key_data,
|
||||
storage_key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_get_child_storage_into"))?;
|
||||
let key = this.memory.get(
|
||||
key_data,
|
||||
key_len as usize
|
||||
).map_err(|_| UserError("Invalid attempt to get key in ext_get_child_storage_into"))?;
|
||||
let maybe_value = this.ext.child_storage(&storage_key, &key);
|
||||
debug_trace!(target: "wasm-trace", "*** Getting storage: {} -> {} == {} [k={}]",
|
||||
::primitives::hexdisplay::ascii_format(&storage_key),
|
||||
if let Some(_preimage) = this.hash_lookup.get(&key) {
|
||||
format!("%{}", ::primitives::hexdisplay::ascii_format(&_preimage))
|
||||
} else {
|
||||
format!(" {}", ::primitives::hexdisplay::ascii_format(&key))
|
||||
},
|
||||
if let Some(ref b) = maybe_value {
|
||||
&format!("{}", HexDisplay::from(b))
|
||||
} else {
|
||||
"<empty>"
|
||||
},
|
||||
HexDisplay::from(&key)
|
||||
);
|
||||
|
||||
if let Some(value) = maybe_value {
|
||||
let value = &value[value_offset as usize..];
|
||||
let written = ::std::cmp::min(value_len as usize, value.len());
|
||||
this.memory.set(value_data, &value[..written]).map_err(|_| UserError("Invalid attempt to set value in ext_get_child_storage_into"))?;
|
||||
Ok(written as u32)
|
||||
} else {
|
||||
Ok(u32::max_value())
|
||||
}
|
||||
},
|
||||
ext_storage_root(result: *mut u8) => {
|
||||
let r = this.ext.storage_root();
|
||||
this.memory.set(result, r.as_ref()).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_root"))?;
|
||||
Ok(())
|
||||
},
|
||||
ext_child_storage_root(storage_key_data: *const u8, storage_key_len: u32, written_out: *mut u32) -> *mut u8 => {
|
||||
let storage_key = this.memory.get(storage_key_data, storage_key_len as usize).map_err(|_| UserError("Invalid attempt to determine storage_key in ext_child_storage_root"))?;
|
||||
let r = this.ext.child_storage_root(&storage_key);
|
||||
if let Some(value) = r {
|
||||
let offset = this.heap.allocate(value.len() as u32) as u32;
|
||||
this.memory.set(offset, &value).map_err(|_| UserError("Invalid attempt to set memory in ext_child_storage_root"))?;
|
||||
this.memory.write_primitive(written_out, value.len() as u32)
|
||||
.map_err(|_| UserError("Invalid attempt to write written_out in ext_child_storage_root"))?;
|
||||
Ok(offset)
|
||||
} else {
|
||||
this.memory.write_primitive(written_out, u32::max_value())
|
||||
.map_err(|_| UserError("Invalid attempt to write failed written_out in ext_child_storage_root"))?;
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
ext_storage_changes_root(block: u64, result: *mut u8) -> u32 => {
|
||||
let r = this.ext.storage_changes_root(block);
|
||||
if let Some(ref r) = r {
|
||||
@@ -300,9 +448,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
let hashed_key = twox_128(&key);
|
||||
debug_trace!(target: "xxhash", "XXhash: {} -> {}",
|
||||
if let Ok(_skey) = ::std::str::from_utf8(&key) {
|
||||
_skey.to_owned()
|
||||
_skey
|
||||
} else {
|
||||
format!("{}", HexDisplay::from(&key))
|
||||
&format!("{}", HexDisplay::from(&key))
|
||||
},
|
||||
HexDisplay::from(&hashed_key)
|
||||
);
|
||||
|
||||
@@ -161,7 +161,7 @@ where TSubstream: AsyncRead + AsyncWrite,
|
||||
// Note that `inner` is wrapped in a `Fuse`, therefore we can poll it forever.
|
||||
loop {
|
||||
match self.inner.poll()? {
|
||||
Async::Ready(Some(mut data)) =>
|
||||
Async::Ready(Some(data)) =>
|
||||
return Ok(Async::Ready(Some(data.freeze()))),
|
||||
Async::Ready(None) =>
|
||||
if !self.requires_poll_complete && self.send_queue.is_empty() {
|
||||
|
||||
@@ -72,4 +72,13 @@ pub mod well_known_keys {
|
||||
|
||||
/// Changes trie configuration is stored under this key.
|
||||
pub const CHANGES_TRIE_CONFIG: &'static [u8] = b":changes_trie";
|
||||
|
||||
/// Prefix of child storage keys.
|
||||
pub const CHILD_STORAGE_KEY_PREFIX: &'static [u8] = b":child_storage:";
|
||||
|
||||
/// Whether a key is a child storage key.
|
||||
pub fn is_child_storage_key(key: &[u8]) -> bool {
|
||||
key.starts_with(CHILD_STORAGE_KEY_PREFIX)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
|
||||
.expect("storage cannot be called outside of an Externalities-provided environment.")
|
||||
}
|
||||
|
||||
/// Get `key` from child storage and return a `Vec`, empty if there's a problem.
|
||||
pub fn child_storage(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
ext::with(|ext| ext.child_storage(storage_key, key).map(|s| s.to_vec()))
|
||||
.expect("storage cannot be called outside of an Externalities-provided environment.")
|
||||
}
|
||||
|
||||
/// Get `key` from storage, placing the value into `value_out` (as much of it as possible) and return
|
||||
/// the number of bytes that the entry in storage had beyond the offset or None if the storage entry
|
||||
/// doesn't exist at all. Note that if the buffer is smaller than the storage entry length, the returned
|
||||
@@ -55,7 +61,20 @@ pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Op
|
||||
ext::with(|ext| ext.storage(key).map(|value| {
|
||||
let value = &value[value_offset..];
|
||||
let written = ::std::cmp::min(value.len(), value_out.len());
|
||||
value_out[0..written].copy_from_slice(&value[0..written]);
|
||||
value_out[..written].copy_from_slice(&value[..written]);
|
||||
value.len()
|
||||
})).expect("read_storage cannot be called outside of an Externalities-provided environment.")
|
||||
}
|
||||
|
||||
/// Get `key` from child storage, placing the value into `value_out` (as much of it as possible) and return
|
||||
/// the number of bytes that the entry in storage had beyond the offset or None if the storage entry
|
||||
/// doesn't exist at all. Note that if the buffer is smaller than the storage entry length, the returned
|
||||
/// number of bytes is not equal to the number of bytes written to the `value_out`.
|
||||
pub fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
|
||||
ext::with(|ext| ext.child_storage(storage_key, key).map(|value| {
|
||||
let value = &value[value_offset..];
|
||||
let written = ::std::cmp::min(value.len(), value_out.len());
|
||||
value_out[..written].copy_from_slice(&value[..written]);
|
||||
value.len()
|
||||
})).expect("read_storage cannot be called outside of an Externalities-provided environment.")
|
||||
}
|
||||
@@ -67,6 +86,13 @@ pub fn set_storage(key: &[u8], value: &[u8]) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the child storage of a key to some value.
|
||||
pub fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) {
|
||||
ext::with(|ext|
|
||||
ext.set_child_storage(storage_key.to_vec(), key.to_vec(), value.to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
/// Clear the storage of a key.
|
||||
pub fn clear_storage(key: &[u8]) {
|
||||
ext::with(|ext|
|
||||
@@ -74,6 +100,13 @@ pub fn clear_storage(key: &[u8]) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Clear the storage of a key.
|
||||
pub fn clear_child_storage(storage_key: &[u8], key: &[u8]) {
|
||||
ext::with(|ext|
|
||||
ext.clear_child_storage(storage_key, key)
|
||||
);
|
||||
}
|
||||
|
||||
/// Check whether a given `key` exists in storage.
|
||||
pub fn exists_storage(key: &[u8]) -> bool {
|
||||
ext::with(|ext|
|
||||
@@ -81,6 +114,13 @@ pub fn exists_storage(key: &[u8]) -> bool {
|
||||
).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check whether a given `key` exists in storage.
|
||||
pub fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool {
|
||||
ext::with(|ext|
|
||||
ext.exists_child_storage(storage_key, key)
|
||||
).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Clear the storage entries with a key that starts with the given prefix.
|
||||
pub fn clear_prefix(prefix: &[u8]) {
|
||||
ext::with(|ext|
|
||||
@@ -88,6 +128,13 @@ pub fn clear_prefix(prefix: &[u8]) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Clear an entire child storage.
|
||||
pub fn kill_child_storage(storage_key: &[u8]) {
|
||||
ext::with(|ext|
|
||||
ext.kill_child_storage(storage_key)
|
||||
);
|
||||
}
|
||||
|
||||
/// The current relay chain identifier.
|
||||
pub fn chain_id() -> u64 {
|
||||
ext::with(|ext|
|
||||
@@ -102,6 +149,13 @@ pub fn storage_root() -> H256 {
|
||||
).unwrap_or(H256::new())
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and compute the resultant child storage root.
|
||||
pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
ext::with(|ext|
|
||||
ext.child_storage_root(storage_key)
|
||||
).unwrap_or(None)
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and get the resultant storage change root.
|
||||
pub fn storage_changes_root(block: u64) -> Option<H256> {
|
||||
ext::with(|ext|
|
||||
|
||||
@@ -54,16 +54,24 @@ pub extern fn oom(_: ::core::alloc::Layout) -> ! {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn ext_free(addr: *mut u8);
|
||||
fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32);
|
||||
fn ext_print_hex(data: *const u8, len: u32);
|
||||
fn ext_print_num(value: u64);
|
||||
fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32);
|
||||
fn ext_set_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32);
|
||||
fn ext_clear_storage(key_data: *const u8, key_len: u32);
|
||||
fn ext_clear_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32);
|
||||
fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32;
|
||||
fn ext_exists_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32) -> u32;
|
||||
fn ext_clear_prefix(prefix_data: *const u8, prefix_len: u32);
|
||||
fn ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32);
|
||||
fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
|
||||
fn ext_get_allocated_child_storage(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
|
||||
fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32;
|
||||
fn ext_get_child_storage_into(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32;
|
||||
fn ext_storage_root(result: *mut u8);
|
||||
fn ext_child_storage_root(storage_key_data: *const u8, storage_key_len: u32, written_out: *mut u32) -> *mut u8;
|
||||
fn ext_storage_changes_root(block: u64, result: *mut u8) -> u32;
|
||||
fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8);
|
||||
fn ext_chain_id() -> u64;
|
||||
@@ -104,7 +112,24 @@ pub fn storage(key: &[u8]) -> Option<Vec<u8>> {
|
||||
if length == u32::max_value() {
|
||||
None
|
||||
} else {
|
||||
Some(Vec::from_raw_parts(ptr, length as usize, length as usize))
|
||||
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
|
||||
ext_free(ptr);
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `key` from child storage and return a `Vec`, empty if there's a problem.
|
||||
pub fn child_storage(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut length: u32 = 0;
|
||||
unsafe {
|
||||
let ptr = ext_get_allocated_child_storage(storage_key.as_ptr(), storage_key.len() as u32, key.as_ptr(), key.len() as u32, &mut length);
|
||||
if length == u32::max_value() {
|
||||
None
|
||||
} else {
|
||||
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
|
||||
ext_free(ptr);
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +144,17 @@ pub fn set_storage(key: &[u8], value: &[u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the child storage of some particular key to Some value.
|
||||
pub fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) {
|
||||
unsafe {
|
||||
ext_set_child_storage(
|
||||
storage_key.as_ptr(), key.len() as u32,
|
||||
key.as_ptr(), key.len() as u32,
|
||||
value.as_ptr(), value.len() as u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the storage of some particular key.
|
||||
pub fn clear_storage(key: &[u8]) {
|
||||
unsafe {
|
||||
@@ -128,6 +164,16 @@ pub fn clear_storage(key: &[u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the storage of some particular key.
|
||||
pub fn clear_child_storage(storage_key: &[u8], key: &[u8]) {
|
||||
unsafe {
|
||||
ext_clear_child_storage(
|
||||
storage_key.as_ptr(), storage_key.len() as u32,
|
||||
key.as_ptr(), key.len() as u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether a particular key exists in storage.
|
||||
pub fn exists_storage(key: &[u8]) -> bool {
|
||||
unsafe {
|
||||
@@ -137,6 +183,16 @@ pub fn exists_storage(key: &[u8]) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether a particular key exists in storage.
|
||||
pub fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool {
|
||||
unsafe {
|
||||
ext_exists_child_storage(
|
||||
storage_key.as_ptr(), storage_key.len() as u32,
|
||||
key.as_ptr(), key.len() as u32
|
||||
) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the storage entries key of which starts with the given prefix.
|
||||
pub fn clear_prefix(prefix: &[u8]) {
|
||||
unsafe {
|
||||
@@ -147,6 +203,16 @@ pub fn clear_prefix(prefix: &[u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear an entire child storage.
|
||||
pub fn kill_child_storage(storage_key: &[u8]) {
|
||||
unsafe {
|
||||
ext_kill_child_storage(
|
||||
storage_key.as_ptr(),
|
||||
storage_key.len() as u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return
|
||||
/// the number of bytes that the key in storage was beyond the offset.
|
||||
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
|
||||
@@ -162,6 +228,22 @@ pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Op
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `key` from child storage, placing the value into `value_out` (as much as possible) and return
|
||||
/// the number of bytes that the key in storage was beyond the offset.
|
||||
pub fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option<usize> {
|
||||
unsafe {
|
||||
match ext_get_child_storage_into(
|
||||
storage_key.as_ptr(), storage_key.len() as u32,
|
||||
key.as_ptr(), key.len() as u32,
|
||||
value_out.as_mut_ptr(), value_out.len() as u32,
|
||||
value_offset as u32
|
||||
) {
|
||||
none if none == u32::max_value() => None,
|
||||
length => Some(length as usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The current storage's root.
|
||||
pub fn storage_root() -> [u8; 32] {
|
||||
let mut result: [u8; 32] = Default::default();
|
||||
@@ -171,6 +253,21 @@ pub fn storage_root() -> [u8; 32] {
|
||||
result
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and compute the resultant child storage root.
|
||||
pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut length: u32 = 0;
|
||||
unsafe {
|
||||
let ptr = ext_child_storage_root(storage_key.as_ptr(), storage_key.len() as u32, &mut length);
|
||||
if length == u32::max_value() {
|
||||
None
|
||||
} else {
|
||||
let ret = slice::from_raw_parts(ptr, length as usize).to_vec();
|
||||
ext_free(ptr);
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The current storage' changes root.
|
||||
pub fn storage_changes_root(block: u64) -> Option<[u8; 32]> {
|
||||
let mut result: [u8; 32] = Default::default();
|
||||
|
||||
@@ -23,7 +23,7 @@ use std::marker::PhantomData;
|
||||
use hash_db::Hasher;
|
||||
use trie_backend::TrieBackend;
|
||||
use trie_backend_essence::TrieBackendStorage;
|
||||
use substrate_trie::{TrieDBMut, TrieMut, MemoryDB, trie_root};
|
||||
use substrate_trie::{TrieDBMut, TrieMut, MemoryDB, trie_root, child_trie_root};
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
/// A state backend is used to read state data and can have changes committed
|
||||
@@ -35,7 +35,7 @@ pub trait Backend<H: Hasher> {
|
||||
type Error: super::Error;
|
||||
|
||||
/// Storage changes to be applied if committing
|
||||
type Transaction;
|
||||
type Transaction: Consolidate + Default;
|
||||
|
||||
/// Type of trie backend storage.
|
||||
type TrieBackendStorage: TrieBackendStorage<H>;
|
||||
@@ -43,11 +43,22 @@ pub trait Backend<H: Hasher> {
|
||||
/// Get keyed storage associated with specific address, or None if there is nothing associated.
|
||||
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
|
||||
|
||||
/// Get keyed child storage associated with specific address, or None if there is nothing associated.
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error>;
|
||||
|
||||
/// true if a key exists in storage.
|
||||
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
|
||||
Ok(self.storage(key)?.is_some())
|
||||
}
|
||||
|
||||
/// true if a key exists in child storage.
|
||||
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<bool, Self::Error> {
|
||||
Ok(self.child_storage(storage_key, key)?.is_some())
|
||||
}
|
||||
|
||||
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F);
|
||||
|
||||
/// Retrieve all entries keys of which start with the given prefix and
|
||||
/// call `f` for each of those keys.
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F);
|
||||
@@ -59,6 +70,13 @@ pub trait Backend<H: Hasher> {
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord;
|
||||
|
||||
/// Calculate the child storage root, with given delta over what is already stored in
|
||||
/// the backend, and produce a "transaction" that can be used to commit.
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord;
|
||||
|
||||
/// Get all key/value pairs into a Vec.
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)>;
|
||||
|
||||
@@ -66,6 +84,30 @@ pub trait Backend<H: Hasher> {
|
||||
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>>;
|
||||
}
|
||||
|
||||
/// Trait that allows consolidate two transactions together.
|
||||
pub trait Consolidate {
|
||||
/// Consolidate two transactions into one.
|
||||
fn consolidate(&mut self, other: Self);
|
||||
}
|
||||
|
||||
impl Consolidate for () {
|
||||
fn consolidate(&mut self, _: Self) {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl Consolidate for Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)> {
|
||||
fn consolidate(&mut self, mut other: Self) {
|
||||
self.append(&mut other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Consolidate for MemoryDB<H> {
|
||||
fn consolidate(&mut self, other: Self) {
|
||||
MemoryDB::consolidate(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error impossible.
|
||||
// TODO: use `!` type when stabilized.
|
||||
#[derive(Debug)]
|
||||
@@ -85,7 +127,7 @@ impl error::Error for Void {
|
||||
/// tests.
|
||||
#[derive(Eq)]
|
||||
pub struct InMemory<H> {
|
||||
inner: HashMap<Vec<u8>, Vec<u8>>,
|
||||
inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
_hasher: PhantomData<H>,
|
||||
}
|
||||
|
||||
@@ -117,10 +159,10 @@ impl<H: Hasher> InMemory<H> where H::Out: HeapSizeOf {
|
||||
/// Copy the state, with applied updates
|
||||
pub fn update(&self, changes: <Self as Backend<H>>::Transaction) -> Self {
|
||||
let mut inner: HashMap<_, _> = self.inner.clone();
|
||||
for (key, val) in changes {
|
||||
for (storage_key, key, val) in changes {
|
||||
match val {
|
||||
Some(v) => { inner.insert(key, v); },
|
||||
None => { inner.remove(&key); },
|
||||
Some(v) => { inner.entry(storage_key).or_default().insert(key, v); },
|
||||
None => { inner.entry(storage_key).or_default().remove(&key); },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +170,8 @@ impl<H: Hasher> InMemory<H> where H::Out: HeapSizeOf {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
|
||||
fn from(inner: HashMap<Vec<u8>, Vec<u8>>) -> Self {
|
||||
impl<H> From<HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>> for InMemory<H> {
|
||||
fn from(inner: HashMap<Option<Vec<u8>>, HashMap<Vec<u8>, Vec<u8>>>) -> Self {
|
||||
InMemory {
|
||||
inner: inner,
|
||||
_hasher: PhantomData,
|
||||
@@ -137,23 +179,42 @@ impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> From<HashMap<Vec<u8>, Vec<u8>>> for InMemory<H> {
|
||||
fn from(inner: HashMap<Vec<u8>, Vec<u8>>) -> Self {
|
||||
let mut expanded = HashMap::new();
|
||||
expanded.insert(None, inner);
|
||||
InMemory {
|
||||
inner: expanded,
|
||||
_hasher: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Error for Void {}
|
||||
|
||||
impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
|
||||
type Error = Void;
|
||||
type Transaction = Vec<(Vec<u8>, Option<Vec<u8>>)>;
|
||||
type Transaction = Vec<(Option<Vec<u8>>, Vec<u8>, Option<Vec<u8>>)>;
|
||||
type TrieBackendStorage = MemoryDB<H>;
|
||||
|
||||
fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
Ok(self.inner.get(key).map(Clone::clone))
|
||||
Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone)))
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
Ok(self.inner.get(&Some(storage_key.to_vec())).and_then(|map| map.get(key).map(Clone::clone)))
|
||||
}
|
||||
|
||||
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
|
||||
Ok(self.inner.get(key).is_some())
|
||||
Ok(self.inner.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false))
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
self.inner.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f);
|
||||
self.inner.get(&None).map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f));
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], mut f: F) {
|
||||
self.inner.get(&Some(storage_key.to_vec())).map(|map| map.keys().for_each(|k| f(&k)));
|
||||
}
|
||||
|
||||
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
|
||||
@@ -161,7 +222,7 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
<H as Hasher>::Out: Ord,
|
||||
{
|
||||
let existing_pairs = self.inner.iter().map(|(k, v)| (k.clone(), Some(v.clone())));
|
||||
let existing_pairs = self.inner.get(&None).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
|
||||
|
||||
let transaction: Vec<_> = delta.into_iter().collect();
|
||||
let root = trie_root::<H, _, _, _>(existing_pairs.chain(transaction.iter().cloned())
|
||||
@@ -170,16 +231,52 @@ impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: HeapSizeOf {
|
||||
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
|
||||
);
|
||||
|
||||
(root, transaction)
|
||||
let full_transaction = transaction.into_iter().map(|(k, v)| (None, k, v)).collect();
|
||||
|
||||
(root, full_transaction)
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord
|
||||
{
|
||||
let storage_key = storage_key.to_vec();
|
||||
|
||||
let existing_pairs = self.inner.get(&Some(storage_key.clone())).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
|
||||
|
||||
let transaction: Vec<_> = delta.into_iter().collect();
|
||||
let root = child_trie_root::<H, _, _, _>(
|
||||
&storage_key,
|
||||
existing_pairs.chain(transaction.iter().cloned())
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into_iter()
|
||||
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
|
||||
);
|
||||
|
||||
let full_transaction = transaction.into_iter().map(|(k, v)| (Some(storage_key.clone()), k, v)).collect();
|
||||
|
||||
(root, full_transaction)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
self.inner.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
|
||||
self.inner.get(&None).into_iter().flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone()))).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 root = insert_into_memory_db::<H, _>(&mut mdb, self.inner.into_iter())?;
|
||||
let mut root = None;
|
||||
for (storage_key, map) in self.inner {
|
||||
if storage_key != None {
|
||||
let _ = insert_into_memory_db::<H, _>(&mut mdb, map.into_iter())?;
|
||||
} else {
|
||||
root = Some(insert_into_memory_db::<H, _>(&mut mdb, map.into_iter())?);
|
||||
}
|
||||
}
|
||||
let root = match root {
|
||||
Some(root) => root,
|
||||
None => insert_into_memory_db::<H, _>(&mut mdb, ::std::iter::empty())?,
|
||||
};
|
||||
Some(TrieBackend::new(mdb, root))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,10 @@ fn prepare_extrinsics_input<B, H>(
|
||||
where
|
||||
B: Backend<H>,
|
||||
H: Hasher,
|
||||
|
||||
|
||||
{
|
||||
let mut extrinsic_map = BTreeMap::<Vec<u8>, BTreeSet<u32>>::new();
|
||||
for (key, val) in changes.prospective.iter().chain(changes.committed.iter()) {
|
||||
for (key, val) in changes.prospective.top.iter().chain(changes.committed.top.iter()) {
|
||||
let extrinsics = match val.extrinsics {
|
||||
Some(ref extrinsics) => extrinsics,
|
||||
None => continue,
|
||||
@@ -274,7 +274,7 @@ mod test {
|
||||
let (backend, storage, mut changes) = prepare_for_build();
|
||||
|
||||
// 110: missing from backend, set to None in overlay
|
||||
changes.prospective.insert(vec![110], OverlayedValue {
|
||||
changes.prospective.top.insert(vec![110], OverlayedValue {
|
||||
value: None,
|
||||
extrinsics: Some(vec![1].into_iter().collect())
|
||||
});
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
//! block }, containing entries for every storage key that has been changed in
|
||||
//! the last N*digest_level-1 blocks (except for genesis block), mapping these keys
|
||||
//! to the set of lower-level digest blocks.
|
||||
//!
|
||||
//! Changes trie only contains the top level storage changes. Sub-level changes
|
||||
//! are propogated through its storage root on the top level storage.
|
||||
|
||||
mod build;
|
||||
mod build_iterator;
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
//! Conrete externalities implementation.
|
||||
|
||||
use std::{error, fmt, cmp::Ord};
|
||||
use backend::Backend;
|
||||
use backend::{Backend, Consolidate};
|
||||
use changes_trie::{Storage as ChangesTrieStorage, compute_changes_trie_root};
|
||||
use {Externalities, OverlayedChanges};
|
||||
use hash_db::Hasher;
|
||||
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut};
|
||||
use primitives::storage::well_known_keys::is_child_storage_key;
|
||||
use substrate_trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid};
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
const EXT_NOT_ALLOWED_TO_FAIL: &'static str = "Externalities not allowed to fail within runtime";
|
||||
@@ -122,6 +123,31 @@ where
|
||||
fn mark_dirty(&mut self) {
|
||||
self.storage_transaction = None;
|
||||
}
|
||||
|
||||
/// Fetch child storage root together with its transaction.
|
||||
fn child_storage_root_transaction(&mut self, storage_key: &[u8]) -> (Vec<u8>, B::Transaction) {
|
||||
self.mark_dirty();
|
||||
|
||||
let (root, transaction) = {
|
||||
let delta = self.overlay.committed.children.get(storage_key)
|
||||
.into_iter()
|
||||
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))
|
||||
.chain(self.overlay.prospective.children.get(storage_key)
|
||||
.into_iter()
|
||||
.flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))));
|
||||
|
||||
self.backend.child_storage_root(storage_key, delta)
|
||||
};
|
||||
|
||||
let root_val = if root == default_child_trie_root::<H>(storage_key) {
|
||||
None
|
||||
} else {
|
||||
Some(root.clone())
|
||||
};
|
||||
self.overlay.sync_child_storage_root(storage_key, root_val);
|
||||
|
||||
(root, transaction)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -137,8 +163,8 @@ where
|
||||
|
||||
self.backend.pairs().iter()
|
||||
.map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec())))
|
||||
.chain(self.overlay.committed.clone().into_iter().map(|(k, v)| (k, v.value)))
|
||||
.chain(self.overlay.prospective.clone().into_iter().map(|(k, v)| (k, v.value)))
|
||||
.chain(self.overlay.committed.top.clone().into_iter().map(|(k, v)| (k, v.value)))
|
||||
.chain(self.overlay.prospective.top.clone().into_iter().map(|(k, v)| (k, v.value)))
|
||||
.collect::<HashMap<_, _>>()
|
||||
.into_iter()
|
||||
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
|
||||
@@ -158,6 +184,11 @@ where
|
||||
self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL))
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.overlay.child_storage(storage_key, key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(||
|
||||
self.backend.child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL))
|
||||
}
|
||||
|
||||
fn exists_storage(&self, key: &[u8]) -> bool {
|
||||
match self.overlay.storage(key) {
|
||||
Some(x) => x.is_some(),
|
||||
@@ -165,12 +196,52 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
|
||||
match self.overlay.child_storage(storage_key, key) {
|
||||
Some(x) => x.is_some(),
|
||||
_ => self.backend.exists_child_storage(storage_key, key).expect(EXT_NOT_ALLOWED_TO_FAIL),
|
||||
}
|
||||
}
|
||||
|
||||
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) {
|
||||
if is_child_storage_key(&key) {
|
||||
warn!(target: "trie", "Refuse to directly set child storage key");
|
||||
return;
|
||||
}
|
||||
|
||||
self.mark_dirty();
|
||||
self.overlay.set_storage(key, value);
|
||||
}
|
||||
|
||||
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool {
|
||||
if !is_child_storage_key(&storage_key) || !is_child_trie_key_valid::<H>(&storage_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.mark_dirty();
|
||||
self.overlay.set_child_storage(storage_key, key, value);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn kill_child_storage(&mut self, storage_key: &[u8]) {
|
||||
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.mark_dirty();
|
||||
self.overlay.clear_child_storage(storage_key);
|
||||
self.backend.for_keys_in_child_storage(storage_key, |key| {
|
||||
self.overlay.set_child_storage(storage_key.to_vec(), key.to_vec(), None);
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_prefix(&mut self, prefix: &[u8]) {
|
||||
if is_child_storage_key(prefix) {
|
||||
warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key");
|
||||
return;
|
||||
}
|
||||
|
||||
self.mark_dirty();
|
||||
self.overlay.clear_prefix(prefix);
|
||||
self.backend.for_keys_with_prefix(prefix, |key| {
|
||||
@@ -183,19 +254,40 @@ where
|
||||
}
|
||||
|
||||
fn storage_root(&mut self) -> H::Out {
|
||||
if let Some((_, ref root)) = self.storage_transaction {
|
||||
if let Some((_, ref root)) = self.storage_transaction {
|
||||
return root.clone();
|
||||
}
|
||||
|
||||
// compute and memoize
|
||||
let delta = self.overlay.committed.iter().map(|(k, v)| (k.clone(), v.value.clone()))
|
||||
.chain(self.overlay.prospective.iter().map(|(k, v)| (k.clone(), v.value.clone())));
|
||||
let mut transaction = B::Transaction::default();
|
||||
let child_storage_keys: Vec<_> = self.overlay.prospective.children.keys().cloned().collect();
|
||||
|
||||
let (root, transaction) = self.backend.storage_root(delta);
|
||||
for key in child_storage_keys {
|
||||
let (_, t) = self.child_storage_root_transaction(&key);
|
||||
transaction.consolidate(t);
|
||||
}
|
||||
|
||||
// compute and memoize
|
||||
let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))
|
||||
.chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone())));
|
||||
|
||||
let (root, t) = self.backend.storage_root(delta);
|
||||
transaction.consolidate(t);
|
||||
self.storage_transaction = Some((transaction, root));
|
||||
root
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
if !is_child_storage_key(storage_key) || !is_child_trie_key_valid::<H>(storage_key) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.storage_transaction.is_some() {
|
||||
return Some(self.storage(storage_key).unwrap_or(default_child_trie_root::<H>(storage_key)));
|
||||
}
|
||||
|
||||
Some(self.child_storage_root_transaction(storage_key).0)
|
||||
}
|
||||
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
|
||||
let root_and_tx = compute_changes_trie_root::<_, T, H>(
|
||||
self.backend,
|
||||
@@ -287,7 +379,7 @@ mod tests {
|
||||
#[test]
|
||||
fn storage_changes_root_is_some_when_extrinsic_changes_are_empty() {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
overlay.prospective.get_mut(&vec![1]).unwrap().value = None;
|
||||
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
|
||||
@@ -97,33 +97,62 @@ pub trait Externalities<H: Hasher> {
|
||||
/// Read storage of current contract being called.
|
||||
fn storage(&self, key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Read child storage of current contract being called.
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Set storage entry `key` of current contract being called (effective immediately).
|
||||
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||
self.place_storage(key, Some(value));
|
||||
}
|
||||
|
||||
/// Set child storage entry `key` of current contract being called (effective immediately).
|
||||
fn set_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Vec<u8>) -> bool {
|
||||
self.place_child_storage(storage_key, key, Some(value))
|
||||
}
|
||||
|
||||
/// Clear a storage entry (`key`) of current contract being called (effective immediately).
|
||||
fn clear_storage(&mut self, key: &[u8]) {
|
||||
self.place_storage(key.to_vec(), None);
|
||||
}
|
||||
|
||||
/// Clear a storage entry (`key`) of current contract being called (effective immediately).
|
||||
/// Clear a child storage entry (`key`) of current contract being called (effective immediately).
|
||||
fn clear_child_storage(&mut self, storage_key: &[u8], key: &[u8]) -> bool {
|
||||
self.place_child_storage(storage_key.to_vec(), key.to_vec(), None)
|
||||
}
|
||||
|
||||
/// Whether a storage entry exists.
|
||||
fn exists_storage(&self, key: &[u8]) -> bool {
|
||||
self.storage(key).is_some()
|
||||
}
|
||||
|
||||
/// Whether a child storage entry exists.
|
||||
fn exists_child_storage(&self, storage_key: &[u8], key: &[u8]) -> bool {
|
||||
self.child_storage(storage_key, key).is_some()
|
||||
}
|
||||
|
||||
/// Clear an entire child storage.
|
||||
fn kill_child_storage(&mut self, storage_key: &[u8]);
|
||||
|
||||
/// Clear storage entries which keys are start with the given prefix.
|
||||
fn clear_prefix(&mut self, prefix: &[u8]);
|
||||
|
||||
/// Set or clear a storage entry (`key`) of current contract being called (effective immediately).
|
||||
fn place_storage(&mut self, key: Vec<u8>, value: Option<Vec<u8>>);
|
||||
|
||||
/// Set or clear a child storage entry. Return whether the operation succeeds.
|
||||
fn place_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, value: Option<Vec<u8>>) -> bool;
|
||||
|
||||
/// Get the identity of the chain.
|
||||
fn chain_id(&self) -> u64;
|
||||
|
||||
/// Get the trie root of the current storage map.
|
||||
/// Get the trie root of the current storage map. This will also update all child storage keys in the top-level storage map.
|
||||
fn storage_root(&mut self) -> H::Out where H::Out: Ord;
|
||||
|
||||
/// Get the trie root of a child storage map. This will also update the value of the child storage keys in the top-level storage map. If the storage root equals default hash as defined by trie, the key in top-level storage map will be removed.
|
||||
///
|
||||
/// Returns None if key provided is not a storage key. This can due to not being started with CHILD_STORAGE_KEY_PREFIX, or the trie implementation regards the key as invalid.
|
||||
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Get the change trie root of the current storage overlay at given block.
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> where H::Out: Ord;
|
||||
}
|
||||
@@ -477,6 +506,7 @@ where
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use codec::Encode;
|
||||
use overlayed_changes::OverlayedValue;
|
||||
use super::*;
|
||||
use super::backend::InMemory;
|
||||
use super::ext::Ext;
|
||||
@@ -600,12 +630,12 @@ mod tests {
|
||||
let backend = InMemory::<Blake2Hasher>::from(initial).try_into_trie_backend().unwrap();
|
||||
let mut overlay = OverlayedChanges {
|
||||
committed: map![
|
||||
b"aba".to_vec() => Some(b"1312".to_vec()).into(),
|
||||
b"bab".to_vec() => Some(b"228".to_vec()).into()
|
||||
b"aba".to_vec() => OverlayedValue::from(Some(b"1312".to_vec())),
|
||||
b"bab".to_vec() => OverlayedValue::from(Some(b"228".to_vec()))
|
||||
],
|
||||
prospective: map![
|
||||
b"abd".to_vec() => Some(b"69".to_vec()).into(),
|
||||
b"bbd".to_vec() => Some(b"42".to_vec()).into()
|
||||
b"abd".to_vec() => OverlayedValue::from(Some(b"69".to_vec())),
|
||||
b"bbd".to_vec() => OverlayedValue::from(Some(b"42".to_vec()))
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
@@ -631,6 +661,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_child_storage_works() {
|
||||
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::new();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage));
|
||||
|
||||
assert!(ext.set_child_storage(b":child_storage:testchild".to_vec(), b"abc".to_vec(), b"def".to_vec()));
|
||||
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec()));
|
||||
ext.kill_child_storage(b":child_storage:testchild");
|
||||
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prove_read_and_proof_check_works() {
|
||||
// fetch read proof from 'remote' full node
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
//! The overlayed changes to state.
|
||||
|
||||
#[cfg(test)] use std::iter::FromIterator;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use codec::Decode;
|
||||
use changes_trie::{NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig};
|
||||
@@ -28,9 +29,9 @@ use primitives::storage::well_known_keys::EXTRINSIC_INDEX;
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct OverlayedChanges {
|
||||
/// Changes that are not yet committed.
|
||||
pub(crate) prospective: HashMap<Vec<u8>, OverlayedValue>,
|
||||
pub(crate) prospective: OverlayedChangeSet,
|
||||
/// Committed changes.
|
||||
pub(crate) committed: HashMap<Vec<u8>, OverlayedValue>,
|
||||
pub(crate) committed: OverlayedChangeSet,
|
||||
/// Changes trie configuration. None by default, but could be installed by the
|
||||
/// runtime if it supports change tries.
|
||||
pub(crate) changes_trie_config: Option<ChangesTrieConfig>,
|
||||
@@ -47,6 +48,39 @@ pub struct OverlayedValue {
|
||||
pub extrinsics: Option<HashSet<u32>>,
|
||||
}
|
||||
|
||||
/// Prospective or committed overlayed change set.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct OverlayedChangeSet {
|
||||
/// Top level storage changes.
|
||||
pub top: HashMap<Vec<u8>, OverlayedValue>,
|
||||
/// Child storage changes.
|
||||
pub children: HashMap<Vec<u8>, (Option<HashSet<u32>>, HashMap<Vec<u8>, Option<Vec<u8>>>)>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl FromIterator<(Vec<u8>, OverlayedValue)> for OverlayedChangeSet {
|
||||
fn from_iter<T: IntoIterator<Item = (Vec<u8>, OverlayedValue)>>(iter: T) -> Self {
|
||||
Self {
|
||||
top: iter.into_iter().collect(),
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayedChangeSet {
|
||||
/// Whether the change set is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.top.is_empty() && self.children.is_empty()
|
||||
}
|
||||
|
||||
/// Clear the change set.
|
||||
pub fn clear(&mut self) {
|
||||
self.top.clear();
|
||||
self.children.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayedChanges {
|
||||
/// Sets the changes trie configuration.
|
||||
///
|
||||
@@ -68,17 +102,36 @@ impl OverlayedChanges {
|
||||
/// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose
|
||||
/// value has been set.
|
||||
pub fn storage(&self, key: &[u8]) -> Option<Option<&[u8]>> {
|
||||
self.prospective.get(key)
|
||||
.or_else(|| self.committed.get(key))
|
||||
self.prospective.top.get(key)
|
||||
.or_else(|| self.committed.top.get(key))
|
||||
.map(|x| x.value.as_ref().map(AsRef::as_ref))
|
||||
}
|
||||
|
||||
/// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered
|
||||
/// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose
|
||||
/// value has been set.
|
||||
pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Option<Option<&[u8]>> {
|
||||
if let Some(map) = self.prospective.children.get(storage_key) {
|
||||
if let Some(val) = map.1.get(key) {
|
||||
return Some(val.as_ref().map(AsRef::as_ref));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(map) = self.committed.children.get(storage_key) {
|
||||
if let Some(val) = map.1.get(key) {
|
||||
return Some(val.as_ref().map(AsRef::as_ref));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Inserts the given key-value pair into the prospective change set.
|
||||
///
|
||||
/// `None` can be used to delete a value specified by the given key.
|
||||
pub(crate) fn set_storage(&mut self, key: Vec<u8>, val: Option<Vec<u8>>) {
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let entry = self.prospective.entry(key).or_default();
|
||||
let entry = self.prospective.top.entry(key).or_default();
|
||||
entry.value = val;
|
||||
|
||||
if let Some(extrinsic) = extrinsic_index {
|
||||
@@ -87,6 +140,57 @@ impl OverlayedChanges {
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts the given key-value pair into the prospective child change set.
|
||||
///
|
||||
/// `None` can be used to delete a value specified by the given key.
|
||||
pub(crate) fn set_child_storage(&mut self, storage_key: Vec<u8>, key: Vec<u8>, val: Option<Vec<u8>>) {
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let map_entry = self.prospective.children.entry(storage_key).or_default();
|
||||
map_entry.1.insert(key, val);
|
||||
|
||||
if let Some(extrinsic) = extrinsic_index {
|
||||
map_entry.0.get_or_insert_with(Default::default)
|
||||
.insert(extrinsic);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync the child storage root.
|
||||
pub(crate) fn sync_child_storage_root(&mut self, storage_key: &[u8], root: Option<Vec<u8>>) {
|
||||
let entry = self.prospective.top.entry(storage_key.to_vec()).or_default();
|
||||
entry.value = root;
|
||||
|
||||
if let Some((Some(extrinsics), _)) = self.prospective.children.get(storage_key) {
|
||||
for extrinsic in extrinsics {
|
||||
entry.extrinsics.get_or_insert_with(Default::default)
|
||||
.insert(*extrinsic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear child storage of given storage key.
|
||||
///
|
||||
/// NOTE that this doesn't take place immediately but written into the prospective
|
||||
/// change set, and still can be reverted by [`discard_prospective`].
|
||||
///
|
||||
/// [`discard_prospective`]: #method.discard_prospective
|
||||
pub(crate) fn clear_child_storage(&mut self, storage_key: &[u8]) {
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let map_entry = self.prospective.children.entry(storage_key.to_vec()).or_default();
|
||||
|
||||
if let Some(extrinsic) = extrinsic_index {
|
||||
map_entry.0.get_or_insert_with(Default::default)
|
||||
.insert(extrinsic);
|
||||
}
|
||||
|
||||
map_entry.1.values_mut().for_each(|e| *e = None);
|
||||
|
||||
if let Some((_, committed_map)) = self.committed.children.get(storage_key) {
|
||||
for (key, _) in committed_map.iter() {
|
||||
map_entry.1.insert(key.clone(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all key-value pairs which keys share the given prefix.
|
||||
///
|
||||
/// NOTE that this doesn't take place immediately but written into the prospective
|
||||
@@ -98,7 +202,7 @@ impl OverlayedChanges {
|
||||
|
||||
// Iterate over all prospective and mark all keys that share
|
||||
// the given prefix as removed (None).
|
||||
for (key, entry) in self.prospective.iter_mut() {
|
||||
for (key, entry) in self.prospective.top.iter_mut() {
|
||||
if key.starts_with(prefix) {
|
||||
entry.value = None;
|
||||
|
||||
@@ -111,9 +215,9 @@ impl OverlayedChanges {
|
||||
|
||||
// Then do the same with keys from commited changes.
|
||||
// NOTE that we are making changes in the prospective change set.
|
||||
for key in self.committed.keys() {
|
||||
for key in self.committed.top.keys() {
|
||||
if key.starts_with(prefix) {
|
||||
let entry = self.prospective.entry(key.clone()).or_default();
|
||||
let entry = self.prospective.top.entry(key.clone()).or_default();
|
||||
entry.value = None;
|
||||
|
||||
if let Some(extrinsic) = extrinsic_index {
|
||||
@@ -134,8 +238,8 @@ impl OverlayedChanges {
|
||||
if self.committed.is_empty() {
|
||||
::std::mem::swap(&mut self.prospective, &mut self.committed);
|
||||
} else {
|
||||
for (key, val) in self.prospective.drain() {
|
||||
let entry = self.committed.entry(key).or_default();
|
||||
for (key, val) in self.prospective.top.drain() {
|
||||
let entry = self.committed.top.entry(key).or_default();
|
||||
entry.value = val.value;
|
||||
|
||||
if let Some(prospective_extrinsics) = val.extrinsics {
|
||||
@@ -143,16 +247,16 @@ impl OverlayedChanges {
|
||||
.extend(prospective_extrinsics);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (storage_key, map) in self.prospective.children.drain() {
|
||||
let entry = self.committed.children.entry(storage_key).or_default();
|
||||
entry.1.extend(map.1.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
|
||||
/// Drain committed changes to an iterator.
|
||||
///
|
||||
/// Panics:
|
||||
/// Will panic if there are any uncommitted prospective changes.
|
||||
pub fn drain<'a>(&'a mut self) -> impl Iterator<Item=(Vec<u8>, OverlayedValue)> + 'a {
|
||||
assert!(self.prospective.is_empty());
|
||||
self.committed.drain()
|
||||
if let Some(prospective_extrinsics) = map.0 {
|
||||
entry.0.get_or_insert_with(Default::default)
|
||||
.extend(prospective_extrinsics);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume `OverlayedChanges` and take committed set.
|
||||
@@ -161,14 +265,14 @@ impl OverlayedChanges {
|
||||
/// Will panic if there are any uncommitted prospective changes.
|
||||
pub fn into_committed(self) -> impl Iterator<Item=(Vec<u8>, Option<Vec<u8>>)> {
|
||||
assert!(self.prospective.is_empty());
|
||||
self.committed.into_iter().map(|(k, v)| (k, v.value))
|
||||
self.committed.top.into_iter().map(|(k, v)| (k, v.value))
|
||||
}
|
||||
|
||||
/// Inserts storage entry responsible for current extrinsic index.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn set_extrinsic_index(&mut self, extrinsic_index: u32) {
|
||||
use codec::Encode;
|
||||
self.prospective.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue {
|
||||
self.prospective.top.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue {
|
||||
value: Some(extrinsic_index.encode()),
|
||||
extrinsics: None,
|
||||
});
|
||||
@@ -293,7 +397,7 @@ mod tests {
|
||||
digest_interval: 4, digest_levels: 1,
|
||||
}), true);
|
||||
assert_eq!(
|
||||
strip_extrinsic_index(&overlay.prospective),
|
||||
strip_extrinsic_index(&overlay.prospective.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![2]), extrinsics: Some(vec![0].into_iter().collect()) }),
|
||||
].into_iter().collect(),
|
||||
@@ -329,7 +433,7 @@ mod tests {
|
||||
overlay.set_extrinsic_index(2);
|
||||
overlay.set_storage(vec![1], Some(vec![6]));
|
||||
|
||||
assert_eq!(strip_extrinsic_index(&overlay.prospective),
|
||||
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![6]), extrinsics: Some(vec![0, 2].into_iter().collect()) }),
|
||||
(vec![3], OverlayedValue { value: Some(vec![4]), extrinsics: Some(vec![1].into_iter().collect()) }),
|
||||
@@ -344,14 +448,14 @@ mod tests {
|
||||
overlay.set_extrinsic_index(4);
|
||||
overlay.set_storage(vec![1], Some(vec![8]));
|
||||
|
||||
assert_eq!(strip_extrinsic_index(&overlay.committed),
|
||||
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![6]), extrinsics: Some(vec![0, 2].into_iter().collect()) }),
|
||||
(vec![3], OverlayedValue { value: Some(vec![4]), extrinsics: Some(vec![1].into_iter().collect()) }),
|
||||
(vec![100], OverlayedValue { value: Some(vec![101]), extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }),
|
||||
].into_iter().collect());
|
||||
|
||||
assert_eq!(strip_extrinsic_index(&overlay.prospective),
|
||||
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![8]), extrinsics: Some(vec![4].into_iter().collect()) }),
|
||||
(vec![3], OverlayedValue { value: Some(vec![7]), extrinsics: Some(vec![3].into_iter().collect()) }),
|
||||
@@ -359,7 +463,7 @@ mod tests {
|
||||
|
||||
overlay.commit_prospective();
|
||||
|
||||
assert_eq!(strip_extrinsic_index(&overlay.committed),
|
||||
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![8]), extrinsics: Some(vec![0, 2, 4].into_iter().collect()) }),
|
||||
(vec![3], OverlayedValue { value: Some(vec![7]), extrinsics: Some(vec![1, 3].into_iter().collect()) }),
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::cell::RefCell;
|
||||
use hash_db::Hasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use hash_db::HashDB;
|
||||
use trie::{TrieDB, Trie, Recorder, MemoryDB, TrieError};
|
||||
use trie::{Recorder, MemoryDB, TrieError, default_child_trie_root, read_trie_value_with, read_child_trie_value_with, record_all_keys};
|
||||
use trie_backend::TrieBackend;
|
||||
use trie_backend_essence::{Ephemeral, TrieBackendEssence, TrieBackendStorage};
|
||||
use {Error, ExecutionError, Backend};
|
||||
@@ -47,10 +47,21 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H>
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
TrieDB::<H>::new(&eph, self.backend.root()).map_err(map_e)?
|
||||
.get_with(key, &mut *self.proof_recorder)
|
||||
.map(|x| x.map(|val| val.to_vec()))
|
||||
.map_err(map_e)
|
||||
read_trie_value_with(&eph, self.backend.root(), key, &mut *self.proof_recorder).map_err(map_e)
|
||||
}
|
||||
|
||||
pub fn child_storage(&mut self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, String> {
|
||||
let root = self.storage(storage_key)?.unwrap_or(default_child_trie_root::<H>(storage_key));
|
||||
|
||||
let mut read_overlay = MemoryDB::default();
|
||||
let eph = Ephemeral::new(
|
||||
self.backend.backend_storage(),
|
||||
&mut read_overlay,
|
||||
);
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_child_trie_value_with(storage_key, &eph, &root, key, &mut *self.proof_recorder).map_err(map_e)
|
||||
}
|
||||
|
||||
pub fn record_all_keys(&mut self) {
|
||||
@@ -62,20 +73,7 @@ impl<'a, S, H> ProvingBackendEssence<'a, S, H>
|
||||
|
||||
let mut iter = move || -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let root = self.backend.root();
|
||||
let trie = TrieDB::<H>::new(&eph, root)?;
|
||||
let iter = trie.iter()?;
|
||||
|
||||
for x in iter {
|
||||
let (key, _) = x?;
|
||||
|
||||
// there's currently no API like iter_with()
|
||||
// => use iter to enumerate all keys AND lookup each
|
||||
// key using get_with
|
||||
trie.get_with(&key, &mut *self.proof_recorder)
|
||||
.map(|x| x.map(|val| val.to_vec()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
record_all_keys::<H>(&eph, root, &mut *self.proof_recorder)
|
||||
};
|
||||
|
||||
if let Err(e) = iter() {
|
||||
@@ -128,6 +126,18 @@ impl<S, H> Backend<H> for ProvingBackend<S, H>
|
||||
}.storage(key)
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
ProvingBackendEssence {
|
||||
backend: self.backend.essence(),
|
||||
proof_recorder: &mut *self.proof_recorder.try_borrow_mut()
|
||||
.expect("only fails when already borrowed; child_storage() is non-reentrant; qed"),
|
||||
}.child_storage(storage_key, key)
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
|
||||
self.backend.for_keys_in_child_storage(storage_key, f)
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
self.backend.for_keys_with_prefix(prefix, f)
|
||||
}
|
||||
@@ -142,6 +152,14 @@ impl<S, H> Backend<H> for ProvingBackend<S, H>
|
||||
self.backend.storage_root(delta)
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord
|
||||
{
|
||||
self.backend.child_storage_root(storage_key, delta)
|
||||
}
|
||||
|
||||
fn try_into_trie_backend(self) -> Option<TrieBackend<Self::TrieBackendStorage, H>> {
|
||||
None
|
||||
}
|
||||
@@ -211,7 +229,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn proof_recorded_and_checked() {
|
||||
let contents = (0..64).map(|i| (vec![i], Some(vec![i]))).collect::<Vec<_>>();
|
||||
let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))).collect::<Vec<_>>();
|
||||
let in_memory = InMemory::<Blake2Hasher>::default();
|
||||
let in_memory = in_memory.update(contents);
|
||||
let in_memory_root = in_memory.storage_root(::std::iter::empty()).0;
|
||||
|
||||
@@ -103,6 +103,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
|
||||
self.inner.get(key).map(|x| x.to_vec())
|
||||
}
|
||||
|
||||
fn child_storage(&self, _storage_key: &[u8], _key: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
|
||||
self.changes.set_storage(key.clone(), maybe_value.clone());
|
||||
match maybe_value {
|
||||
@@ -111,6 +115,12 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
|
||||
}
|
||||
}
|
||||
|
||||
fn place_child_storage(&mut self, _storage_key: Vec<u8>, _key: Vec<u8>, _value: Option<Vec<u8>>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn kill_child_storage(&mut self, _storage_key: &[u8]) { }
|
||||
|
||||
fn clear_prefix(&mut self, prefix: &[u8]) {
|
||||
self.changes.clear_prefix(prefix);
|
||||
self.inner.retain(|key, _| !key.starts_with(prefix));
|
||||
@@ -122,6 +132,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
|
||||
trie_root::<H, _, _, _>(self.inner.clone())
|
||||
}
|
||||
|
||||
fn child_storage_root(&mut self, _storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
|
||||
compute_changes_trie_root::<_, _, H>(
|
||||
&InMemory::default(),
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use hash_db::Hasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::{TrieDB, TrieDBMut, TrieError, Trie, TrieMut, MemoryDB};
|
||||
use trie::{TrieDB, TrieError, Trie, MemoryDB, delta_trie_root, default_child_trie_root, child_delta_trie_root};
|
||||
use trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral};
|
||||
use {Backend};
|
||||
|
||||
@@ -64,10 +64,18 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
self.essence.storage(key)
|
||||
}
|
||||
|
||||
fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.essence.child_storage(storage_key, key)
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
self.essence.for_keys_with_prefix(prefix, f)
|
||||
}
|
||||
|
||||
fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
|
||||
self.essence.for_keys_in_child_storage(storage_key, f)
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
let mut read_overlay = MemoryDB::default(); // TODO: use new for correctness
|
||||
let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay);
|
||||
@@ -97,22 +105,45 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
{
|
||||
let mut write_overlay = MemoryDB::default();
|
||||
let mut root = *self.essence.root();
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(
|
||||
self.essence.backend_storage(),
|
||||
&mut write_overlay,
|
||||
);
|
||||
|
||||
let mut trie = TrieDBMut::<H>::from_existing(&mut eph, &mut root).expect("prior state root to exist"); // TODO: handle gracefully
|
||||
for (key, change) in delta {
|
||||
let result = match change {
|
||||
Some(val) => trie.insert(&key, &val),
|
||||
None => trie.remove(&key), // TODO: archive mode
|
||||
};
|
||||
match delta_trie_root::<H, _, _, _>(&mut eph, root, delta) {
|
||||
Ok(ret) => root = ret,
|
||||
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
warn!(target: "trie", "Failed to write to trie: {}", e);
|
||||
}
|
||||
(root, write_overlay)
|
||||
}
|
||||
|
||||
fn child_storage_root<I>(&self, storage_key: &[u8], delta: I) -> (Vec<u8>, Self::Transaction)
|
||||
where
|
||||
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
|
||||
H::Out: Ord
|
||||
{
|
||||
let mut write_overlay = MemoryDB::default();
|
||||
let mut root = match self.storage(storage_key) {
|
||||
Ok(value) => value.unwrap_or(default_child_trie_root::<H>(storage_key)),
|
||||
Err(e) => {
|
||||
warn!(target: "trie", "Failed to read child storage root: {}", e);
|
||||
default_child_trie_root::<H>(storage_key)
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
let mut eph = Ephemeral::new(
|
||||
self.essence.backend_storage(),
|
||||
&mut write_overlay,
|
||||
);
|
||||
|
||||
match child_delta_trie_root::<H, _, _, _>(storage_key, &mut eph, root.clone(), delta) {
|
||||
Ok(ret) => root = ret,
|
||||
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +159,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
pub mod tests {
|
||||
use std::collections::HashSet;
|
||||
use primitives::{Blake2Hasher, H256};
|
||||
use trie::{TrieMut, TrieDBMut};
|
||||
use super::*;
|
||||
|
||||
fn test_db() -> (MemoryDB<Blake2Hasher>, H256) {
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use hash_db::{self, Hasher};
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::{TrieDB, Trie, MemoryDB, DBValue, TrieError};
|
||||
use trie::{TrieDB, Trie, MemoryDB, DBValue, TrieError, default_child_trie_root, read_trie_value, read_child_trie_value, for_keys_in_child_trie};
|
||||
use changes_trie::Storage as ChangesTrieStorage;
|
||||
|
||||
/// Patricia trie-based storage trait.
|
||||
@@ -66,8 +66,43 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
TrieDB::<H>::new(&eph, &self.root).map_err(map_e)?
|
||||
.get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e)
|
||||
read_trie_value(&eph, &self.root, key).map_err(map_e)
|
||||
}
|
||||
|
||||
/// Get the value of child storage at given key.
|
||||
pub fn child_storage(&self, storage_key: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, String> {
|
||||
let root = self.storage(storage_key)?.unwrap_or(default_child_trie_root::<H>(storage_key));
|
||||
|
||||
let mut read_overlay = MemoryDB::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_child_trie_value(storage_key, &eph, &root, key).map_err(map_e)
|
||||
}
|
||||
|
||||
/// Retrieve all entries keys of child storage and call `f` for each of those keys.
|
||||
pub fn for_keys_in_child_storage<F: FnMut(&[u8])>(&self, storage_key: &[u8], f: F) {
|
||||
let root = match self.storage(storage_key) {
|
||||
Ok(v) => v.unwrap_or(default_child_trie_root::<H>(storage_key)),
|
||||
Err(e) => {
|
||||
debug!(target: "trie", "Error while iterating child storage: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut read_overlay = MemoryDB::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
if let Err(e) = for_keys_in_child_trie::<H, _>(storage_key, &eph, &root, f) {
|
||||
debug!(target: "trie", "Error while iterating child storage: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute given closure for all keys starting with prefix.
|
||||
|
||||
@@ -45,7 +45,7 @@ pub use trie_stream::TrieStream;
|
||||
/// The Substrate format implementation of `NodeCodec`.
|
||||
pub use node_codec::NodeCodec;
|
||||
/// Various re-exports from the `trie-db` crate.
|
||||
pub use trie_db::{Trie, TrieMut, DBValue, Recorder};
|
||||
pub use trie_db::{Trie, TrieMut, DBValue, Recorder, Query};
|
||||
|
||||
/// As in `trie_db`, but less generic, error type for the crate.
|
||||
pub type TrieError<H> = trie_db::TrieError<H, Error>;
|
||||
@@ -53,7 +53,7 @@ pub type TrieError<H> = trie_db::TrieError<H, Error>;
|
||||
pub trait AsHashDB<H: Hasher>: hash_db::AsHashDB<H, trie_db::DBValue> {}
|
||||
impl<H: Hasher, T: hash_db::AsHashDB<H, trie_db::DBValue>> AsHashDB<H> for T {}
|
||||
/// As in `hash_db`, but less generic, trait exposed.
|
||||
pub type HashDB<H> = hash_db::HashDB<H, trie_db::DBValue>;
|
||||
pub type HashDB<'a, H> = hash_db::HashDB<H, trie_db::DBValue> + 'a;
|
||||
/// As in `memory_db`, but less generic, trait exposed.
|
||||
pub type MemoryDB<H> = memory_db::MemoryDB<H, trie_db::DBValue>;
|
||||
|
||||
@@ -73,6 +73,36 @@ pub fn trie_root<H: Hasher, I, A, B>(input: I) -> H::Out where
|
||||
trie_root::trie_root::<H, TrieStream, _, _, _>(input)
|
||||
}
|
||||
|
||||
/// Determine a trie root given a hash DB and delta values.
|
||||
pub fn delta_trie_root<H: Hasher, I, A, B>(db: &mut HashDB<H>, mut root: H::Out, delta: I) -> Result<H::Out, Box<TrieError<H::Out>>> where
|
||||
I: IntoIterator<Item = (A, Option<B>)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
{
|
||||
let mut trie = TrieDBMut::<H>::from_existing(db, &mut root)?;
|
||||
|
||||
for (key, change) in delta {
|
||||
match change {
|
||||
Some(val) => trie.insert(key.as_ref(), val.as_ref())?,
|
||||
None => trie.remove(key.as_ref())?, // TODO: archive mode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
/// Read a value from the trie.
|
||||
pub fn read_trie_value<H: Hasher>(db: &HashDB<H>, root: &H::Out, key: &[u8]) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
|
||||
Ok(TrieDB::<H>::new(db, root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
/// Read a value from the trie with given Query.
|
||||
pub fn read_trie_value_with<H: Hasher, Q: Query<H, Item=DBValue>>(db: &HashDB<H>, root: &H::Out, key: &[u8], query: Q) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
|
||||
Ok(TrieDB::<H>::new(db, root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
/// Determine a trie root node's data given its ordered contents, closed form.
|
||||
pub fn unhashed_trie<H: Hasher, I, A, B>(input: I) -> Vec<u8> where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
@@ -95,6 +125,102 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
/// Determine whether a child trie key is valid. `child_trie_root` and `child_delta_trie_root` can panic if invalid value is provided to them.
|
||||
pub fn is_child_trie_key_valid<H: Hasher>(_storage_key: &[u8]) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Determine the default child trie root.
|
||||
pub fn default_child_trie_root<H: Hasher>(_storage_key: &[u8]) -> Vec<u8> {
|
||||
let mut db = MemoryDB::default();
|
||||
let mut root = H::Out::default();
|
||||
let mut empty = TrieDBMut::<H>::new(&mut db, &mut root);
|
||||
empty.commit();
|
||||
empty.root().as_ref().to_vec()
|
||||
}
|
||||
|
||||
/// Determine a child trie root given its ordered contents, closed form. H is the default hasher, but a generic
|
||||
/// implementation may ignore this type parameter and use other hashers.
|
||||
pub fn child_trie_root<H: Hasher, I, A, B>(_storage_key: &[u8], input: I) -> Vec<u8> where
|
||||
I: IntoIterator<Item = (A, B)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
trie_root::<H, _, _, _>(input).as_ref().iter().cloned().collect()
|
||||
}
|
||||
|
||||
/// Determine a child trie root given a hash DB and delta values. H is the default hasher, but a generic implementation may ignore this type parameter and use other hashers.
|
||||
pub fn child_delta_trie_root<H: Hasher, I, A, B>(_storage_key: &[u8], db: &mut HashDB<H>, root_vec: Vec<u8>, delta: I) -> Result<Vec<u8>, Box<TrieError<H::Out>>> where
|
||||
I: IntoIterator<Item = (A, Option<B>)>,
|
||||
A: AsRef<[u8]> + Ord,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(&root_vec); // root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
|
||||
{
|
||||
let mut trie = TrieDBMut::<H>::from_existing(db, &mut root)?;
|
||||
|
||||
for (key, change) in delta {
|
||||
match change {
|
||||
Some(val) => trie.insert(key.as_ref(), val.as_ref())?,
|
||||
None => trie.remove(key.as_ref())?, // TODO: archive mode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(root.as_ref().to_vec())
|
||||
}
|
||||
|
||||
/// Call `f` for all keys in a child trie.
|
||||
pub fn for_keys_in_child_trie<H: Hasher, F: FnMut(&[u8])>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], mut f: F) -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
|
||||
let trie = TrieDB::<H>::new(db, &root)?;
|
||||
let iter = trie.iter()?;
|
||||
|
||||
for x in iter {
|
||||
let (key, _) = x?;
|
||||
f(&key);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Record all keys for a given root.
|
||||
pub fn record_all_keys<H: Hasher>(db: &HashDB<H>, root: &H::Out, recorder: &mut Recorder<H::Out>) -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let trie = TrieDB::<H>::new(db, root)?;
|
||||
let iter = trie.iter()?;
|
||||
|
||||
for x in iter {
|
||||
let (key, _) = x?;
|
||||
|
||||
// there's currently no API like iter_with()
|
||||
// => use iter to enumerate all keys AND lookup each
|
||||
// key using get_with
|
||||
trie.get_with(&key, &mut *recorder)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a value from the child trie.
|
||||
pub fn read_child_trie_value<H: Hasher>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], key: &[u8]) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
|
||||
Ok(TrieDB::<H>::new(db, &root)?.get(key).map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
/// Read a value from the child trie with given query.
|
||||
pub fn read_child_trie_value_with<H: Hasher, Q: Query<H, Item=DBValue>>(_storage_key: &[u8], db: &HashDB<H>, root_slice: &[u8], key: &[u8], query: Q) -> Result<Option<Vec<u8>>, Box<TrieError<H::Out>>> {
|
||||
let mut root = H::Out::default();
|
||||
root.as_mut().copy_from_slice(root_slice); // root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
|
||||
Ok(TrieDB::<H>::new(db, &root)?.get_with(key, query).map(|x| x.map(|val| val.to_vec()))?)
|
||||
}
|
||||
|
||||
// Utilities (not exported):
|
||||
|
||||
const EMPTY_TRIE: u8 = 0;
|
||||
@@ -418,4 +544,4 @@ mod tests {
|
||||
|
||||
assert_eq!(pairs, iter_pairs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user