Remove substrate-wasm-builder

By defining our own build script, we gain complete clarity and control over how
the WASM is built. This also removes the need to patch the upstream due to it
allowing pollution of the environment variables from the host.

Notable appreciation is given to
https://github.com/rust-lang/rust/issues/145491 for identifying an issue
encountered here, with the associated PR clarifying the necessary flags for the
linker to fix this.
This commit is contained in:
Luke Parker
2025-12-04 20:07:52 -05:00
parent 36ac9c56a4
commit b791256648
7 changed files with 104 additions and 90 deletions

72
Cargo.lock generated
View File

@@ -1445,38 +1445,6 @@ dependencies = [
name = "c-kzg"
version = "2.99.99"
[[package]]
name = "camino"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
dependencies = [
"serde_core",
]
[[package]]
name = "cargo-platform"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror 2.0.17",
]
[[package]]
name = "cc"
version = "1.2.48"
@@ -7080,15 +7048,6 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "sc-allocator"
version = "34.0.0"
@@ -8096,10 +8055,6 @@ name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serai-abi"
@@ -9033,7 +8988,6 @@ dependencies = [
"sp-timestamp",
"sp-transaction-pool",
"sp-version",
"substrate-wasm-builder",
]
[[package]]
@@ -10117,22 +10071,6 @@ dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "substrate-wasm-builder"
version = "29.0.0"
source = "git+https://github.com/serai-dex/patch-polkadot-sdk#0cd66846e58f167b09b48a77c3f70bf7b2b67000"
dependencies = [
"cargo_metadata",
"console",
"jobserver",
"parity-wasm",
"shlex",
"strum 0.27.2",
"tempfile",
"toml 0.9.8",
"walkdir",
]
[[package]]
name = "subtle"
version = "2.6.1"
@@ -10874,16 +10812,6 @@ dependencies = [
"libc",
]
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.1"

View File

@@ -37,6 +37,6 @@ COPY --from=builder /bin/busybox /bin/
ENV LD_LIBRARY_PATH=/lib/
ENV PATH=/bin
# Copy the artifact itself
COPY --from=builder /serai/target/release/wbuild/serai-runtime/serai_runtime.wasm /serai.wasm
COPY --from=builder /serai/target/release/serai_runtime.wasm /serai.wasm
# By default, copy the artifact to `/volume`, presumably a provided volume
CMD ["busybox", "cp", "/serai.wasm", "/volume/serai.wasm"]

View File

