Forward port blake2 storage support (#2360)

* move storage maps to blake2_128 (#2268)

* remove default hash, introduce twox_128 and blake2

* use blake2_128 & create ext_blake2_128

* refactor code

* add benchmark

* factorize generator

* fix

* parameterizable hasher

* some fix

* fix

* fix

* fix

* metadata

* fix

* remove debug print

* map -> blake2_256

* fix test

* fix test

* Apply suggestions from code review

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* impl twox 128 concat (#2353)

* impl twox_128_concat

* comment addressed

* fix

* impl twox_128->64_concat

* fix test

* Fix compilation and cleanup some docs

* Apply suggestions from code review

Co-Authored-By: bkchr <bkchr@users.noreply.github.com>
This commit is contained in:
Bastian Köcher
2019-04-24 11:05:22 +02:00
committed by Gavin Wood
parent 21d1ee4e99
commit f0862606b7
38 changed files with 1101 additions and 732 deletions
+7 -7
View File
@@ -1536,7 +1536,7 @@ impl<B, E, Block, RA> backend::AuxStore for Client<B, E, Block, RA>
pub(crate) mod tests {
use std::collections::HashMap;
use super::*;
use primitives::twox_128;
use primitives::blake2_256;
use runtime_primitives::traits::DigestItem as DigestItemT;
use runtime_primitives::generic::DigestItem;
use test_client::{self, TestClient, AccountKeyring};
@@ -1586,12 +1586,12 @@ pub(crate) mod tests {
}
// prepare test cases
let alice = twox_128(&runtime::system::balance_of_key(AccountKeyring::Alice.into())).to_vec();
let bob = twox_128(&runtime::system::balance_of_key(AccountKeyring::Bob.into())).to_vec();
let charlie = twox_128(&runtime::system::balance_of_key(AccountKeyring::Charlie.into())).to_vec();
let dave = twox_128(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let eve = twox_128(&runtime::system::balance_of_key(AccountKeyring::Eve.into())).to_vec();
let ferdie = twox_128(&runtime::system::balance_of_key(AccountKeyring::Ferdie.into())).to_vec();
let alice = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())).to_vec();
let bob = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Bob.into())).to_vec();
let charlie = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Charlie.into())).to_vec();
let dave = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let eve = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Eve.into())).to_vec();
let ferdie = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Ferdie.into())).to_vec();
let test_cases = vec![
(1, 4, alice.clone(), vec![(4, 0), (1, 0)]),
(1, 3, alice.clone(), vec![(1, 0)]),
+3 -3
View File
@@ -404,7 +404,7 @@ pub mod tests {
use crate::light::fetcher::{Fetcher, FetchChecker, LightDataChecker,
RemoteCallRequest, RemoteHeaderRequest};
use crate::light::blockchain::tests::{DummyStorage, DummyBlockchain};
use primitives::{twox_128, Blake2Hasher};
use primitives::{blake2_256, Blake2Hasher};
use primitives::storage::{StorageKey, well_known_keys};
use runtime_primitives::generic::BlockId;
use state_machine::Backend;
@@ -587,7 +587,7 @@ pub mod tests {
// we're testing this test case here:
// (1, 4, dave.clone(), vec![(4, 0), (1, 1), (1, 0)]),
let (remote_client, remote_roots, _) = prepare_client_with_key_changes();
let dave = twox_128(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let dave = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let dave = StorageKey(dave);
// 'fetch' changes proof from remote node:
@@ -699,7 +699,7 @@ pub mod tests {
let (remote_client, remote_roots, _) = prepare_client_with_key_changes();
let local_cht_root = cht::compute_root::<Header, Blake2Hasher, _>(
4, 0, remote_roots.iter().cloned().map(|ct| Ok(Some(ct)))).unwrap();
let dave = twox_128(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let dave = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
let dave = StorageKey(dave);
// 'fetch' changes proof from remote node:
+54 -1
View File
@@ -28,7 +28,7 @@ use wasmi::memory_units::{Pages};
use state_machine::{Externalities, ChildStorageKey};
use crate::error::{Error, ErrorKind, Result};
use crate::wasm_utils::UserError;
use primitives::{blake2_256, twox_128, twox_256, ed25519, sr25519, Pair};
use primitives::{blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair};
use primitives::hexdisplay::HexDisplay;
use primitives::sandbox as sandbox_primitives;
use primitives::{H256, Blake2Hasher};
@@ -448,6 +448,30 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
ext_chain_id() -> u64 => {
Ok(this.ext.chain_id())
},
ext_twox_64(data: *const u8, len: u32, out: *mut u8) => {
let result: [u8; 8] = if len == 0 {
let hashed = twox_64(&[0u8; 0]);
debug_trace!(target: "xxhash", "XXhash: '' -> {}", HexDisplay::from(&hashed));
this.hash_lookup.insert(hashed.to_vec(), vec![]);
hashed
} else {
let key = this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_twox_64"))?;
let hashed_key = twox_64(&key);
debug_trace!(target: "xxhash", "XXhash: {} -> {}",
if let Ok(_skey) = ::std::str::from_utf8(&key) {
_skey
} else {
&format!("{}", HexDisplay::from(&key))
},
HexDisplay::from(&hashed_key)
);
this.hash_lookup.insert(hashed_key.to_vec(), key);
hashed_key
};
this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_64"))?;
Ok(())
},
ext_twox_128(data: *const u8, len: u32, out: *mut u8) => {
let result: [u8; 16] = if len == 0 {
let hashed = twox_128(&[0u8; 0]);
@@ -481,6 +505,21 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_twox_256"))?;
Ok(())
},
ext_blake2_128(data: *const u8, len: u32, out: *mut u8) => {
let result: [u8; 16] = if len == 0 {
let hashed = blake2_128(&[0u8; 0]);
this.hash_lookup.insert(hashed.to_vec(), vec![]);
hashed
} else {
let key = this.memory.get(data, len as usize).map_err(|_| UserError("Invalid attempt to get key in ext_blake2_128"))?;
let hashed_key = blake2_128(&key);
this.hash_lookup.insert(hashed_key.to_vec(), key);
hashed_key
};
this.memory.set(out, &result).map_err(|_| UserError("Invalid attempt to set result in ext_blake2_128"))?;
Ok(())
},
ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => {
let result: [u8; 32] = if len == 0 {
blake2_256(&[0u8; 0])
@@ -940,6 +979,20 @@ mod tests {
);
}
#[test]
fn blake2_128_should_work() {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_128", &[]).unwrap(),
blake2_128(&b""[..]).encode()
);
assert_eq!(
WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_blake2_128", b"Hello world!").unwrap(),
blake2_128(&b"Hello world!"[..]).encode()
);
}
#[test]
fn twox_256_should_work() {
let mut ext = TestExternalities::default();
+2 -1
View File
@@ -7,7 +7,7 @@ use alloc::vec::Vec;
use alloc::slice;
use runtime_io::{
set_storage, storage, clear_prefix, print, blake2_256,
set_storage, storage, clear_prefix, print, blake2_128, blake2_256,
twox_128, twox_256, ed25519_verify, sr25519_verify, enumerated_trie_root
};
@@ -68,6 +68,7 @@ impl_stubs!(
input.to_vec()
},
test_blake2_256 => |input| blake2_256(input).to_vec(),
test_blake2_128 => |input| blake2_128(input).to_vec(),
test_twox_256 => |input| twox_256(input).to_vec(),
test_twox_128 => |input| twox_128(input).to_vec(),
test_ed25519_verify => |input: &[u8]| {
+4 -2
View File
@@ -31,8 +31,10 @@ regex = {version = "1.1", optional = true }
[dev-dependencies]
substrate-serializer = { path = "../serializer" }
pretty_assertions = "0.6.1"
heapsize = "0.4.2"
pretty_assertions = "0.6"
heapsize = "0.4"
hex-literal = "0.1"
rand = "0.6"
[features]
default = ["std"]
@@ -0,0 +1,60 @@
// Copyright 2019 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO: Move benchmark to criterion #2354
#![feature(test)]
extern crate test;
use hex_literal::{hex, hex_impl};
use substrate_primitives::hashing::{twox_128, blake2_128};
const MAX_KEY_SIZE: u32 = 32;
fn data_set() -> Vec<Vec<u8>> {
use rand::SeedableRng;
use rand::Rng;
let rnd: [u8; 32] = rand::rngs::StdRng::seed_from_u64(12).gen();
let mut rnd = rnd.iter().cycle();
let mut res = Vec::new();
for size in 1..=MAX_KEY_SIZE {
for _ in 0..1_000 {
let value = (0..size)
.map(|_| rnd.next().unwrap().clone())
.collect();
res.push(value);
}
}
res
}
fn bench_hash_128(b: &mut test::Bencher, f: &Fn(&[u8]) -> [u8; 16]) {
let data_set = data_set();
b.iter(|| {
for data in &data_set {
let _a = f(data);
}
});
}
#[bench]
fn bench_blake2_128(b: &mut test::Bencher) {
bench_hash_128(b, &blake2_128);
}
#[bench]
fn bench_twox_128(b: &mut test::Bencher) {
bench_hash_128(b, &twox_128);
}
+17
View File
@@ -55,6 +55,23 @@ pub fn blake2_128(data: &[u8]) -> [u8; 16] {
r
}
/// Do a XX 64-bit hash and place result in `dest`.
pub fn twox_64_into(data: &[u8], dest: &mut [u8; 8]) {
use ::core::hash::Hasher;
let mut h0 = twox_hash::XxHash::with_seed(0);
h0.write(data);
let r0 = h0.finish();
use byteorder::{ByteOrder, LittleEndian};
LittleEndian::write_u64(&mut dest[0..8], r0);
}
/// Do a XX 64-bit hash and return result.
pub fn twox_64(data: &[u8]) -> [u8; 8] {
let mut r: [u8; 8] = [0; 8];
twox_64_into(data, &mut r);
r
}
/// Do a XX 128-bit hash and place result in `dest`.
pub fn twox_128_into(data: &[u8], dest: &mut [u8; 16]) {
use ::core::hash::Hasher;
+1 -1
View File
@@ -46,7 +46,7 @@ pub use impl_serde::serialize as bytes;
#[cfg(feature = "std")]
pub mod hashing;
#[cfg(feature = "std")]
pub use hashing::{blake2_256, twox_128, twox_256};
pub use hashing::{blake2_128, blake2_256, twox_64, twox_128, twox_256};
#[cfg(feature = "std")]
pub mod hexdisplay;
pub mod crypto;
+3 -3
View File
@@ -17,7 +17,7 @@
use super::*;
use self::error::{Error, ErrorKind};
use sr_io::twox_128;
use sr_io::blake2_256;
use assert_matches::assert_matches;
use consensus::BlockOrigin;
use test_client::{self, runtime, AccountKeyring, TestClient, BlockBuilderExt};
@@ -88,7 +88,7 @@ fn should_send_initial_storage_changes_and_notifications() {
{
let api = State::new(Arc::new(test_client::new()), Subscriptions::new(remote));
let alice_balance_key = twox_128(&test_runtime::system::balance_of_key(AccountKeyring::Alice.into()));
let alice_balance_key = blake2_256(&test_runtime::system::balance_of_key(AccountKeyring::Alice.into()));
api.subscribe_storage(Default::default(), subscriber, Some(vec![
StorageKey(alice_balance_key.to_vec()),
@@ -147,7 +147,7 @@ fn should_query_storage() {
let block2_hash = add_block(1);
let genesis_hash = client.genesis_hash();
let alice_balance_key = twox_128(&test_runtime::system::balance_of_key(AccountKeyring::Alice.into()));
let alice_balance_key = blake2_256(&test_runtime::system::balance_of_key(AccountKeyring::Alice.into()));
let mut expected = vec![
StorageChangeSet {
+2 -2
View File
@@ -18,8 +18,8 @@
pub use parity_codec as codec;
// re-export hashing functions.
pub use primitives::{
blake2_256, twox_128, twox_256, ed25519, Blake2Hasher, sr25519,
Pair
blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher,
sr25519, Pair
};
pub use tiny_keccak::keccak256 as keccak_256;
// Switch to this after PoC-3
+20
View File
@@ -273,7 +273,9 @@ extern_functions! {
/// Hash calculation and verification
fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8);
fn ext_blake2_128(data: *const u8, len: u32, out: *mut u8);
fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_64(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_128(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_256(data: *const u8, len: u32, out: *mut u8);
fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8);
@@ -544,6 +546,15 @@ pub fn blake2_256(data: &[u8]) -> [u8; 32] {
result
}
/// Conduct a 128-bit Blake2 hash.
pub fn blake2_128(data: &[u8]) -> [u8; 16] {
let mut result: [u8; 16] = Default::default();
unsafe {
ext_blake2_128.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
}
result
}
/// Conduct a 256-bit Keccak hash.
pub fn keccak_256(data: &[u8]) -> [u8; 32] {
let mut result: [u8; 32] = Default::default();
@@ -571,6 +582,15 @@ pub fn twox_128(data: &[u8]) -> [u8; 16] {
result
}
/// Conduct two XX hashes to give a 64-bit result.
pub fn twox_64(data: &[u8]) -> [u8; 8] {
let mut result: [u8; 8] = Default::default();
unsafe {
ext_twox_64.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
}
result
}
/// Verify a ed25519 signature.
pub fn ed25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
unsafe {
-8
View File
@@ -88,14 +88,6 @@ pub use serde::{Serialize, Deserialize, de::DeserializeOwned};
/// Complex storage builder stuff.
#[cfg(feature = "std")]
pub trait BuildStorage: Sized {
/// Hash given slice.
///
/// Default to xx128 hashing.
fn hash(data: &[u8]) -> [u8; 16] {
let r = runtime_io::twox_128(data);
log::trace!(target: "build_storage", "{} <= {}", substrate_primitives::hexdisplay::HexDisplay::from(&r), ascii_format(data));
r
}
/// Build the storage out of this builder.
fn build_storage(self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> {
let mut storage = Default::default();
@@ -17,7 +17,7 @@
//! Tool for creating the genesis block.
use std::collections::HashMap;
use runtime_io::twox_128;
use runtime_io::{blake2_256, twox_128};
use super::AccountId;
use parity_codec::{Encode, KeyedVec, Joiner};
use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys};
@@ -47,7 +47,7 @@ impl GenesisConfig {
let wasm_runtime = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm").to_vec();
let mut map: HashMap<Vec<u8>, Vec<u8>> = self.balances.iter()
.map(|&(ref account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance)))
.map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec()))
.map(|(k, v)| (blake2_256(&k[..])[..].to_vec(), v.to_vec()))
.chain(vec![
(well_known_keys::CODE.into(), wasm_runtime),
(well_known_keys::HEAP_PAGES.into(), vec![].and(&(16 as u64))),
+12 -12
View File
@@ -18,7 +18,7 @@
//! and depositing logs.
use rstd::prelude::*;
use runtime_io::{storage_root, enumerated_trie_root, storage_changes_root, twox_128};
use runtime_io::{storage_root, enumerated_trie_root, storage_changes_root, twox_128, blake2_256};
use runtime_support::storage::{self, StorageValue, StorageMap};
use runtime_support::storage_items;
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256, Digest as DigestT};
@@ -45,11 +45,11 @@ pub fn balance_of_key(who: AccountId) -> Vec<u8> {
}
pub fn balance_of(who: AccountId) -> u64 {
storage::get_or(&balance_of_key(who), 0)
storage::hashed::get_or(&blake2_256, &balance_of_key(who), 0)
}
pub fn nonce_of(who: AccountId) -> u64 {
storage::get_or(&who.to_keyed_vec(NONCE_OF), 0)
storage::hashed::get_or(&blake2_256, &who.to_keyed_vec(NONCE_OF), 0)
}
/// Get authorities at given block.
@@ -152,7 +152,7 @@ pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
let tx = utx.transfer();
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::get_or(&nonce_key, 0);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if tx.nonce < expected_nonce {
return TransactionValidity::Invalid(ApplyError::Stale as i8);
}
@@ -241,26 +241,26 @@ fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
fn execute_transfer_backend(tx: &Transfer) -> ApplyResult {
// check nonce
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
let expected_nonce: u64 = storage::get_or(&nonce_key, 0);
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
if !(tx.nonce == expected_nonce) {
return Err(ApplyError::Stale)
}
// increment nonce in storage
storage::put(&nonce_key, &(expected_nonce + 1));
storage::hashed::put(&blake2_256, &nonce_key, &(expected_nonce + 1));
// check sender balance
let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF);
let from_balance: u64 = storage::get_or(&from_balance_key, 0);
let from_balance: u64 = storage::hashed::get_or(&blake2_256, &from_balance_key, 0);
// enact transfer
if !(tx.amount <= from_balance) {
return Err(ApplyError::CantPay)
}
let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF);
let to_balance: u64 = storage::get_or(&to_balance_key, 0);
storage::put(&from_balance_key, &(from_balance - tx.amount));
storage::put(&to_balance_key, &(to_balance + tx.amount));
let to_balance: u64 = storage::hashed::get_or(&blake2_256, &to_balance_key, 0);
storage::hashed::put(&blake2_256, &from_balance_key, &(from_balance - tx.amount));
storage::hashed::put(&blake2_256, &to_balance_key, &(to_balance + tx.amount));
Ok(ApplyOutcome::Success)
}
@@ -295,7 +295,7 @@ fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
mod tests {
use super::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use runtime_io::{with_externalities, twox_128, blake2_256, TestExternalities};
use parity_codec::{Joiner, KeyedVec};
use substrate_test_client::{AuthorityKeyring, AccountKeyring};
use crate::{Header, Transfer};
@@ -313,7 +313,7 @@ mod tests {
twox_128(&0u32.to_keyed_vec(well_known_keys::AUTHORITY_PREFIX)).to_vec() => AuthorityKeyring::Alice.to_raw_public().to_vec(),
twox_128(&1u32.to_keyed_vec(well_known_keys::AUTHORITY_PREFIX)).to_vec() => AuthorityKeyring::Bob.to_raw_public().to_vec(),
twox_128(&2u32.to_keyed_vec(well_known_keys::AUTHORITY_PREFIX)).to_vec() => AuthorityKeyring::Charlie.to_raw_public().to_vec(),
twox_128(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
])
}
+2 -4
View File
@@ -2223,7 +2223,6 @@ version = "1.0.0"
dependencies = [
"parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 1.0.0",
"substrate-primitives 1.0.0",
]
@@ -2238,7 +2237,6 @@ dependencies = [
"parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"paste 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 1.0.0",
"sr-primitives 1.0.0",
"sr-std 1.0.0",
@@ -2577,7 +2575,7 @@ dependencies = [
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slog-json 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2595,7 +2593,6 @@ dependencies = [
"memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 1.0.0",
"sr-primitives 1.0.0",
"sr-std 1.0.0",
@@ -2628,6 +2625,7 @@ dependencies = [
"memory-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 1.0.0",
"substrate-primitives 1.0.0",
"trie-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"trie-root 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
]