-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Implement runtime version checks in set_code
#4548
Conversation
Check that the new runtime code given to `set_code` fullfills some requirements: - `spec_name` matches - `spec_version` does not decreases - `impl_version` does not decreases - Either `spec_version` and `impl_version` increase
looks like a promising direction. |
client/src/client.rs
Outdated
Block: BlockT, | ||
{ | ||
type Type = Block; | ||
} | ||
|
||
impl<B, E, Block, RA> Client<B, E, Block, RA> where | ||
B: backend::Backend<Block>, | ||
E: CallExecutor<Block>, | ||
E: CallExecutor<Block> + 'static, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we just require on CallExecutor
trait that it's 'static
? This repetition is going to be pretty cumbersome.
/// | ||
/// Returns the SCALE encoded runtime version and `None` if the call failed. | ||
fn runtime_version(&mut self, wasm: &[u8]) -> Option<Vec<u8>> { | ||
// Create some dummy externalities, `Core_version` should not write data anyway. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just error out if it does? Just to make sure that even if set_code
fails due to checks there are no side-effects of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What kind of side effects? Even if the call would write something, it would not leak into the Externalities of the actual block. If that was your question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I meant, I wanted to be sure that the two contexts do not share anything.
primitives/sandbox/with_std.rs
Outdated
println!("{} {}", module_name, field_name); | ||
let lol = ExternVal::HostFunc(HostFuncIndex(100)); | ||
let externval = self.map.get(&key).unwrap_or_else(|| { | ||
// wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's going on here 🗡️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I had forgot some debugging stuff 😅 Good that you found it!
primitives/sandbox/with_std.rs
Outdated
@@ -262,10 +266,9 @@ impl<T> Instance<T> { | |||
env_def_builder: &EnvironmentDefinitionBuilder<T>, | |||
state: &mut T, | |||
) -> Result<Instance<T>, Error> { | |||
let module = Module::from_buffer(code).map_err(|_| Error::Module)?; | |||
let module = Module::from_buffer(code).map_err(|e| { println!("{:?}", e); Error::Module})?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bunch of stray println
s in this file.
) -> Self { | ||
extensions.register(CallInWasmExt::new(exec.clone())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it's now available to every runtime call I guess, what are the consequences here? Besides using it being super slow, as I guess a new wasm env has to be initiated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why super slow? The wasm blob needs to be initialized when we want to call some function in it, but there is nothing we can optimize (as we don't know the blob in front of).
We need the extension while building/executing a block. But there is also no direct method that you can call from the runtime, besides the one that only can call Core_version
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I know there is no other way to call Core_version
from a different runtime blob, but afair when talking with Sergei he was saying that initializing wasm runtime (alloc memory, load module, etc) takes quite some time, hence I've mentioned that it's most likely going to be way slower than any other operation or host function we have, so we need to be careful to avoid using it too often.
I was wondering if there are any other (more severe) consequences of exposing this kind of function to the runtime - i.e. if it can be somehow abused.
. But there is also no direct method that you can call from the runtime, besides the one that only can call Core_version.
That's good to know, so it means that it's not easy to shoot yourself in a foot if you are a runtime developer, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is probably no easy operation, but better to include less transactions than to stop the network again ;) If something does not work or for whatever reason we need to put a blob in without any checks, I added set_code_without_checks
. This will just do it the old way, so we are sure that we can put a blob in :)
Yeah, the runtime developer can not access the CallInRuntimeExt
in any other way. It is not exposed to the runtime. The developer can just call misc::runtime_version
that uses this stuff internally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, that's fair, but I would really add extra docs to make sure it's not being called as it's a possible DoS vector. Imagine if we do that check whenever someone proposes a runtime, then people can craft arbitrary runtimes and do arbitrary operations within Core_version
and it wouldn't be priced correctly. So we should only make sure to run in during set_code
, not during any preliminary checks (that accepts unreviewed user's input).
function: &str, | ||
call_data: &[u8], | ||
execution_method: WasmExecutionMethod, | ||
ext: &mut E, | ||
ext: &mut dyn Externalities, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all the storage accesses (and there are a lot) go through externalities. does this really have an insubstantial effect on performance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We store the Externalities as trait pointer in the environmental anyway. The compiler can not optimize this anywax.
I added some temporary execution time measuring into |
will need resolving |
Done |
Check that the new runtime code given to
set_code
fullfills somerequirements:
spec_name
matchesspec_version
does not decreasesimpl_version
does not decreasesspec_version
orimpl_version
increases