@@ -74,7 +74,7 @@ fn wasm_binary(dev: bool) -> Vec<u8> {
}
log::info!("using built-in wasm");
serai_runtime::WASM_BINARY.ok_or("compiled in wasm not available").unwrap().to_vec()
serai_runtime::WASM.to_vec()
}
fn devnet_genesis(validators: &[&'static str], endowed_accounts: Vec<Public>) -> GenesisConfig {

View File

@@ -6,7 +6,7 @@ use sp_timestamp::InherentDataProvider as TimestampInherent;
use sp_consensus_babe::{SlotDuration, inherents::InherentDataProvider as BabeInherent};
use sp_io::SubstrateHostFunctions;
use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, WasmExecutor};
use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, HeapAllocStrategy, WasmExecutor};
use sc_network::{Event, NetworkEventStream, NetworkBackend};
use sc_service::{error::Error as ServiceError, Configuration, TaskManager, TFullClient};
@@ -99,14 +99,13 @@ pub fn new_partial(
})
.transpose()?;
#[allow(deprecated)]
let executor = Executor::new(
config.executor.wasm_method,
config.executor.default_heap_pages,
config.executor.max_runtime_instances,
None,
config.executor.runtime_cache_size,
);
let executor = Executor::builder()
.with_execution_method(config.executor.wasm_method)
.with_onchain_heap_alloc_strategy(HeapAllocStrategy::Dynamic { maximum_pages: None })
.with_offchain_heap_alloc_strategy(HeapAllocStrategy::Dynamic { maximum_pages: None })
.with_max_runtime_instances(config.executor.max_runtime_instances)
.with_runtime_cache_size(config.executor.runtime_cache_size)
.build();
let (client, backend, keystore_container, task_manager) = {
let telemetry = telemetry.as_ref().map(|(_, telemetry)| telemetry.handle());

View File

@@ -70,9 +70,6 @@ serai-dex-pallet = { path = "../dex", default-features = false }
serai-genesis-liquidity-pallet = { path = "../genesis-liquidity", default-features = false }
serai-in-instructions-pallet = { path = "../in-instructions", default-features = false }
[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk" }
[features]
std = [
"scale/std",

View File

@@ -1,4 +1,90 @@
fn main() {
#[cfg(not(target_family = "wasm"))]
substrate_wasm_builder::WasmBuilder::build_using_defaults();
use std::{path::PathBuf, fs, env, process::Command};
// Prevent recursing infinitely
if env::var("TARGET").unwrap() == "wasm32v1-none" {
return;
}
// https://github.com/rust-lang/rust/issues/145491
const ONE_45491: &str = "-C link-arg=--mllvm=-mcpu=mvp,--mllvm=-mattr=+mutable-globals";
const WASM: &str = "-C link-arg=--export-table";
const REQUIRED_BY_SUBSTRATE: &str = "--cfg substrate_runtime -C link-arg=--import-memory";
const SAFETY: &str = "-C overflow-checks=true -C panic=abort";
const COMPILATION: &str =
"-C symbol-mangling-version=v0 -C embed-bitcode=false -C linker-plugin-lto=true";
let profile = env::var("PROFILE").unwrap();
let release = profile == "release";
let rustflags = format!("{ONE_45491} {WASM} {REQUIRED_BY_SUBSTRATE} {SAFETY} {COMPILATION}");
let rustflags = if release {
format!("{rustflags} -C codegen-units=1 -C strip=symbols -C debug-assertions=false")
} else {
rustflags
};
let target_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("target");
let cargo_command = || {
let cargo = env::var("CARGO").unwrap();
let mut command = Command::new(&cargo);
command
.current_dir(env::var("CARGO_MANIFEST_DIR").unwrap())
.env_clear()
.env("PATH", env::var("PATH").unwrap())
.env("CARGO", cargo)
.env("RUSTC", env::var("RUSTC").unwrap())
.env("RUSTFLAGS", &rustflags)
.env("CARGO_TARGET_DIR", &target_dir);
command
};
let workspace = {
let workspace = cargo_command()
.arg("locate-project")
.arg("--workspace")
.arg("--message-format")
.arg("plain")
.output()
.unwrap();
assert!(workspace.status.success());
let mut workspace = PathBuf::from(String::from_utf8(workspace.stdout).unwrap().trim());
assert_eq!(workspace.file_name().unwrap(), "Cargo.toml");
assert!(workspace.pop());
workspace
};
// Re-run anytime the workspace changes
// TODO: Re-run anytime `Cargo.lock` or specifically the `src` folders change
println!("cargo::rerun-if-changed={}", workspace.display());
let mut command = cargo_command();
command
.arg("rustc")
.arg("--package")
.arg(env::var("CARGO_PKG_NAME").unwrap())
.arg("--target")
.arg("wasm32v1-none")
.arg("--crate-type")
.arg("cdylib")
.arg("--no-default-features");
if release {
command.arg("--release");
}
assert!(command.status().unwrap().success());
// Place the resulting WASM blob into the parent `target` directory
{
let wasm_file = env::var("CARGO_PKG_NAME").unwrap().replace('-', "_") + ".wasm";
let src_file = target_dir.join("wasm32v1-none").join(&profile).join(&wasm_file);
let dst_file = {
let mut dst_dir = workspace.clone();
// e.g. workspace/target/debug
dst_dir.extend(["target", &profile]);
let _ = fs::create_dir_all(&dst_dir);
// e.g. workspace/target/debug/serai_runtime.wasm
dst_dir.join(&wasm_file)
};
fs::copy(&src_file, &dst_file).unwrap();
}
}

View File

@@ -20,5 +20,9 @@ mod std_runtime_api;
pub use std_runtime_api::RuntimeApi;
// If this isn't WASM, regardless of what it is, we include the WASM blob from the build script
#[cfg(not(target_family = "wasm"))]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
pub const WASM: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/target/wasm32v1-none/debug/serai_runtime.wasm"));
#[cfg(all(not(target_family = "wasm"), not(debug_assertions)))]
pub const WASM: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/target/wasm32v1-none/release/serai_runtime.wasm"));