Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 2722c58

Browse files
authored
Fix runtime version cache (#586)
- Use blake2 instead of xxhash for guaranteed safety - Simplify by caching the version rather than the compatibility - Additional logging
1 parent fa1392a commit 2722c58

File tree

2 files changed

+40
-41
lines changed

2 files changed

+40
-41
lines changed

substrate/codec/derive/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
//! Derives serialization and deserialization codec for complex structs for simple marshalling.
1818
1919
#![cfg_attr(not(feature = "std"), no_std)]
20-
#![cfg_attr(not(feature = "std"), feature(alloc))]
20+
//#![cfg_attr(not(feature = "std"), feature(alloc))]
2121

2222
extern crate proc_macro;
2323
extern crate proc_macro2;

substrate/executor/src/native_executor.rs

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,20 @@ use wasmi::Module as WasmModule;
2121
use runtime_version::RuntimeVersion;
2222
use std::collections::HashMap;
2323
use codec::Decode;
24-
use twox_hash::XxHash;
25-
use std::hash::Hasher;
24+
use primitives::hashing::blake2_256;
2625
use parking_lot::{Mutex, MutexGuard};
2726
use RuntimeInfo;
2827
use primitives::KeccakHasher;
2928

3029
// For the internal Runtime Cache:
3130
// Is it compatible enough to run this natively or do we need to fall back on the WasmModule
3231

33-
enum Compatibility {
34-
InvalidVersion(WasmModule),
35-
IsCompatible(RuntimeVersion, WasmModule),
36-
NotCompatible(RuntimeVersion, WasmModule)
32+
enum RuntimePreproc {
33+
InvalidCode,
34+
ValidCode(WasmModule, Option<RuntimeVersion>),
3735
}
3836

39-
unsafe impl Send for Compatibility {}
40-
41-
type CacheType = HashMap<u64, Compatibility>;
37+
type CacheType = HashMap<[u8; 32], RuntimePreproc>;
4238

4339
lazy_static! {
4440
static ref RUNTIMES_CACHE: Mutex<CacheType> = Mutex::new(HashMap::new());
@@ -48,10 +44,8 @@ lazy_static! {
4844
// it is asserted that part of the audit process that any potential on-chain code change
4945
// will have done is to ensure that the two-x hash is different to that of any other
5046
// :code value from the same chain
51-
fn gen_cache_key(code: &[u8]) -> u64 {
52-
let mut h = XxHash::with_seed(0);
53-
h.write(code);
54-
h.finish()
47+
fn gen_cache_key(code: &[u8]) -> [u8; 32] {
48+
blake2_256(code)
5549
}
5650

5751
/// fetch a runtime version from the cache or if there is no cached version yet, create
@@ -61,25 +55,22 @@ fn fetch_cached_runtime_version<'a, E: Externalities<KeccakHasher>>(
6155
wasm_executor: &WasmExecutor,
6256
cache: &'a mut MutexGuard<CacheType>,
6357
ext: &mut E,
64-
code: &[u8],
65-
ref_version: RuntimeVersion
66-
) -> &'a Compatibility {
67-
cache.entry(gen_cache_key(code))
68-
.or_insert_with(|| {
69-
let module = WasmModule::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
70-
let version = wasm_executor.call_in_wasm_module(ext, &module, "version", &[]).ok()
71-
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()));
72-
73-
if let Some(v) = version {
74-
if ref_version.can_call_with(&v) {
75-
Compatibility::IsCompatible(v, module)
76-
} else {
77-
Compatibility::NotCompatible(v, module)
78-
}
79-
} else {
80-
Compatibility::InvalidVersion(module)
58+
code: &[u8]
59+
) -> Result<(&'a WasmModule, &'a Option<RuntimeVersion>)> {
60+
let maybe_runtime_preproc = cache.entry(gen_cache_key(code))
61+
.or_insert_with(|| match WasmModule::from_buffer(code) {
62+
Ok(module) => {
63+
let version = wasm_executor.call_in_wasm_module(ext, &module, "version", &[])
64+
.ok()
65+
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()));
66+
RuntimePreproc::ValidCode(module, version)
8167
}
82-
})
68+
Err(_) => RuntimePreproc::InvalidCode,
69+
});
70+
match maybe_runtime_preproc {
71+
RuntimePreproc::InvalidCode => Err(ErrorKind::InvalidCode(code.into()).into()),
72+
RuntimePreproc::ValidCode(m, v) => Ok((m, v)),
73+
}
8374
}
8475

8576
fn safe_call<F, U>(f: F) -> Result<U>
@@ -157,11 +148,7 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
157148
ext: &mut E,
158149
code: &[u8],
159150
) -> Option<RuntimeVersion> {
160-
let mut c = RUNTIMES_CACHE.lock();
161-
match fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION) {
162-
Compatibility::IsCompatible(v, _) | Compatibility::NotCompatible(v, _) => Some(v.clone()),
163-
Compatibility::InvalidVersion(_m) => None
164-
}
151+
fetch_cached_runtime_version(&self.fallback, &mut RUNTIMES_CACHE.lock(), ext, code).ok()?.1.clone()
165152
}
166153
}
167154

@@ -177,10 +164,22 @@ impl<D: NativeExecutionDispatch> CodeExecutor<KeccakHasher> for NativeExecutor<D
177164
use_native: bool,
178165
) -> (Result<Vec<u8>>, bool) {
179166
let mut c = RUNTIMES_CACHE.lock();
180-
match (use_native, fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION)) {
181-
(_, Compatibility::NotCompatible(_, m)) | (_, Compatibility::InvalidVersion(m)) | (false, Compatibility::IsCompatible(_, m)) =>
182-
(self.fallback.call_in_wasm_module(ext, m, method, data), false),
183-
_ => (D::dispatch(ext, method, data), true),
167+
let (module, onchain_version) = match fetch_cached_runtime_version(&self.fallback, &mut c, ext, code) {
168+
Ok((module, onchain_version)) => (module, onchain_version),
169+
Err(_) => return (Err(ErrorKind::InvalidCode(code.into()).into()), false),
170+
};
171+
match (use_native, onchain_version.as_ref().map_or(false, |v| v.can_call_with(&D::VERSION))) {
172+
(_, false) => {
173+
trace!(target: "executor", "Request for native execution failed (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"<None>".into(), |v| format!("{}", v)));
174+
(self.fallback.call_in_wasm_module(ext, module, method, data), false)
175+
}
176+
(false, _) => {
177+
(self.fallback.call_in_wasm_module(ext, module, method, data), false)
178+
}
179+
_ => {
180+
trace!(target: "executor", "Request for native execution succeeded (native: {}, chain: {})", D::VERSION, onchain_version.as_ref().map_or_else(||"<None>".into(), |v| format!("{}", v)));
181+
(D::dispatch(ext, method, data), true)
182+
}
184183
}
185184
}
186185
}

0 commit comments

Comments
 (0)