Skip to content

Add cli option --wasmtime-precompiled and subcommand precompile-wasm#1641

Open
librelois wants to merge 29 commits into
paritytech:masterfrom
moonbeam-foundation:elois-wasmtime-precompiled
Open

Add cli option --wasmtime-precompiled and subcommand precompile-wasm#1641
librelois wants to merge 29 commits into
paritytech:masterfrom
moonbeam-foundation:elois-wasmtime-precompiled

Conversation

@librelois
Copy link
Copy Markdown
Contributor

@librelois librelois commented Sep 20, 2023

Description

What does this PR do?

This PR adds the possibility of starting the node with a precompiled WASM runtime.
This eliminates the need to compile the runtime at startup, making it possible to launch the node much more quickly.

Why are these changes needed?

A practical example of where this feature is useful is when you need to run a large number of end2end tests quickly: you precompile the runtime once in the same environment, then use the precompiled artifact every time you need to spawn the node (potentially hundreds of times).

How were these changes implemented and what do they affect?

As a security measure, a hash of the wasmtime configuration is inserted into the precompiled artifact and checked by wasmtime at startup, to ensure that, to ensure that the precompiled artifact has been generated under the same conditions.

This check is delegated to wasmtime directly thanks to the module_version function.

How to use

  1. Generate the precompiled artifact with the subcommand precompile-wasm:
precompile-wasm --chain <your-chain-spec> path/to/artifacts/folder

This will precompile the on-chain wasm at the last finalized block and save the precompiled artifact in path/to/artifacts/folder.

  1. Use the precompiled artifact with the cli option --wasmtime-precompiled path/to/artifacts/folder

@librelois librelois marked this pull request as ready for review September 21, 2023 08:22
@librelois librelois requested a review from koute as a code owner September 21, 2023 08:22
@koute
Copy link
Copy Markdown
Contributor

koute commented Sep 21, 2023

What's your motivation to add this? Is this only supposed to be used for tests?

I'd be fine to have this to speed up tests, but I'm not convinced we should support this for running normal nodes, as it would add extra complexity and be yet another place where something can go wrong for not much benefit.

There's also the issue that for PVFs in Polkadot we already have caching implemented, so this would essentially duplicate that functionality in an incompatible way. (Although the requirements are different, as for PVFs we need sandboxing, so it's not really obvious how you would unify those.)

How about we fundamentally simplify this and do something like this instead?

  • Remove the extra CLI arguments/subcommands you've added.
  • Remove all other changes outside of the sc-executor-wasmtime.
  • In sc-executor-wasmtime check if a special environment variable is set, let's say, WASMTIME_PRECOMPILED_CACHE_DIR or something like that.
  • If it is set then the executor would automatically and transparently use that directory as a cache.
  • As the key for the cache use BLAKE3 hash of the semantics struct, the raw .wasm blob and of the current executable (std::fs::read the std::env::current_exe); unlike CARGO_PKG_VERSION this would guarantee that the executable has to be bit-exact for cache to be used.
  • If there are errors anywhere in this process they should be warn! printed and ignored (that is, the node should compile the module normally)

This would be, I think, simpler, need less code, be more maintainable, be more foolproof, and be easier to use.

@bkchr
Copy link
Copy Markdown
Member

bkchr commented Sep 25, 2023

Yeah I was proposing to go this route. They are running a lot of tests in their CI and that got really slow with the removal of wasmi as they will now need to recompile it all the time.

I'm not sure that a env variable is the way to go here either. No one will find out about this env variable besides the three of us :P I would propose that you extend WasmExecutorBuilder to add a precompiled_path or whatever. The CLI stuff is really not required here in our repo. You can just configure the executor as you like in moonbeam and use a CLI argument or a env variable or whatever. I didn't check your pr in detail, but if you need functionality exposed to precompile a binary, it is also fine to expose this function to be used in moonbeam.

@koute
Copy link
Copy Markdown
Contributor

koute commented Sep 25, 2023

I'm not sure that a env variable is the way to go here either. No one will find out about this env variable besides the three of us :P I would propose that you extend WasmExecutorBuilder to add a precompiled_path or whatever.

Yeah, that also sounds good to me.

@crystalin
Copy link
Copy Markdown
Contributor

The command should output the name of the file also IMO

@crystalin
Copy link
Copy Markdown
Contributor

