-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Integrate Wasmer into Substrate sandbox environment #5920
Conversation
It will be removed before merge. It is so that the benchbot uses the wasmer sandbox.
/benchmark runtime pallet pallet_contracts |
Benchmark Runtime Pallet for branch "wasmtime-sandbox" with command cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic="*" --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs Results
|
…path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs
…anifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs" This reverts commit d713590.
Even though we won't be using this in production soon it will be very helpful to have this merged for benchmarking and experimentation. @pepyakin please consider removing your "change requested". |
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.
LGTM.
If it can share any cache with other jobs, it would make sense to merge them.
Also, for the upcoming optimizations: can we run this test only if certain files were changed?
Adding the
In the future I hope that we can get rid of the |
bot merge |
Waiting for commit status. |
bot merge |
Trying merge. |
TL;DR
Currently Substrate can execute Wasm in two ways: either by interpreting it using Wasmi or by compiling Wasm bytecode into native instructions using Wasmtime. The latter is faster to execute, but is only supported for trusted code, i.e. runtime. For security reasons smart contracts are still interpreted.
In order to execute smart contracts safely we need to have an environment that is suitable to execute untrusted code and resistant to JIT bomb attacks. Such an environment is provided by the Wasmer Singlepass compiler that is advertised as being designed specifically with blockchain in mind:
This PR integrates Wasmer Singlepass into Substrate sandbox environment.
API conundrum
Wasmer has rather simple API so its integration was straightforward. However, Substrate sandbox API was designed and implemented with Wasmi in mind, which is essentially single-threaded. This is incompatible with that of Wasmer and really annoys Rust borrowck.
Namely, Wasmer forces host function closures to be
Fn + Send + Sync
. Yet,supervisor_externals
is passed as&mut
dispatch_thunk
is neitherSend
norSync
Both of those entities are used by host function closures to dispatch the call.
What was done
The solution to both issues is scoped thread local storage. Since current execution model is single-threaded anyway, introducing TLS does not violate invariants and calms down the borrow checker. This was done by carefully wrapping objects in a scoped TLS and making state objects clonable by wrapping inner data with
Rc
.Also, please note that there are a couple of
unsafe
one-liners to access construct a slice that views into wasmer memory. The context is very limited and single-threaded, so I suppose this is safe.Memory API redesign
To eliminate potential aliasing issues when accessing memory, the sandbox memory access API was redesigned. It is now use external buffers. Also the
MemoryTransfer
trait was introduced to abstract over backends.How it was tested
I successfully tested it on the following ink! examples:
flipper
incrementer
erc20
erc721
dns
delegator
Considering sufficiently complex logic of example contracts (such as hash table manipulation and cross module operation) I cautiously state that the code is working as expected and is ready for extensive testing in a real world scenarios.
When testing yourself don't forget to pass
--features wastime,wasmer-sandbox
tocargo build
and--execution Wasm --wasm-execution Compiled
when running a node.Further work
Current implementation hard-codes sandbox execution mode to match that of runtime and is gated by
wasmer-sandbox
feature flag. So, if control flow passing from runtime being interpreted by Wasmi, then sandboxed module will be interpreted by Wasmi as well. And vice versa, if runtime is executed by Wasmtime, then sandbox will be executed by Wasmer. Ifwasmer-sandbox
feature is disabled, sandbox will always be executed via wasmi, as it was before.In some cases there might be an intention to force execution mode of a sandboxed code, say, for security or performance reasons. In that case command line parameter is needed. I tried adding one, but unfortunately this ended up being a non trivial task, since currently there is no way to pass configuration options to the sandbox environment which is instantiated on a per call basis. Of course, this is possible but would require substantial refactoring, which probably should be done in a separate PR.
Also we should refactor sandbox logic to eliminate copy-pasta in Value conversion. Yet, I'd really like to do that in a separate PR.
Note to reviewers
This PR affects execution of untrusted code and contains
unsafe
one-liners. Please review very carefully. I may suggest starting fromcommon/src/sandbox.rs
andcommon/src/util.rs
.