The expected wasm and the generated one don't have the same hash:

2023-09-28 09:38:09 artifact_version: 0x3bb7567dc2e545fc246e248f2d0d3e3da0ba14c65bb01948001586448da83e56
2023-09-28 09:38:09 wasm file_name: precompiled_wasm_0x0x91b7bcbc9e993dded10ed74930ca3f2805a470980b47889f4d5cff4aab63e952

@crystalin
Copy link
Copy Markdown
Contributor

crystalin commented Sep 28, 2023

The wasm is actually loaded at multiple places with different hashes:
In resolve_state_version_from_wasm where it takes the :code value but uses a special hash (Which I think is due to not having storage yet as we are building genesis)

The other one is initiated by grandpa where it retrieve the runtime and hash in the call_executor, computed in the state-machine using the storage_hash

This mean that for the same code, we have 2 different hashes and so we won't load the precompiled wasm

@paritytech-cicd-pr
Copy link
Copy Markdown

The CI pipeline was cancelled due to failure one of the required jobs.
Job name: cargo-clippy
Logs: https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/3840831

@crystalin
Copy link
Copy Markdown
Contributor

Something to verify. I replaced the hasher of the genesis code to use the Blake2Hasher to be compatible with the precompiled and on-chain wasm calculated hash:
https://github.com/paritytech/polkadot-sdk/pull/1641/files#diff-680ae468913e48e81b977d13c888b391a21b3254e62ed1cc40bfb0e7310b7389R51-R52

After that change and marking allow_missing_func_imports to false, I was able on Moonbeam to boot the node without any compilation.

Something to note also, by default the "precompile-wasm" command will use the default base-path if none is provided, making this command:
../target/release/moonbeam precompile-wasm -lwasmtime-runtime --chain moonbase-dev wasm not use the moonbase-dev chain spec code but the one from your $HOME/.local/share/moonbeam/chains/moonbase_dev which might not be the same

@crystalin
Copy link
Copy Markdown
Contributor

The precompile-wasm actually store the chain data in the base-path, like when launching a network. I don't think we should include that

@librelois
Copy link
Copy Markdown
Contributor Author

The CLI stuff is really not required here in our repo. You can just configure the executor as you like in moonbeam and use a CLI argument or a env variable

This isn't a problem specific to moonbeam, it concerns all substrate chains that need to spawn a lot of nodes for their end2end tests. For example, Tanssi also has many end2end typescrit tests.

The precompile-wasm actually store the chain data in the base-path, like when launching a network. I don't think we should include that

This is linked to the fact that the command doesn't know if you want to retrieve the runtime code from the DB or not, so it has to be initialized. Adding an explicit flag to retrieve from chain spec should solve the problem.

@girazoki
Copy link
Copy Markdown
Contributor

Any update on this? as @librelois this would be heplful for other ecosystem projects as well (like Tanssi in this case)

tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Feb 23, 2024
This cherry-pick should match this pull request:

paritytech#1641
@RomarQ RomarQ requested review from a team as code owners December 5, 2024 07:17
tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Sep 17, 2025
This cherry-pick should match this pull request:

paritytech#1641
tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Sep 17, 2025
This cherry-pick should match this pull request:

paritytech#1641
tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Sep 18, 2025
This cherry-pick should match this pull request:

paritytech#1641
tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Jan 15, 2026
This cherry-pick should match this pull request:

paritytech#1641
tmpolaczyk pushed a commit to moondance-labs/polkadot-sdk that referenced this pull request Jan 15, 2026
This cherry-pick should match this pull request:

paritytech#1641
manuelmauro added a commit to moonbeam-foundation/polkadot-sdk that referenced this pull request May 8, 2026
Introduces a new subcommand that precompiles a runtime's WASM blob and
caches the result on disk, so subsequent node startups skip the JIT
compilation step entirely.

Moonbeam's CI runs Moonwall integration tests that spawn the node many
times per pipeline. Without precompilation, each spawn pays the WASM
compile cost (several seconds) and the cumulative wall-clock time
becomes the dominant factor in test duration. Precompiling once and
reusing the cached blob across all spawns reduces total CI time
significantly on this workload.

Adapted to stable2603's
`BackendRuntimeCode::new(state, TryPendingCode::No)` signature change.

Upstream: paritytech#1641 (not merged).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants