From ab5c0d6f66a7dec10fbca320a73fa0253f11dd8d Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:45:23 +0100 Subject: [PATCH 01/23] revive: bump polkavm --- Cargo.lock | 40 +++++++++++++++++++++---------- substrate/frame/revive/Cargo.toml | 4 ++-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d42e29dd885d0..d3a7bc2042d9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13344,8 +13344,8 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "paste", - "polkavm 0.29.1", - "polkavm-common 0.29.0", + "polkavm 0.30.0", + "polkavm-common 0.30.0", "pretty_assertions", "proptest", "rand 0.8.5", @@ -15089,6 +15089,12 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "picosimd" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af35c838647fef3d6d052e27006ef88ea162336eee33063c50a63f163c18cdeb" + [[package]] name = "pin-project" version = "1.1.10" @@ -17472,15 +17478,16 @@ dependencies = [ [[package]] name = "polkavm" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c8211d36125b6cc451b3cbc46b8ee27fefb54521b67f43c8630bd1afbd44d4" +checksum = "4323d016144b2852da47cee55ca5fc33dfe7517be1f52395759f247ecc5695f6" dependencies = [ "libc", "log", - "polkavm-assembler 0.29.0", - "polkavm-common 0.29.0", - "polkavm-linux-raw 0.29.0", + "picosimd", + "polkavm-assembler 0.30.0", + "polkavm-common 0.30.0", + "polkavm-linux-raw 0.30.0", ] [[package]] @@ -17494,9 +17501,9 @@ dependencies = [ [[package]] name = "polkavm-assembler" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914aacebfbc22da7772f5ecb6f79b39901dc4061121598bd4383a590a7506ebb" +checksum = "b3a873fa7ace058d6507debf5fccb1d06bd3279f5b35dbaf70dc7fe94a6c415c" dependencies = [ "log", ] @@ -17522,10 +17529,17 @@ name = "polkavm-common" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f634b46a6a47a5de381f56d1d8cced9f8640d063b2b1a44b0da6dbef91bbd400" + +[[package]] +name = "polkavm-common" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed1b408db93d4f49f5c651a7844682b9d7a561827b4dc6202c10356076c055c9" dependencies = [ "blake3", "log", - "polkavm-assembler 0.29.0", + "picosimd", + "polkavm-assembler 0.30.0", ] [[package]] @@ -17661,9 +17675,9 @@ checksum = "28919f542476f4158cc71e6c072b1051f38f4b514253594ac3ad80e3c0211fc8" [[package]] name = "polkavm-linux-raw" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751fbbcf86635834dd9a700039c74ce8c7871b317acc84582d9667dad2ed9848" +checksum = "604b23cdb201979304449f53d21bfd5fb1724c03e3ea889067c9a3bf7ae33862" [[package]] name = "polling" @@ -18119,7 +18133,7 @@ checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "once_cell", diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index d51d6db753546..ce8f01660c632 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -34,8 +34,8 @@ num-bigint = { workspace = true } num-integer = { workspace = true } num-traits = { workspace = true } paste = { workspace = true } -polkavm = { version = "0.29.1", default-features = false } -polkavm-common = { version = "0.29.0", default-features = false, features = ["alloc"] } +polkavm = { version = "0.30.0", default-features = false } +polkavm-common = { version = "0.30.0", default-features = false, features = ["alloc"] } rand = { workspace = true, optional = true } rand_pcg = { workspace = true, optional = true } revm = { workspace = true } From 56d2af401d26aaa257ac6001030414d8f569c978 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:46:20 +0100 Subject: [PATCH 02/23] revive: align to updated polkavm --- substrate/frame/revive/src/limits.rs | 3 +-- substrate/frame/revive/src/vm/pvm/env.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index 68108846b5856..b74bc461059d9 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -138,7 +138,6 @@ pub mod code { pvm_blob: Vec, available_syscalls: &[&[u8]], ) -> Result, DispatchError> { - use polkavm::program::ISA64_V1 as ISA; use polkavm_common::program::EstimateInterpreterMemoryUsageArgs; let len: u64 = pvm_blob.len() as u64; @@ -190,7 +189,7 @@ pub mod code { let mut block_size: u32 = 0; let mut basic_block_count: u32 = 0; let mut instruction_count: u32 = 0; - for inst in program.instructions(ISA) { + for inst in program.instructions() { use polkavm::program::Instruction; block_size += 1; instruction_count += 1; diff --git a/substrate/frame/revive/src/vm/pvm/env.rs b/substrate/frame/revive/src/vm/pvm/env.rs index 6e491c26ae01e..a13e314215a45 100644 --- a/substrate/frame/revive/src/vm/pvm/env.rs +++ b/substrate/frame/revive/src/vm/pvm/env.rs @@ -62,7 +62,6 @@ impl ContractBlob { let mut module_config = polkavm::ModuleConfig::new(); module_config.set_page_size(limits::PAGE_SIZE); module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync)); - module_config.set_allow_sbrk(false); module_config.set_aux_data_size(aux_data_size); let module = polkavm::Module::new(&engine, &module_config, self.code.into()).map_err(|err| { From 53ec86b4fcc115e45bbff0e059d49305c273aac8 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:47:02 +0100 Subject: [PATCH 03/23] revive: disable polkavm logging --- substrate/frame/revive/src/vm/pvm/env.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/substrate/frame/revive/src/vm/pvm/env.rs b/substrate/frame/revive/src/vm/pvm/env.rs index a13e314215a45..7a5229f78ce57 100644 --- a/substrate/frame/revive/src/vm/pvm/env.rs +++ b/substrate/frame/revive/src/vm/pvm/env.rs @@ -47,6 +47,11 @@ impl ContractBlob { aux_data_size: u32, ) -> Result, ExecError> { let mut config = polkavm::Config::default(); + // Log filtering by level with log::enabled! returns always true, + // passing all logs through impacting performance \ + // (more details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774) + // Let's disable polkavm logging. + config.set_imperfect_logger_filtering_workaround(true); config.set_backend(Some(polkavm::BackendKind::Interpreter)); config.set_cache_enabled(false); #[cfg(feature = "std")] From 2deab4221f05f3818c8ba8f09d0b92d19ae03da4 Mon Sep 17 00:00:00 2001 From: "cmd[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:55:35 +0000 Subject: [PATCH 04/23] Update from github-actions[bot] running command 'prdoc --audience runtime_dev --bump minor' --- prdoc/pr_10385.prdoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 prdoc/pr_10385.prdoc diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc new file mode 100644 index 0000000000000..43d7d3837b342 --- /dev/null +++ b/prdoc/pr_10385.prdoc @@ -0,0 +1,13 @@ +title: Disable polkavm logging in `pallet-revive` +doc: +- audience: Runtime Dev + description: |- + This PR disables logging in `polkavm` in `pallet-revive`. + This is to make sure massive `polkavm` logging don't impact the overall perfomance, which might be manifested with eg. long block proposal time. + + details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774 + + The fix requires bumping `polkavm` to `0.30.0` which includes a method that allows to disable logging. +crates: +- name: pallet-revive + bump: minor From fa32f5973386b6170db7f5c24003931a45319ee0 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:30:01 +0100 Subject: [PATCH 05/23] revive: use instructions_with_isa --- substrate/frame/revive/src/limits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index b74bc461059d9..bcaf7fe0dcc6f 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -138,7 +138,7 @@ pub mod code { pvm_blob: Vec, available_syscalls: &[&[u8]], ) -> Result, DispatchError> { - use polkavm_common::program::EstimateInterpreterMemoryUsageArgs; + use polkavm_common::program::{EstimateInterpreterMemoryUsageArgs, ISA_Latest64 as ISA}; let len: u64 = pvm_blob.len() as u64; if len > crate::limits::code::BLOB_BYTES.into() { @@ -189,7 +189,7 @@ pub mod code { let mut block_size: u32 = 0; let mut basic_block_count: u32 = 0; let mut instruction_count: u32 = 0; - for inst in program.instructions() { + for inst in program.instructions_with_isa(ISA) { use polkavm::program::Instruction; block_size += 1; instruction_count += 1; From f1e9b3abdec5ae65a8cae36c9f79e5978cbbc768 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:31:50 +0100 Subject: [PATCH 06/23] revive: updates call_builder --- substrate/frame/revive/src/call_builder.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/substrate/frame/revive/src/call_builder.rs b/substrate/frame/revive/src/call_builder.rs index c6b6886b4e900..76dccd484b517 100644 --- a/substrate/frame/revive/src/call_builder.rs +++ b/substrate/frame/revive/src/call_builder.rs @@ -449,7 +449,11 @@ impl VmBinaryModule { } } text.push_str("ret\n"); - let code = polkavm_common::assembler::assemble(&text).unwrap(); + let code = polkavm_common::assembler::assemble( + Some(polkavm_common::program::InstructionSetKind::ReviveV1), + &text, + ) + .unwrap(); Self::new(code) } @@ -478,7 +482,11 @@ impl VmBinaryModule { ret " ); - let code = polkavm_common::assembler::assemble(&text).unwrap(); + let code = polkavm_common::assembler::assemble( + Some(polkavm_common::program::InstructionSetKind::ReviveV1), + &text, + ) + .unwrap(); Self::new(code) } From 42c84c1cd77f807d6c57c30ab96d0064e023bb07 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:59:38 +0100 Subject: [PATCH 07/23] bump polkavm globally --- Cargo.lock | 137 +++--------------- Cargo.toml | 6 +- substrate/frame/revive/fixtures/Cargo.toml | 2 +- substrate/frame/revive/fixtures/build.rs | 13 +- substrate/frame/revive/uapi/Cargo.toml | 2 +- substrate/utils/wasm-builder/src/lib.rs | 4 +- .../utils/wasm-builder/src/wasm_project.rs | 7 +- 7 files changed, 47 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3a7bc2042d9e..95c755d383785 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13344,7 +13344,7 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "paste", - "polkavm 0.30.0", + "polkavm", "polkavm-common 0.30.0", "pretty_assertions", "proptest", @@ -13424,7 +13424,7 @@ dependencies = [ "cargo_metadata", "hex", "pallet-revive-uapi", - "polkavm-linker 0.29.0", + "polkavm-linker", "serde_json", "sp-core 28.0.0", "sp-io", @@ -13450,7 +13450,7 @@ dependencies = [ "hex-literal", "pallet-revive-proc-macro", "parity-scale-codec", - "polkavm-derive 0.29.0", + "polkavm-derive 0.30.0", "scale-info", ] @@ -17463,19 +17463,6 @@ dependencies = [ "zombienet-sdk", ] -[[package]] -name = "polkavm" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa028f713d0613f0f08b8b3367402cb859218854f6b96fcbe39a501862894d6f" -dependencies = [ - "libc", - "log", - "polkavm-assembler 0.26.0", - "polkavm-common 0.26.0", - "polkavm-linux-raw 0.26.0", -] - [[package]] name = "polkavm" version = "0.30.0" @@ -17485,18 +17472,9 @@ dependencies = [ "libc", "log", "picosimd", - "polkavm-assembler 0.30.0", + "polkavm-assembler", "polkavm-common 0.30.0", - "polkavm-linux-raw 0.30.0", -] - -[[package]] -name = "polkavm-assembler" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4859a29e1f4ad64610c4bc2bfc40bb9a535068a034933a5b56b5e7a0febf105a" -dependencies = [ - "log", + "polkavm-linux-raw", ] [[package]] @@ -17514,22 +17492,6 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ff33982a807d8567645d4784b9b5d7ab87bcb494f534a57cadd9012688e102" -[[package]] -name = "polkavm-common" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a5794b695626ba70d29e66e3f4f4835767452a6723f3a0bc20884b07088fe8" -dependencies = [ - "log", - "polkavm-assembler 0.26.0", -] - -[[package]] -name = "polkavm-common" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f634b46a6a47a5de381f56d1d8cced9f8640d063b2b1a44b0da6dbef91bbd400" - [[package]] name = "polkavm-common" version = "0.30.0" @@ -17539,7 +17501,7 @@ dependencies = [ "blake3", "log", "picosimd", - "polkavm-assembler 0.30.0", + "polkavm-assembler", ] [[package]] @@ -17553,20 +17515,11 @@ dependencies = [ [[package]] name = "polkavm-derive" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95282a203ae1f6828a04ff334145c3f6dc718bba6d3959805d273358b45eab93" -dependencies = [ - "polkavm-derive-impl-macro 0.26.0", -] - -[[package]] -name = "polkavm-derive" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ba6256c003853b6adb5dc8394e0e1882a9545ee3bec4e4ce533e7e4f488825" +checksum = "acb4463fb0b9dbfafdc1d1a1183df4bf7afa3350d124f29d5700c6bee54556b5" dependencies = [ - "polkavm-derive-impl-macro 0.29.0", + "polkavm-derive-impl-macro 0.30.0", ] [[package]] @@ -17583,23 +17536,11 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6069dc7995cde6e612b868a02ce48b54397c6d2582bd1b97b63aabbe962cd779" -dependencies = [ - "polkavm-common 0.26.0", - "proc-macro2 1.0.95", - "quote 1.0.40", - "syn 2.0.98", -] - -[[package]] -name = "polkavm-derive-impl" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90751404f08622c8a671695605cfc1bd83ec091339bd3258a37acc7a931c8741" +checksum = "993ff45b972e09babe68adce7062c3c38a84b9f50f07b7caf393a023eaa6c74a" dependencies = [ - "polkavm-common 0.29.0", + "polkavm-common 0.30.0", "proc-macro2 1.0.95", "quote 1.0.40", "syn 2.0.98", @@ -17617,62 +17558,30 @@ dependencies = [ [[package]] name = "polkavm-derive-impl-macro" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581d34cafec741dc5ffafbb341933c205b6457f3d76257a9d99fb56687219c91" -dependencies = [ - "polkavm-derive-impl 0.26.0", - "syn 2.0.98", -] - -[[package]] -name = "polkavm-derive-impl-macro" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e463de593b485c8685d42737aae81c24005dba967deaaaccbb6f3e936d8596" +checksum = "0a4f5352e13c1ca5f0e4d7b4a804fbb85b0e02c45cae435d101fe71081bc8ed8" dependencies = [ - "polkavm-derive-impl 0.29.0", + "polkavm-derive-impl 0.30.0", "syn 2.0.98", ] [[package]] name = "polkavm-linker" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb896023e5bd89bba40311797d8d42490fa4a1fd5256c74820753c5722d1e67" -dependencies = [ - "dirs", - "gimli 0.31.1", - "hashbrown 0.14.5", - "log", - "object 0.36.7", - "polkavm-common 0.26.0", - "regalloc2 0.9.3", - "rustc-demangle", -] - -[[package]] -name = "polkavm-linker" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e01613e9e3e4ebd624aa3a11f1775a5c90b881200c50e054fe13c3ba451f98" +checksum = "6739125c4f8f44b4282b6531d765d599f20514e9b608737c6c3544594d08f995" dependencies = [ "dirs", "gimli 0.31.1", "hashbrown 0.14.5", "log", "object 0.36.7", - "polkavm-common 0.29.0", + "polkavm-common 0.30.0", "regalloc2 0.9.3", "rustc-demangle", ] -[[package]] -name = "polkavm-linux-raw" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28919f542476f4158cc71e6c072b1051f38f4b514253594ac3ad80e3c0211fc8" - [[package]] name = "polkavm-linux-raw" version = "0.30.0" @@ -20473,7 +20382,7 @@ dependencies = [ name = "sc-executor-common" version = "0.29.0" dependencies = [ - "polkavm 0.26.0", + "polkavm", "sc-allocator", "sp-maybe-compressed-blob", "sp-wasm-interface 20.0.0", @@ -20486,7 +20395,7 @@ name = "sc-executor-polkavm" version = "0.29.0" dependencies = [ "log", - "polkavm 0.26.0", + "polkavm", "sc-executor-common", "sp-wasm-interface 20.0.0", ] @@ -23528,7 +23437,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive 0.26.0", + "polkavm-derive 0.30.0", "rustversion", "secp256k1 0.28.2", "sp-core 28.0.0", @@ -23699,7 +23608,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive 0.26.0", + "polkavm-derive 0.30.0", "rustversion", "sp-externalities 0.25.0", "sp-io", @@ -24972,7 +24881,7 @@ dependencies = [ "merkleized-metadata", "parity-scale-codec", "parity-wasm", - "polkavm-linker 0.26.0", + "polkavm-linker", "sc-executor", "shlex", "sp-core 28.0.0", diff --git a/Cargo.toml b/Cargo.toml index f61817fff4142..ccbd5497268e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1173,9 +1173,9 @@ polkadot-subsystem-bench = { path = "polkadot/node/subsystem-bench" } polkadot-test-client = { path = "polkadot/node/test/client" } polkadot-test-runtime = { path = "polkadot/runtime/test-runtime" } polkadot-test-service = { path = "polkadot/node/test/service" } -polkavm = { version = "0.26.0", default-features = false } -polkavm-derive = "0.26.0" -polkavm-linker = "0.26.0" +polkavm = { version = "0.30.0", default-features = false } +polkavm-derive = "0.30.0" +polkavm-linker = "0.30.0" portpicker = { version = "0.1.1" } pretty_assertions = { version = "1.3.0" } primitive-types = { version = "0.13.1", default-features = false, features = ["num-traits"] } diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 9566058e62552..06e105ba53ade 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -26,7 +26,7 @@ anyhow = { workspace = true, default-features = true } cargo_metadata = { workspace = true } hex = { workspace = true, features = ["alloc"] } pallet-revive-uapi = { workspace = true } -polkavm-linker = { version = "0.29.0" } +polkavm-linker = { version = "0.30.0" } serde_json = { workspace = true } toml = { workspace = true } diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index d4af984d6bf3c..8b6fe3678e5af 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -177,6 +177,9 @@ fn create_cargo_toml<'a>( fn invoke_build(current_dir: &Path) -> Result<()> { let encoded_rustflags = ["-Dwarnings"].join("\x1f"); + let mut args = polkavm_linker::TargetJsonArgs::default(); + args.is_64_bit = true; + let mut build_command = Command::new("cargo"); build_command .current_dir(current_dir) @@ -193,7 +196,7 @@ fn invoke_build(current_dir: &Path) -> Result<()> { "-Zbuild-std-features=panic_immediate_abort", ]) .arg("--target") - .arg(polkavm_linker::target_json_64_path().unwrap()); + .arg(polkavm_linker::target_json_path(args).unwrap()); if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) { build_command.env("RUSTUP_TOOLCHAIN", &toolchain); @@ -220,8 +223,12 @@ fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { config.set_strip(strip); config.set_optimize(optimize); let orig = fs::read(input_path).with_context(|| format!("Failed to read {input_path:?}"))?; - let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) - .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; + let linked = polkavm_linker::program_from_elf( + config, + polkavm_linker::TargetInstructionSet::Latest, + orig.as_ref(), + ) + .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; fs::write(output_path, linked).with_context(|| format!("Failed to write {output_path:?}"))?; Ok(()) } diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 98adc0b1e6afd..57e9c87db601f 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -28,7 +28,7 @@ pallet-revive-proc-macro = { workspace = true } scale-info = { features = ["derive"], optional = true, workspace = true } [target.'cfg(target_arch = "riscv64")'.dependencies] -polkavm-derive = { version = "0.29.0" } +polkavm-derive = { version = "0.30.0" } [features] default = ["scale"] diff --git a/substrate/utils/wasm-builder/src/lib.rs b/substrate/utils/wasm-builder/src/lib.rs index 47c156e5907f3..dbb554d2306a1 100644 --- a/substrate/utils/wasm-builder/src/lib.rs +++ b/substrate/utils/wasm-builder/src/lib.rs @@ -440,7 +440,9 @@ impl RuntimeTarget { "wasm32-unknown-unknown".into() }, RuntimeTarget::Riscv => { - let path = polkavm_linker::target_json_32_path().expect("riscv not found"); + let mut args = polkavm_linker::TargetJsonArgs::default(); + args.is_64_bit = false; + let path = polkavm_linker::target_json_path(args).expect("riscv not found"); path.into_os_string().into_string().unwrap() }, } diff --git a/substrate/utils/wasm-builder/src/wasm_project.rs b/substrate/utils/wasm-builder/src/wasm_project.rs index e1bea1f04cf13..905ee1eec260b 100644 --- a/substrate/utils/wasm-builder/src/wasm_project.rs +++ b/substrate/utils/wasm-builder/src/wasm_project.rs @@ -23,6 +23,7 @@ use build_helper::rerun_if_changed; use cargo_metadata::{DependencyKind, Metadata, MetadataCommand}; use console::style; use parity_wasm::elements::{deserialize_buffer, Module}; +use polkavm_linker::TargetInstructionSet; use std::{ borrow::ToOwned, collections::HashSet, @@ -1006,7 +1007,11 @@ fn build_bloaty_blob( let mut config = polkavm_linker::Config::default(); config.set_strip(true); // TODO: This shouldn't always be done. - let program = match polkavm_linker::program_from_elf(config, &blob_bytes) { + let program = match polkavm_linker::program_from_elf( + config, + TargetInstructionSet::Latest, + &blob_bytes, + ) { Ok(program) => program, Err(error) => { println!("Failed to link the runtime blob; this is probably a bug!"); From b6e334349b18ee54c4d0a15692c556adee3f8681 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:34:45 +0100 Subject: [PATCH 08/23] revive: Add pvm_logs flag to DebugSettings --- substrate/frame/revive/src/debug.rs | 11 +++++++++-- substrate/frame/revive/src/tests/sol.rs | 2 +- substrate/frame/revive/src/vm/pvm/env.rs | 6 ++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs index 82cea3e8c308e..099c536b2eb2d 100644 --- a/substrate/frame/revive/src/debug.rs +++ b/substrate/frame/revive/src/debug.rs @@ -38,11 +38,13 @@ use sp_runtime::RuntimeDebug; pub struct DebugSettings { /// Whether to allow unlimited contract size. allow_unlimited_contract_size: bool, + /// Whether to enable PolkaVM logs. + pvm_logs: bool, } impl DebugSettings { - pub fn new(allow_unlimited_contract_size: bool) -> Self { - Self { allow_unlimited_contract_size } + pub fn new(allow_unlimited_contract_size: bool, pvm_logs: bool) -> Self { + Self { allow_unlimited_contract_size, pvm_logs } } /// Returns true if unlimited contract size is allowed. @@ -50,6 +52,11 @@ impl DebugSettings { T::DebugEnabled::get() && DebugSettingsOf::::get().allow_unlimited_contract_size } + /// Returns true if PolkaVM logs are enabled. + pub fn is_pvm_logs_enabled() -> bool { + T::DebugEnabled::get() && DebugSettingsOf::::get().pvm_logs + } + /// Write the debug settings to storage. pub fn write_to_storage(&self) { DebugSettingsOf::::put(self); diff --git a/substrate/frame/revive/src/tests/sol.rs b/substrate/frame/revive/src/tests/sol.rs index 93de26d0ee9d5..d04c749719db1 100644 --- a/substrate/frame/revive/src/tests/sol.rs +++ b/substrate/frame/revive/src/tests/sol.rs @@ -180,7 +180,7 @@ fn eth_contract_too_large() { // Initialize genesis config with allow_unlimited_contract_size let genesis_config = GenesisConfig:: { - debug_settings: Some(DebugSettings::new(allow_unlimited_contract_size)), + debug_settings: Some(DebugSettings::new(allow_unlimited_contract_size, false)), ..Default::default() }; diff --git a/substrate/frame/revive/src/vm/pvm/env.rs b/substrate/frame/revive/src/vm/pvm/env.rs index 7a5229f78ce57..06b753ea8eb8c 100644 --- a/substrate/frame/revive/src/vm/pvm/env.rs +++ b/substrate/frame/revive/src/vm/pvm/env.rs @@ -19,6 +19,7 @@ use super::*; use crate::{ address::AddressMapper, + debug::DebugSettings, exec::Ext, limits, primitives::ExecReturnValue, @@ -50,8 +51,9 @@ impl ContractBlob { // Log filtering by level with log::enabled! returns always true, // passing all logs through impacting performance \ // (more details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774) - // Let's disable polkavm logging. - config.set_imperfect_logger_filtering_workaround(true); + // By default, disable polkavm logging unless pvm_logs debug setting is enabled. + let pvm_logs_enabled = DebugSettings::is_pvm_logs_enabled::(); + config.set_imperfect_logger_filtering_workaround(!pvm_logs_enabled); config.set_backend(Some(polkavm::BackendKind::Interpreter)); config.set_cache_enabled(false); #[cfg(feature = "std")] From cf29625b3c0ce6832cf3744e4e8f7bc37163f395 Mon Sep 17 00:00:00 2001 From: "cmd[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:56:16 +0000 Subject: [PATCH 09/23] Update from github-actions[bot] running command 'prdoc --audience runtime_dev --bump major --force' --- prdoc/pr_10385.prdoc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 43d7d3837b342..3fbb690583cf6 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -2,12 +2,23 @@ title: Disable polkavm logging in `pallet-revive` doc: - audience: Runtime Dev description: |- - This PR disables logging in `polkavm` in `pallet-revive`. - This is to make sure massive `polkavm` logging don't impact the overall perfomance, which might be manifested with eg. long block proposal time. + This PR adds configurable control over PolkaVM logging in `pallet-revive` to address performance degradation (details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774) - details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774 + - Upgrades PolkaVM to v0.30.0 which provides `set_imperfect_logger_filtering_workaround()` + - Adds `pvm_logs` flag to `DebugSettings` to control PolkaVM interpreter logging + - Disables PolkaVM logs by default (when `pvm_logs=false`), enabling them only when explicitly configured + - Fixes performance issue where excessive PolkaVM logging was impacting block proposal times - The fix requires bumping `polkavm` to `0.30.0` which includes a method that allows to disable logging. + The logging can be re-enabled via debug settings when needed for troubleshooting. + + Additionally: + - PolkaVM has been bumped globally across whole codebase. crates: - name: pallet-revive - bump: minor + bump: major +- name: pallet-revive-fixtures + bump: major +- name: pallet-revive-uapi + bump: major +- name: substrate-wasm-builder + bump: major From 8d84b6adf8aa1af0a4d13b5368ab3c94aa1c6f72 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:26:08 +0100 Subject: [PATCH 10/23] fix prdoc --- prdoc/pr_10385.prdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 3fbb690583cf6..6b5aeae19356a 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -17,8 +17,8 @@ crates: - name: pallet-revive bump: major - name: pallet-revive-fixtures - bump: major + bump: patch - name: pallet-revive-uapi - bump: major + bump: patch - name: substrate-wasm-builder - bump: major + bump: minor From 3d02063772db83deb6e1ce0f45b868ff41348225 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 17:13:46 +0100 Subject: [PATCH 11/23] pallet-revive: Use PVM Revive instructions --- substrate/frame/revive/fixtures/build.rs | 2 +- substrate/frame/revive/src/limits.rs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index 8b6fe3678e5af..aeec2e9163d42 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -225,7 +225,7 @@ fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { let orig = fs::read(input_path).with_context(|| format!("Failed to read {input_path:?}"))?; let linked = polkavm_linker::program_from_elf( config, - polkavm_linker::TargetInstructionSet::Latest, + polkavm_linker::TargetInstructionSet::ReviveV1, orig.as_ref(), ) .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index bcaf7fe0dcc6f..edac4e2cdf62e 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -138,7 +138,9 @@ pub mod code { pvm_blob: Vec, available_syscalls: &[&[u8]], ) -> Result, DispatchError> { - use polkavm_common::program::{EstimateInterpreterMemoryUsageArgs, ISA_Latest64 as ISA}; + use polkavm_common::program::{ + EstimateInterpreterMemoryUsageArgs, ISA_ReviveV1, InstructionSetKind, + }; let len: u64 = pvm_blob.len() as u64; if len > crate::limits::code::BLOB_BYTES.into() { @@ -162,6 +164,11 @@ pub mod code { Err(Error::::CodeRejected)?; } + if program.isa() != InstructionSetKind::ReviveV1 { + log::debug!(target: LOG_TARGET, "Program instruction set '{}' is not '{}'", program.isa().name(), InstructionSetKind::ReviveV1.name()); + Err(Error::::CodeRejected)?; + } + // Need to check that no non-existent syscalls are used. This allows us to add // new syscalls later without affecting already deployed code. for (idx, import) in program.imports().iter().enumerate() { @@ -189,7 +196,7 @@ pub mod code { let mut block_size: u32 = 0; let mut basic_block_count: u32 = 0; let mut instruction_count: u32 = 0; - for inst in program.instructions_with_isa(ISA) { + for inst in program.instructions_with_isa(ISA_ReviveV1) { use polkavm::program::Instruction; block_size += 1; instruction_count += 1; From b1fdac958e6d0b34c5863914db5380166a82b28a Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:22:01 +0100 Subject: [PATCH 12/23] pallet-revive: refactor sbrk test --- substrate/frame/revive/fixtures/Cargo.toml | 4 + .../sbrk.rs | 0 .../frame/revive/fixtures/src/builder.rs | 164 ++++++++++++++++++ substrate/frame/revive/fixtures/src/lib.rs | 33 ++++ substrate/frame/revive/src/tests/pvm.rs | 32 ++-- 5 files changed, 213 insertions(+), 20 deletions(-) rename substrate/frame/revive/fixtures/{contracts => contracts_not_buildable}/sbrk.rs (100%) create mode 100644 substrate/frame/revive/fixtures/src/builder.rs diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 06e105ba53ade..9b81729c16274 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -18,8 +18,10 @@ workspace = true [dependencies] alloy-core = { workspace = true, default-features = true, features = ["sol-types"], optional = true } anyhow = { workspace = true, default-features = true, optional = true } +polkavm-linker = { version = "0.30.0", optional = true } sp-core = { workspace = true, default-features = true, optional = true } sp-io = { workspace = true, default-features = true, optional = true } +toml = { workspace = true, optional = true } [build-dependencies] anyhow = { workspace = true, default-features = true } @@ -37,7 +39,9 @@ std = [ "alloy-core", "anyhow", "hex/std", + "polkavm-linker", "serde_json/std", "sp-core", "sp-io", + "toml", ] diff --git a/substrate/frame/revive/fixtures/contracts/sbrk.rs b/substrate/frame/revive/fixtures/contracts_not_buildable/sbrk.rs similarity index 100% rename from substrate/frame/revive/fixtures/contracts/sbrk.rs rename to substrate/frame/revive/fixtures/contracts_not_buildable/sbrk.rs diff --git a/substrate/frame/revive/fixtures/src/builder.rs b/substrate/frame/revive/fixtures/src/builder.rs new file mode 100644 index 0000000000000..3c66117d21cb3 --- /dev/null +++ b/substrate/frame/revive/fixtures/src/builder.rs @@ -0,0 +1,164 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared code for building fixtures from Rust source. +//! Used by both build.rs and test code. + +use anyhow::{Context, Result}; +use std::{ + env, + fs, + path::{Path, PathBuf}, + process::Command, +}; + +/// Entry representing a contract to build. +pub struct BuildEntry { + pub name: String, + pub path: String, +} + +impl BuildEntry { + pub fn new(name: impl Into, path: impl Into) -> Self { + Self { name: name.into(), path: path.into() } + } +} + +/// Create a Cargo.toml for building contracts using the template from build/_Cargo.toml. +pub fn create_cargo_toml( + entries: &[BuildEntry], + output_dir: &Path, +) -> Result<()> { + let mut cargo_toml: toml::Value = toml::from_str(include_str!("../build/_Cargo.toml"))?; + + // Set uapi dependency path + let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let uapi_path = PathBuf::from(manifest_dir).parent().unwrap().join("uapi"); + uapi_dep.insert( + "path".to_string(), + toml::Value::String(uapi_path.to_str().unwrap().to_string()), + ); + + // Set binary targets + cargo_toml["bin"] = toml::Value::Array( + entries + .iter() + .map(|entry| { + let mut table = toml::map::Map::new(); + table.insert("name".to_string(), toml::Value::String(entry.name.clone())); + table.insert("path".to_string(), toml::Value::String(entry.path.clone())); + toml::Value::Table(table) + }) + .collect::>(), + ); + + let cargo_toml_str = toml::to_string_pretty(&cargo_toml)?; + fs::write(output_dir.join("Cargo.toml"), cargo_toml_str) + .with_context(|| format!("Failed to write Cargo.toml to {:?}", output_dir))?; + Ok(()) +} + +/// Invoke cargo build to compile contracts to RISC-V ELF. +pub fn invoke_build(current_dir: &Path) -> Result<()> { + let encoded_rustflags = ["-Dwarnings"].join("\x1f"); + + let mut args = polkavm_linker::TargetJsonArgs::default(); + args.is_64_bit = true; + + let mut build_command = Command::new("cargo"); + build_command + .current_dir(current_dir) + .env_clear() + .env("PATH", env::var("PATH").unwrap_or_default()) + .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) + .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) + .env("RUSTC_BOOTSTRAP", "1") + .args([ + "build", + "--release", + "-Zbuild-std=core", + "-Zbuild-std-features=panic_immediate_abort", + ]) + .arg("--target") + .arg(polkavm_linker::target_json_path(args).unwrap()); + + if let Ok(toolchain) = env::var("PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN") { + build_command.env("RUSTUP_TOOLCHAIN", &toolchain); + } + + let build_res = build_command.output().expect("failed to execute process"); + + if !build_res.status.success() { + let stderr = String::from_utf8_lossy(&build_res.stderr); + eprintln!("{}", stderr); + anyhow::bail!("Failed to build contracts"); + } + + Ok(()) +} + +/// Compile a Rust contract source to RISC-V ELF. +pub fn compile_rust_to_elf( + contract_path: &Path, + contract_name: &str, + output_dir: &Path, +) -> Result { + // Create Cargo.toml with single entry + let entry = BuildEntry::new(contract_name, contract_path.to_str().unwrap()); + create_cargo_toml(&[entry], output_dir)?; + + // Build + invoke_build(output_dir)?; + + // Return path to ELF + let elf_path = output_dir + .join("target/riscv64emac-unknown-none-polkavm/release") + .join(contract_name); + + if !elf_path.exists() { + anyhow::bail!("ELF not found at {:?}", elf_path); + } + + Ok(elf_path) +} + +/// Link a RISC-V ELF to PolkaVM bytecode. +pub fn link_elf_to_polkavm(elf_path: &Path) -> Result> { + let elf_bytes = std::fs::read(elf_path) + .with_context(|| format!("Failed to read ELF from {:?}", elf_path))?; + + let config = polkavm_linker::Config::default(); + let linked = polkavm_linker::program_from_elf( + config, + polkavm_linker::TargetInstructionSet::ReviveV1, + &elf_bytes, + ) + .map_err(|err| anyhow::anyhow!("Failed to link polkavm program from {:?}: {}", elf_path, err))?; + + Ok(linked) +} + +/// Compile a Rust contract source all the way to PolkaVM bytecode. +pub fn compile_rust_to_polkavm( + contract_path: &Path, + contract_name: &str, + temp_dir: &Path, +) -> Result> { + let elf_path = compile_rust_to_elf(contract_path, contract_name, temp_dir)?; + link_elf_to_polkavm(&elf_path) +} diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 3477f00ecfc83..4b36fe2e37bee 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -22,6 +22,9 @@ extern crate alloc; // generated file that tells us where to find the fixtures include!(concat!(env!("OUT_DIR"), "/fixture_location.rs")); +#[cfg(feature = "std")] +pub mod builder; + /// Enum for different fixture types #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FixtureType { @@ -68,6 +71,36 @@ pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H compile_module_with_type(fixture_name, FixtureType::Rust) } +/// Try to compile a fixture from the contracts_not_buildable directory. +/// This replicates the build process but expects it to fail at the linking stage. +/// Returns Err if compilation/linking fails (which is expected for invalid fixtures). +#[cfg(feature = "std")] +pub fn try_compile_invalid_fixture(fixture_name: &str) -> anyhow::Result> { + use anyhow::bail; + use std::{fs, path::PathBuf}; + + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let fixture_path = PathBuf::from(manifest_dir) + .join("contracts_not_buildable") + .join(format!("{}.rs", fixture_name)); + + if !fixture_path.exists() { + bail!("Invalid fixture not found: {:?}", fixture_path); + } + + // Create temporary build directory + let temp_dir = std::env::temp_dir().join(format!("revive_invalid_fixture_{}", fixture_name)); + fs::create_dir_all(&temp_dir)?; + + // Use the shared builder module to compile + let result = builder::compile_rust_to_polkavm(&fixture_path, fixture_name, &temp_dir); + + // Cleanup + let _ = fs::remove_dir_all(&temp_dir); + + result +} + /// Fixtures used in runtime benchmarks. /// /// We explicitly include those fixtures into the binary to make them diff --git a/substrate/frame/revive/src/tests/pvm.rs b/substrate/frame/revive/src/tests/pvm.rs index 51066bf1dc220..faf1abcad7408 100644 --- a/substrate/frame/revive/src/tests/pvm.rs +++ b/substrate/frame/revive/src/tests/pvm.rs @@ -3685,26 +3685,18 @@ fn immutable_data_works() { } #[test] -fn sbrk_cannot_be_deployed() { - let (code, _) = compile_module("sbrk").unwrap(); - - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::set_balance(&ALICE, 1_000_000); - - assert_err!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - code.clone(), - deposit_limit::(), - ), - >::InvalidInstruction - ); - - assert_err!( - builder::bare_instantiate(Code::Upload(code)).build().result, - >::InvalidInstruction - ); - }); +fn sbrk_cannot_be_linked() { + // The sbrk instruction is not available in the revive_v1 instruction set. + // This test verifies that the linker rejects it during the linking phase. + let result = pallet_revive_fixtures::try_compile_invalid_fixture("sbrk"); + + assert!(result.is_err(), "Expected linking to fail for sbrk fixture"); + let err_msg = result.unwrap_err().to_string(); + assert!( + err_msg.contains("sbrk") || err_msg.contains("not available"), + "Expected error message to mention 'sbrk' or 'not available', got: {}", + err_msg + ); } #[test] From a8fe52467206246caceae73cde9eeace24b4085a Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:07:30 +0100 Subject: [PATCH 13/23] pallet-revive-fixtures: cleanup --- substrate/frame/revive/fixtures/Cargo.toml | 6 + substrate/frame/revive/fixtures/build.rs | 447 +----------------- .../frame/revive/fixtures/src/builder.rs | 418 +++++++++++++++- 3 files changed, 420 insertions(+), 451 deletions(-) diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 9b81729c16274..b5780c111ed58 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -18,7 +18,10 @@ workspace = true [dependencies] alloy-core = { workspace = true, default-features = true, features = ["sol-types"], optional = true } anyhow = { workspace = true, default-features = true, optional = true } +cargo_metadata = { workspace = true, optional = true } +hex = { workspace = true, features = ["alloc"], optional = true } polkavm-linker = { version = "0.30.0", optional = true } +serde_json = { workspace = true, optional = true } sp-core = { workspace = true, default-features = true, optional = true } sp-io = { workspace = true, default-features = true, optional = true } toml = { workspace = true, optional = true } @@ -38,8 +41,11 @@ default = ["std"] std = [ "alloy-core", "anyhow", + "cargo_metadata", + "hex", "hex/std", "polkavm-linker", + "serde_json", "serde_json/std", "sp-core", "sp-io", diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index aeec2e9163d42..6fa1488d38b3f 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -16,14 +16,16 @@ // limitations under the License. //! Compile text fixtures to PolkaVM binaries. -use anyhow::{bail, Context, Result}; -use cargo_metadata::MetadataCommand; -use std::{ - env, fs, - io::Write, - path::{Path, PathBuf}, - process::Command, + +#[path = "src/builder.rs"] +mod builder; + +use anyhow::{Context, Result}; +use builder::{ + collect_entries, compile_solidity_contracts, create_cargo_toml, generate_fixture_location, + invoke_build, write_output, ContractType, }; +use std::{env, fs, path::PathBuf}; const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN"; const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP"; @@ -33,437 +35,6 @@ const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE"; /// Depending on the usage, they will probably panic at runtime. const SKIP_PALLET_REVIVE_FIXTURES: &str = "SKIP_PALLET_REVIVE_FIXTURES"; -/// A contract entry. -#[derive(Clone)] -struct Entry { - /// The path to the contract source file. - path: PathBuf, - /// The type of the contract (rust or solidity). - contract_type: ContractType, -} - -#[derive(Clone, Copy)] -enum ContractType { - Rust, - Solidity, -} - -/// Type of EVM bytecode to extract from Solidity compiler output. -#[derive(Clone, Copy)] -enum EvmByteCodeType { - InitCode, - RuntimeCode, -} - -impl EvmByteCodeType { - fn json_key(&self) -> &'static str { - match self { - Self::InitCode => "bytecode", - Self::RuntimeCode => "deployedBytecode", - } - } -} - -impl Entry { - /// Create a new contract entry from the given path. - fn new(path: PathBuf, contract_type: ContractType) -> Self { - Self { path, contract_type } - } - - /// Return the path to the contract source file. - fn path(&self) -> &str { - self.path.to_str().expect("path is valid unicode; qed") - } - - /// Return the name of the contract. - fn name(&self) -> &str { - self.path - .file_stem() - .expect("file exits; qed") - .to_str() - .expect("name is valid unicode; qed") - } - - /// Return the name of the bytecode file. - fn out_filename(&self) -> String { - match self.contract_type { - ContractType::Rust => format!("{}.polkavm", self.name()), - ContractType::Solidity => format!("{}.resolc.polkavm", self.name()), - } - } -} - -/// Collect all contract entries from the given source directory. -fn collect_entries(contracts_dir: &Path) -> Vec { - fs::read_dir(contracts_dir) - .expect("src dir exists; qed") - .filter_map(|file| { - let path = file.expect("file exists; qed").path(); - let extension = path.extension(); - - match extension.and_then(|ext| ext.to_str()) { - Some("rs") => Some(Entry::new(path, ContractType::Rust)), - Some("sol") => Some(Entry::new(path, ContractType::Solidity)), - _ => None, - } - }) - .collect::>() -} - -/// Create a `Cargo.toml` to compile the given Rust contract entries. -fn create_cargo_toml<'a>( - fixtures_dir: &Path, - entries: impl Iterator, - output_dir: &Path, -) -> Result<()> { - let mut cargo_toml: toml::Value = toml::from_str(include_str!("./build/_Cargo.toml"))?; - let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); - - let manifest_path = fixtures_dir.join("Cargo.toml"); - let metadata = MetadataCommand::new().manifest_path(&manifest_path).exec().unwrap(); - let dependency_graph = metadata.resolve.unwrap(); - - // Resolve the pallet-revive-fixtures package id - let fixtures_pkg_id = metadata - .packages - .iter() - .find(|pkg| pkg.manifest_path.as_std_path() == manifest_path) - .map(|pkg| pkg.id.clone()) - .unwrap(); - let fixtures_pkg_node = - dependency_graph.nodes.iter().find(|node| node.id == fixtures_pkg_id).unwrap(); - - // Get the pallet-revive-uapi package id - let uapi_pkg_id = fixtures_pkg_node - .deps - .iter() - .find(|dep| dep.name == "pallet_revive_uapi") - .map(|dep| dep.pkg.clone()) - .expect("pallet-revive-uapi is a build dependency of pallet-revive-fixtures; qed"); - - // Get pallet-revive-uapi package - let uapi_pkg = metadata.packages.iter().find(|pkg| pkg.id == uapi_pkg_id).unwrap(); - - if uapi_pkg.source.is_none() { - uapi_dep.insert( - "path".to_string(), - toml::Value::String( - fixtures_dir.join("../uapi").canonicalize()?.to_str().unwrap().to_string(), - ), - ); - } else { - uapi_dep.insert("version".to_string(), toml::Value::String(uapi_pkg.version.to_string())); - } - - cargo_toml["bin"] = toml::Value::Array( - entries - .map(|entry| { - let name = entry.name(); - let path = entry.path(); - toml::Value::Table(toml::toml! { - name = name - path = path - }) - }) - .collect::>(), - ); - - let cargo_toml = toml::to_string_pretty(&cargo_toml)?; - fs::write(output_dir.join("Cargo.toml"), cargo_toml.clone()) - .with_context(|| format!("Failed to write {cargo_toml:?}"))?; - Ok(()) -} - -fn invoke_build(current_dir: &Path) -> Result<()> { - let encoded_rustflags = ["-Dwarnings"].join("\x1f"); - - let mut args = polkavm_linker::TargetJsonArgs::default(); - args.is_64_bit = true; - - let mut build_command = Command::new("cargo"); - build_command - .current_dir(current_dir) - .env_clear() - .env("PATH", env::var("PATH").unwrap_or_default()) - .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) - .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) - // Support compilation on stable rust - .env("RUSTC_BOOTSTRAP", "1") - .args([ - "build", - "--release", - "-Zbuild-std=core", - "-Zbuild-std-features=panic_immediate_abort", - ]) - .arg("--target") - .arg(polkavm_linker::target_json_path(args).unwrap()); - - if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) { - build_command.env("RUSTUP_TOOLCHAIN", &toolchain); - } - - let build_res = build_command.output().expect("failed to execute process"); - - if build_res.status.success() { - return Ok(()); - } - - let stderr = String::from_utf8_lossy(&build_res.stderr); - eprintln!("{}", stderr); - - bail!("Failed to build contracts"); -} - -/// Post-process the compiled code. -fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { - let strip = env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1"); - let optimize = env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1"); - - let mut config = polkavm_linker::Config::default(); - config.set_strip(strip); - config.set_optimize(optimize); - let orig = fs::read(input_path).with_context(|| format!("Failed to read {input_path:?}"))?; - let linked = polkavm_linker::program_from_elf( - config, - polkavm_linker::TargetInstructionSet::ReviveV1, - orig.as_ref(), - ) - .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; - fs::write(output_path, linked).with_context(|| format!("Failed to write {output_path:?}"))?; - Ok(()) -} - -/// Compile a Solidity contract using standard JSON interface. -fn compile_with_standard_json( - compiler: &str, - contracts_dir: &Path, - solidity_entries: &[&Entry], -) -> Result { - let mut input_json = serde_json::json!({ - "language": "Solidity", - "sources": {}, - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": - - serde_json::json!({ - "*": { - "*": ["evm.bytecode", "evm.deployedBytecode"] - } - }), - - } - }); - - // Add all Solidity files to the input - for entry in solidity_entries { - let source_code = fs::read_to_string(entry.path()) - .with_context(|| format!("Failed to read Solidity source: {}", entry.path()))?; - - let file_key = entry.path().split('/').last().unwrap_or(entry.name()); - input_json["sources"][file_key] = serde_json::json!({ - "content": source_code - }); - } - - let compiler_output = Command::new(compiler) - .current_dir(contracts_dir) - .arg("--standard-json") - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .spawn() - .with_context(|| { - format!( - "Failed to execute {compiler}. Make sure {compiler} is installed or \ - set env variable `{SKIP_PALLET_REVIVE_FIXTURES}=1` to skip fixtures compilation." - ) - })?; - - let mut stdin = compiler_output.stdin.as_ref().unwrap(); - stdin - .write_all(input_json.to_string().as_bytes()) - .with_context(|| format!("Failed to write to {} stdin", compiler))?; - let _ = stdin; - - let compiler_result = compiler_output - .wait_with_output() - .with_context(|| format!("Failed to wait for {} output", compiler))?; - - if !compiler_result.status.success() { - let stderr = String::from_utf8_lossy(&compiler_result.stderr); - bail!("{} compilation failed: {}", compiler, stderr); - } - - // Parse JSON output - let compiler_json: serde_json::Value = serde_json::from_slice(&compiler_result.stdout) - .with_context(|| format!("Failed to parse {} JSON output", compiler))?; - - // Abort on errors - if let Some(errors) = compiler_json.get("errors") { - if errors - .as_array() - .unwrap() - .iter() - .any(|object| object.get("severity").unwrap().as_str().unwrap() == "error") - { - bail!( - "failed to compile the Solidity fixtures: {}", - serde_json::to_string_pretty(errors)? - ); - } - } - - Ok(compiler_json) -} - -/// Extract bytecode from compiler JSON output and write binary files. -fn extract_and_write_bytecode( - compiler_json: &serde_json::Value, - out_dir: &Path, - file_suffix: &str, - bytecode_type: EvmByteCodeType, -) -> Result<()> { - if let Some(contracts) = compiler_json["contracts"].as_object() { - for (_file_key, file_contracts) in contracts { - if let Some(contract_map) = file_contracts.as_object() { - for (contract_name, contract_data) in contract_map { - // Navigate through the JSON path to find the bytecode - let mut current = contract_data; - for path_segment in ["evm", bytecode_type.json_key(), "object"] { - if let Some(next) = current.get(path_segment) { - current = next; - } else { - // Skip if path doesn't exist (e.g., contract has no bytecode) - continue; - } - } - - if let Some(bytecode_obj) = current.as_str() { - let bytecode_hex = bytecode_obj.strip_prefix("0x").unwrap_or(bytecode_obj); - let binary_content = hex::decode(bytecode_hex).map_err(|e| { - anyhow::anyhow!("Failed to decode hex for {contract_name}: {e}") - })?; - - let out_path = out_dir.join(format!("{}{}", contract_name, file_suffix)); - fs::write(&out_path, binary_content).with_context(|| { - format!("Failed to write {out_path:?} for {contract_name}") - })?; - } - } - } - } - } - Ok(()) -} - -/// Compile Solidity contracts using both solc and resolc. -fn compile_solidity_contracts( - contracts_dir: &Path, - out_dir: &Path, - entries: &[Entry], -) -> Result<()> { - let solidity_entries: Vec<_> = entries - .iter() - .filter(|entry| matches!(entry.contract_type, ContractType::Solidity)) - .collect(); - - if solidity_entries.is_empty() { - return Ok(()); - } - - let evm_only = vec!["HostEvmOnly"]; - let solidity_entries_pvm: Vec<_> = solidity_entries - .iter() - .cloned() - .filter(|entry| !evm_only.contains(&entry.path.file_stem().unwrap().to_str().unwrap())) - .collect(); - - // Compile with solc for EVM bytecode - let json = compile_with_standard_json("solc", contracts_dir, &solidity_entries)?; - extract_and_write_bytecode(&json, out_dir, ".sol.bin", EvmByteCodeType::InitCode)?; - extract_and_write_bytecode(&json, out_dir, ".sol.runtime.bin", EvmByteCodeType::RuntimeCode)?; - - // Compile with resolc for PVM bytecode - let json = compile_with_standard_json("resolc", contracts_dir, &solidity_entries_pvm)?; - extract_and_write_bytecode(&json, out_dir, ".resolc.polkavm", EvmByteCodeType::InitCode)?; - - Ok(()) -} - -/// Write the compiled Rust contracts to the given output directory. -fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { - for entry in entries { - if matches!(entry.contract_type, ContractType::Rust) { - post_process( - &build_dir - .join("target/riscv64emac-unknown-none-polkavm/release") - .join(entry.name()), - &out_dir.join(entry.out_filename()), - )?; - } - } - - Ok(()) -} - -/// Generate the fixture_location.rs file with macros and sol! definitions. -fn generate_fixture_location(temp_dir: &Path, out_dir: &Path, entries: &[Entry]) -> Result<()> { - let mut file = fs::File::create(temp_dir.join("fixture_location.rs")) - .context("Failed to create fixture_location.rs")?; - - let (fixtures, fixtures_resolc) = if env::var(SKIP_PALLET_REVIVE_FIXTURES).is_err() { - ( - format!( - r#"Some(include_bytes!(concat!("{}", "/", $name, ".polkavm")))"#, - out_dir.display() - ), - format!( - r#"Some(include_bytes!(concat!("{}", "/", $name, ".resolc.polkavm")))"#, - out_dir.display() - ), - ) - } else { - ("None".into(), "None".into()) - }; - - write!( - file, - r#" - #[allow(dead_code)] - const FIXTURE_DIR: &str = "{0}"; - - #[macro_export] - macro_rules! fixture {{ - ($name: literal) => {{ - {fixtures} - }}; - }} - - #[macro_export] - macro_rules! fixture_resolc {{ - ($name: literal) => {{ - {fixtures_resolc} - }}; - }} - "#, - out_dir.display() - ) - .context("Failed to write to fixture_location.rs")?; - - // Generate sol! macros for Solidity contracts - for entry in entries.iter().filter(|e| matches!(e.contract_type, ContractType::Solidity)) { - let relative_path = format!("contracts/{}", entry.path().split('/').last().unwrap()); - writeln!(file, r#"#[cfg(feature = "std")] alloy_core::sol!("{}");"#, relative_path) - .context("Failed to write sol! macro to fixture_location.rs")?; - } - - Ok(()) -} - pub fn main() -> Result<()> { // input pathes let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); diff --git a/substrate/frame/revive/fixtures/src/builder.rs b/substrate/frame/revive/fixtures/src/builder.rs index 3c66117d21cb3..54b41532e97fc 100644 --- a/substrate/frame/revive/fixtures/src/builder.rs +++ b/substrate/frame/revive/fixtures/src/builder.rs @@ -18,14 +18,76 @@ //! Shared code for building fixtures from Rust source. //! Used by both build.rs and test code. -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; +#[cfg(feature = "std")] +use cargo_metadata::MetadataCommand; use std::{ - env, - fs, + env, fs, + io::Write, path::{Path, PathBuf}, process::Command, }; +/// A contract entry. +#[derive(Clone)] +pub struct Entry { + /// The path to the contract source file. + pub path: PathBuf, + /// The type of the contract (rust or solidity). + pub contract_type: ContractType, +} + +#[derive(Clone, Copy)] +pub enum ContractType { + Rust, + Solidity, +} + +/// Type of EVM bytecode to extract from Solidity compiler output. +#[derive(Clone, Copy)] +enum EvmByteCodeType { + InitCode, + RuntimeCode, +} + +impl EvmByteCodeType { + fn json_key(&self) -> &'static str { + match self { + Self::InitCode => "bytecode", + Self::RuntimeCode => "deployedBytecode", + } + } +} + +impl Entry { + /// Create a new contract entry from the given path. + pub fn new(path: PathBuf, contract_type: ContractType) -> Self { + Self { path, contract_type } + } + + /// Return the path to the contract source file. + pub fn path(&self) -> &str { + self.path.to_str().expect("path is valid unicode; qed") + } + + /// Return the name of the contract. + pub fn name(&self) -> &str { + self.path + .file_stem() + .expect("file exits; qed") + .to_str() + .expect("name is valid unicode; qed") + } + + /// Return the name of the bytecode file. + pub fn out_filename(&self) -> String { + match self.contract_type { + ContractType::Rust => format!("{}.polkavm", self.name()), + ContractType::Solidity => format!("{}.resolc.polkavm", self.name()), + } + } +} + /// Entry representing a contract to build. pub struct BuildEntry { pub name: String, @@ -38,21 +100,34 @@ impl BuildEntry { } } +/// Collect all contract entries from the given source directory. +pub fn collect_entries(contracts_dir: &Path) -> Vec { + fs::read_dir(contracts_dir) + .expect("src dir exists; qed") + .filter_map(|file| { + let path = file.expect("file exists; qed").path(); + let extension = path.extension(); + + match extension.and_then(|ext| ext.to_str()) { + Some("rs") => Some(Entry::new(path, ContractType::Rust)), + Some("sol") => Some(Entry::new(path, ContractType::Solidity)), + _ => None, + } + }) + .collect::>() +} + /// Create a Cargo.toml for building contracts using the template from build/_Cargo.toml. -pub fn create_cargo_toml( - entries: &[BuildEntry], - output_dir: &Path, -) -> Result<()> { +/// This is a simple version that uses a hardcoded path for uapi dependency. +pub fn create_cargo_toml_simple(entries: &[BuildEntry], output_dir: &Path) -> Result<()> { let mut cargo_toml: toml::Value = toml::from_str(include_str!("../build/_Cargo.toml"))?; // Set uapi dependency path let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); let manifest_dir = env!("CARGO_MANIFEST_DIR"); let uapi_path = PathBuf::from(manifest_dir).parent().unwrap().join("uapi"); - uapi_dep.insert( - "path".to_string(), - toml::Value::String(uapi_path.to_str().unwrap().to_string()), - ); + uapi_dep + .insert("path".to_string(), toml::Value::String(uapi_path.to_str().unwrap().to_string())); // Set binary targets cargo_toml["bin"] = toml::Value::Array( @@ -73,6 +148,71 @@ pub fn create_cargo_toml( Ok(()) } +/// Create a `Cargo.toml` to compile the given Rust contract entries. +/// This version uses cargo metadata to resolve the uapi dependency. +pub fn create_cargo_toml<'a>( + fixtures_dir: &Path, + entries: impl Iterator, + output_dir: &Path, +) -> Result<()> { + let mut cargo_toml: toml::Value = toml::from_str(include_str!("../build/_Cargo.toml"))?; + let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); + + let manifest_path = fixtures_dir.join("Cargo.toml"); + let metadata = MetadataCommand::new().manifest_path(&manifest_path).exec().unwrap(); + let dependency_graph = metadata.resolve.unwrap(); + + // Resolve the pallet-revive-fixtures package id + let fixtures_pkg_id = metadata + .packages + .iter() + .find(|pkg| pkg.manifest_path.as_std_path() == manifest_path) + .map(|pkg| pkg.id.clone()) + .unwrap(); + let fixtures_pkg_node = + dependency_graph.nodes.iter().find(|node| node.id == fixtures_pkg_id).unwrap(); + + // Get the pallet-revive-uapi package id + let uapi_pkg_id = fixtures_pkg_node + .deps + .iter() + .find(|dep| dep.name == "pallet_revive_uapi") + .map(|dep| dep.pkg.clone()) + .expect("pallet-revive-uapi is a build dependency of pallet-revive-fixtures; qed"); + + // Get pallet-revive-uapi package + let uapi_pkg = metadata.packages.iter().find(|pkg| pkg.id == uapi_pkg_id).unwrap(); + + if uapi_pkg.source.is_none() { + uapi_dep.insert( + "path".to_string(), + toml::Value::String( + fixtures_dir.join("../uapi").canonicalize()?.to_str().unwrap().to_string(), + ), + ); + } else { + uapi_dep.insert("version".to_string(), toml::Value::String(uapi_pkg.version.to_string())); + } + + cargo_toml["bin"] = toml::Value::Array( + entries + .map(|entry| { + let name = entry.name(); + let path = entry.path(); + toml::Value::Table(toml::toml! { + name = name + path = path + }) + }) + .collect::>(), + ); + + let cargo_toml = toml::to_string_pretty(&cargo_toml)?; + fs::write(output_dir.join("Cargo.toml"), cargo_toml.clone()) + .with_context(|| format!("Failed to write {cargo_toml:?}"))?; + Ok(()) +} + /// Invoke cargo build to compile contracts to RISC-V ELF. pub fn invoke_build(current_dir: &Path) -> Result<()> { let encoded_rustflags = ["-Dwarnings"].join("\x1f"); @@ -120,7 +260,7 @@ pub fn compile_rust_to_elf( ) -> Result { // Create Cargo.toml with single entry let entry = BuildEntry::new(contract_name, contract_path.to_str().unwrap()); - create_cargo_toml(&[entry], output_dir)?; + create_cargo_toml_simple(&[entry], output_dir)?; // Build invoke_build(output_dir)?; @@ -148,7 +288,9 @@ pub fn link_elf_to_polkavm(elf_path: &Path) -> Result> { polkavm_linker::TargetInstructionSet::ReviveV1, &elf_bytes, ) - .map_err(|err| anyhow::anyhow!("Failed to link polkavm program from {:?}: {}", elf_path, err))?; + .map_err(|err| { + anyhow::anyhow!("Failed to link polkavm program from {:?}: {}", elf_path, err) + })?; Ok(linked) } @@ -162,3 +304,253 @@ pub fn compile_rust_to_polkavm( let elf_path = compile_rust_to_elf(contract_path, contract_name, temp_dir)?; link_elf_to_polkavm(&elf_path) } + +/// Post-process the compiled code. +pub fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { + let strip = env::var("PALLET_REVIVE_FIXTURES_STRIP").map_or(false, |value| value == "1"); + let optimize = env::var("PALLET_REVIVE_FIXTURES_OPTIMIZE").map_or(true, |value| value == "1"); + + let mut config = polkavm_linker::Config::default(); + config.set_strip(strip); + config.set_optimize(optimize); + let orig = fs::read(input_path).with_context(|| format!("Failed to read {input_path:?}"))?; + let linked = polkavm_linker::program_from_elf( + config, + polkavm_linker::TargetInstructionSet::ReviveV1, + orig.as_ref(), + ) + .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; + fs::write(output_path, linked).with_context(|| format!("Failed to write {output_path:?}"))?; + Ok(()) +} + +/// Compile a Solidity contract using standard JSON interface. +fn compile_with_standard_json( + compiler: &str, + contracts_dir: &Path, + solidity_entries: &[&Entry], +) -> Result { + let mut input_json = serde_json::json!({ + "language": "Solidity", + "sources": {}, + "settings": { + "optimizer": { + "enabled": false, + "runs": 200 + }, + "outputSelection": + + serde_json::json!({ + "*": { + "*": ["evm.bytecode", "evm.deployedBytecode"] + } + }), + + } + }); + + // Add all Solidity files to the input + for entry in solidity_entries { + let source_code = fs::read_to_string(entry.path()) + .with_context(|| format!("Failed to read Solidity source: {}", entry.path()))?; + + let file_key = entry.path().split('/').last().unwrap_or(entry.name()); + input_json["sources"][file_key] = serde_json::json!({ + "content": source_code + }); + } + + let compiler_output = Command::new(compiler) + .current_dir(contracts_dir) + .arg("--standard-json") + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .with_context(|| { + format!( + "Failed to execute {compiler}. Make sure {compiler} is installed or \ + set env variable `SKIP_PALLET_REVIVE_FIXTURES=1` to skip fixtures compilation." + ) + })?; + + let mut stdin = compiler_output.stdin.as_ref().unwrap(); + stdin + .write_all(input_json.to_string().as_bytes()) + .with_context(|| format!("Failed to write to {} stdin", compiler))?; + let _ = stdin; + + let compiler_result = compiler_output + .wait_with_output() + .with_context(|| format!("Failed to wait for {} output", compiler))?; + + if !compiler_result.status.success() { + let stderr = String::from_utf8_lossy(&compiler_result.stderr); + bail!("{} compilation failed: {}", compiler, stderr); + } + + // Parse JSON output + let compiler_json: serde_json::Value = serde_json::from_slice(&compiler_result.stdout) + .with_context(|| format!("Failed to parse {} JSON output", compiler))?; + + // Abort on errors + if let Some(errors) = compiler_json.get("errors") { + if errors + .as_array() + .unwrap() + .iter() + .any(|object| object.get("severity").unwrap().as_str().unwrap() == "error") + { + bail!( + "failed to compile the Solidity fixtures: {}", + serde_json::to_string_pretty(errors)? + ); + } + } + + Ok(compiler_json) +} + +/// Extract bytecode from compiler JSON output and write binary files. +fn extract_and_write_bytecode( + compiler_json: &serde_json::Value, + out_dir: &Path, + file_suffix: &str, + bytecode_type: EvmByteCodeType, +) -> Result<()> { + if let Some(contracts) = compiler_json["contracts"].as_object() { + for (_file_key, file_contracts) in contracts { + if let Some(contract_map) = file_contracts.as_object() { + for (contract_name, contract_data) in contract_map { + // Navigate through the JSON path to find the bytecode + let mut current = contract_data; + for path_segment in ["evm", bytecode_type.json_key(), "object"] { + if let Some(next) = current.get(path_segment) { + current = next; + } else { + // Skip if path doesn't exist (e.g., contract has no bytecode) + continue; + } + } + + if let Some(bytecode_obj) = current.as_str() { + let bytecode_hex = bytecode_obj.strip_prefix("0x").unwrap_or(bytecode_obj); + let binary_content = hex::decode(bytecode_hex).map_err(|e| { + anyhow::anyhow!("Failed to decode hex for {contract_name}: {e}") + })?; + + let out_path = out_dir.join(format!("{}{}", contract_name, file_suffix)); + fs::write(&out_path, binary_content).with_context(|| { + format!("Failed to write {out_path:?} for {contract_name}") + })?; + } + } + } + } + } + Ok(()) +} + +/// Compile Solidity contracts using both solc and resolc. +pub fn compile_solidity_contracts( + contracts_dir: &Path, + out_dir: &Path, + entries: &[Entry], +) -> Result<()> { + let solidity_entries: Vec<_> = entries + .iter() + .filter(|entry| matches!(entry.contract_type, ContractType::Solidity)) + .collect(); + + if solidity_entries.is_empty() { + return Ok(()); + } + + let evm_only = vec!["HostEvmOnly"]; + let solidity_entries_pvm: Vec<_> = solidity_entries + .iter() + .cloned() + .filter(|entry| !evm_only.contains(&entry.path.file_stem().unwrap().to_str().unwrap())) + .collect(); + + // Compile with solc for EVM bytecode + let json = compile_with_standard_json("solc", contracts_dir, &solidity_entries)?; + extract_and_write_bytecode(&json, out_dir, ".sol.bin", EvmByteCodeType::InitCode)?; + extract_and_write_bytecode(&json, out_dir, ".sol.runtime.bin", EvmByteCodeType::RuntimeCode)?; + + // Compile with resolc for PVM bytecode + let json = compile_with_standard_json("resolc", contracts_dir, &solidity_entries_pvm)?; + extract_and_write_bytecode(&json, out_dir, ".resolc.polkavm", EvmByteCodeType::InitCode)?; + + Ok(()) +} + +/// Write the compiled Rust contracts to the given output directory. +pub fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { + for entry in entries { + if matches!(entry.contract_type, ContractType::Rust) { + post_process( + &build_dir + .join("target/riscv64emac-unknown-none-polkavm/release") + .join(entry.name()), + &out_dir.join(entry.out_filename()), + )?; + } + } + + Ok(()) +} + +/// Generate the fixture_location.rs file with macros and sol! definitions. +pub fn generate_fixture_location(temp_dir: &Path, out_dir: &Path, entries: &[Entry]) -> Result<()> { + let mut file = fs::File::create(temp_dir.join("fixture_location.rs")) + .context("Failed to create fixture_location.rs")?; + + let (fixtures, fixtures_resolc) = if env::var("SKIP_PALLET_REVIVE_FIXTURES").is_err() { + ( + format!( + r#"Some(include_bytes!(concat!("{}", "/", $name, ".polkavm")))"#, + out_dir.display() + ), + format!( + r#"Some(include_bytes!(concat!("{}", "/", $name, ".resolc.polkavm")))"#, + out_dir.display() + ), + ) + } else { + ("None".into(), "None".into()) + }; + + write!( + file, + r#" + #[allow(dead_code)] + const FIXTURE_DIR: &str = "{0}"; + + #[macro_export] + macro_rules! fixture {{ + ($name: literal) => {{ + {fixtures} + }}; + }} + + #[macro_export] + macro_rules! fixture_resolc {{ + ($name: literal) => {{ + {fixtures_resolc} + }}; + }} + "#, + out_dir.display() + ) + .context("Failed to write to fixture_location.rs")?; + + // Generate sol! macros for Solidity contracts + for entry in entries.iter().filter(|e| matches!(e.contract_type, ContractType::Solidity)) { + let relative_path = format!("contracts/{}", entry.path().split('/').last().unwrap()); + writeln!(file, r#"#[cfg(feature = "std")] alloy_core::sol!("{}");"#, relative_path) + .context("Failed to write sol! macro to fixture_location.rs")?; + } + + Ok(()) +} From 8b987c6549ab5cd7bb5ee9635255f024e3fdd9a3 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:49:13 +0100 Subject: [PATCH 14/23] pallet-revive-fixtures: cleanup 2 --- substrate/frame/revive/fixtures/build.rs | 2 +- .../frame/revive/fixtures/src/builder.rs | 136 ++++++++---------- 2 files changed, 57 insertions(+), 81 deletions(-) diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index 6fa1488d38b3f..2bc55f25b6673 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -71,7 +71,7 @@ pub fn main() -> Result<()> { .filter(|e| matches!(e.contract_type, ContractType::Rust)) .collect(); if !rust_entries.is_empty() { - create_cargo_toml(&fixtures_dir, rust_entries.into_iter(), &out_build_dir)?; + create_cargo_toml(Some(&fixtures_dir), rust_entries.into_iter(), &out_build_dir)?; invoke_build(&out_build_dir)?; write_output(&out_build_dir, &out_fixtures_dir, entries.clone())?; } diff --git a/substrate/frame/revive/fixtures/src/builder.rs b/substrate/frame/revive/fixtures/src/builder.rs index 54b41532e97fc..8f44b644ebc43 100644 --- a/substrate/frame/revive/fixtures/src/builder.rs +++ b/substrate/frame/revive/fixtures/src/builder.rs @@ -88,18 +88,6 @@ impl Entry { } } -/// Entry representing a contract to build. -pub struct BuildEntry { - pub name: String, - pub path: String, -} - -impl BuildEntry { - pub fn new(name: impl Into, path: impl Into) -> Self { - Self { name: name.into(), path: path.into() } - } -} - /// Collect all contract entries from the given source directory. pub fn collect_entries(contracts_dir: &Path) -> Vec { fs::read_dir(contracts_dir) @@ -117,83 +105,67 @@ pub fn collect_entries(contracts_dir: &Path) -> Vec { .collect::>() } -/// Create a Cargo.toml for building contracts using the template from build/_Cargo.toml. -/// This is a simple version that uses a hardcoded path for uapi dependency. -pub fn create_cargo_toml_simple(entries: &[BuildEntry], output_dir: &Path) -> Result<()> { - let mut cargo_toml: toml::Value = toml::from_str(include_str!("../build/_Cargo.toml"))?; - - // Set uapi dependency path - let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let uapi_path = PathBuf::from(manifest_dir).parent().unwrap().join("uapi"); - uapi_dep - .insert("path".to_string(), toml::Value::String(uapi_path.to_str().unwrap().to_string())); - - // Set binary targets - cargo_toml["bin"] = toml::Value::Array( - entries - .iter() - .map(|entry| { - let mut table = toml::map::Map::new(); - table.insert("name".to_string(), toml::Value::String(entry.name.clone())); - table.insert("path".to_string(), toml::Value::String(entry.path.clone())); - toml::Value::Table(table) - }) - .collect::>(), - ); - - let cargo_toml_str = toml::to_string_pretty(&cargo_toml)?; - fs::write(output_dir.join("Cargo.toml"), cargo_toml_str) - .with_context(|| format!("Failed to write Cargo.toml to {:?}", output_dir))?; - Ok(()) -} - /// Create a `Cargo.toml` to compile the given Rust contract entries. -/// This version uses cargo metadata to resolve the uapi dependency. +/// If fixtures_dir is provided, uses cargo metadata to resolve the uapi dependency. +/// Otherwise, uses a hardcoded path relative to CARGO_MANIFEST_DIR. pub fn create_cargo_toml<'a>( - fixtures_dir: &Path, + fixtures_dir: Option<&Path>, entries: impl Iterator, output_dir: &Path, ) -> Result<()> { let mut cargo_toml: toml::Value = toml::from_str(include_str!("../build/_Cargo.toml"))?; let uapi_dep = cargo_toml["dependencies"]["uapi"].as_table_mut().unwrap(); - let manifest_path = fixtures_dir.join("Cargo.toml"); - let metadata = MetadataCommand::new().manifest_path(&manifest_path).exec().unwrap(); - let dependency_graph = metadata.resolve.unwrap(); - - // Resolve the pallet-revive-fixtures package id - let fixtures_pkg_id = metadata - .packages - .iter() - .find(|pkg| pkg.manifest_path.as_std_path() == manifest_path) - .map(|pkg| pkg.id.clone()) - .unwrap(); - let fixtures_pkg_node = - dependency_graph.nodes.iter().find(|node| node.id == fixtures_pkg_id).unwrap(); - - // Get the pallet-revive-uapi package id - let uapi_pkg_id = fixtures_pkg_node - .deps - .iter() - .find(|dep| dep.name == "pallet_revive_uapi") - .map(|dep| dep.pkg.clone()) - .expect("pallet-revive-uapi is a build dependency of pallet-revive-fixtures; qed"); - - // Get pallet-revive-uapi package - let uapi_pkg = metadata.packages.iter().find(|pkg| pkg.id == uapi_pkg_id).unwrap(); - - if uapi_pkg.source.is_none() { + // Set uapi dependency path + if let Some(fixtures_dir) = fixtures_dir { + // Use cargo metadata to resolve the uapi dependency + let manifest_path = fixtures_dir.join("Cargo.toml"); + let metadata = MetadataCommand::new().manifest_path(&manifest_path).exec().unwrap(); + let dependency_graph = metadata.resolve.unwrap(); + + // Resolve the pallet-revive-fixtures package id + let fixtures_pkg_id = metadata + .packages + .iter() + .find(|pkg| pkg.manifest_path.as_std_path() == manifest_path) + .map(|pkg| pkg.id.clone()) + .unwrap(); + let fixtures_pkg_node = + dependency_graph.nodes.iter().find(|node| node.id == fixtures_pkg_id).unwrap(); + + // Get the pallet-revive-uapi package id + let uapi_pkg_id = fixtures_pkg_node + .deps + .iter() + .find(|dep| dep.name == "pallet_revive_uapi") + .map(|dep| dep.pkg.clone()) + .expect("pallet-revive-uapi is a build dependency of pallet-revive-fixtures; qed"); + + // Get pallet-revive-uapi package + let uapi_pkg = metadata.packages.iter().find(|pkg| pkg.id == uapi_pkg_id).unwrap(); + + if uapi_pkg.source.is_none() { + uapi_dep.insert( + "path".to_string(), + toml::Value::String( + fixtures_dir.join("../uapi").canonicalize()?.to_str().unwrap().to_string(), + ), + ); + } else { + uapi_dep + .insert("version".to_string(), toml::Value::String(uapi_pkg.version.to_string())); + } + } else { + // Use simple hardcoded path + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let uapi_path = PathBuf::from(manifest_dir).parent().unwrap().join("uapi"); uapi_dep.insert( "path".to_string(), - toml::Value::String( - fixtures_dir.join("../uapi").canonicalize()?.to_str().unwrap().to_string(), - ), + toml::Value::String(uapi_path.to_str().unwrap().to_string()), ); - } else { - uapi_dep.insert("version".to_string(), toml::Value::String(uapi_pkg.version.to_string())); } + // Set binary targets cargo_toml["bin"] = toml::Value::Array( entries .map(|entry| { @@ -252,15 +224,16 @@ pub fn invoke_build(current_dir: &Path) -> Result<()> { Ok(()) } +#[allow(dead_code)] /// Compile a Rust contract source to RISC-V ELF. -pub fn compile_rust_to_elf( +fn compile_rust_to_elf( contract_path: &Path, contract_name: &str, output_dir: &Path, ) -> Result { // Create Cargo.toml with single entry - let entry = BuildEntry::new(contract_name, contract_path.to_str().unwrap()); - create_cargo_toml_simple(&[entry], output_dir)?; + let entry = Entry::new(contract_path.to_path_buf(), ContractType::Rust); + create_cargo_toml(None, std::iter::once(&entry), output_dir)?; // Build invoke_build(output_dir)?; @@ -277,8 +250,9 @@ pub fn compile_rust_to_elf( Ok(elf_path) } +#[allow(dead_code)] /// Link a RISC-V ELF to PolkaVM bytecode. -pub fn link_elf_to_polkavm(elf_path: &Path) -> Result> { +fn link_elf_to_polkavm(elf_path: &Path) -> Result> { let elf_bytes = std::fs::read(elf_path) .with_context(|| format!("Failed to read ELF from {:?}", elf_path))?; @@ -295,6 +269,8 @@ pub fn link_elf_to_polkavm(elf_path: &Path) -> Result> { Ok(linked) } +// dead_code - it is used by test code only +#[allow(dead_code)] /// Compile a Rust contract source all the way to PolkaVM bytecode. pub fn compile_rust_to_polkavm( contract_path: &Path, From e2acf38e35a121830eb643efffef5fc23aca55dc Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:11:50 +0100 Subject: [PATCH 15/23] fix prdoc --- prdoc/pr_10385.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 6b5aeae19356a..5a52f06f770a4 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -17,7 +17,7 @@ crates: - name: pallet-revive bump: major - name: pallet-revive-fixtures - bump: patch + bump: minor - name: pallet-revive-uapi bump: patch - name: substrate-wasm-builder From 4b2689e71c2d4c1d2ceffa82b2c162cd8ae81e03 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:36:22 +0100 Subject: [PATCH 16/23] pallet-revive-fixtures: cleanup after merging master --- substrate/frame/revive/fixtures/build.rs | 11 ++----- .../frame/revive/fixtures/src/builder.rs | 29 ++++++++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index 2bc55f25b6673..36fda6beb37e7 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -23,18 +23,11 @@ mod builder; use anyhow::{Context, Result}; use builder::{ collect_entries, compile_solidity_contracts, create_cargo_toml, generate_fixture_location, - invoke_build, write_output, ContractType, + invoke_build, write_output, ContractType, OVERRIDE_OPTIMIZE_ENV_VAR, + OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR, OVERRIDE_STRIP_ENV_VAR, SKIP_PALLET_REVIVE_FIXTURES, }; use std::{env, fs, path::PathBuf}; -const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN"; -const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP"; -const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE"; -/// Do not build the fixtures, they will resolve to `None`. -/// -/// Depending on the usage, they will probably panic at runtime. -const SKIP_PALLET_REVIVE_FIXTURES: &str = "SKIP_PALLET_REVIVE_FIXTURES"; - pub fn main() -> Result<()> { // input pathes let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); diff --git a/substrate/frame/revive/fixtures/src/builder.rs b/substrate/frame/revive/fixtures/src/builder.rs index 3a6dd67f7541c..f7dc8a94c126b 100644 --- a/substrate/frame/revive/fixtures/src/builder.rs +++ b/substrate/frame/revive/fixtures/src/builder.rs @@ -29,6 +29,14 @@ use std::{ process::Command, }; +pub const OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN"; +pub const OVERRIDE_STRIP_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_STRIP"; +pub const OVERRIDE_OPTIMIZE_ENV_VAR: &str = "PALLET_REVIVE_FIXTURES_OPTIMIZE"; +/// Do not build the fixtures, they will resolve to `None`. +/// +/// Depending on the usage, they will probably panic at runtime. +pub const SKIP_PALLET_REVIVE_FIXTURES: &str = "SKIP_PALLET_REVIVE_FIXTURES"; + /// A contract entry. #[derive(Clone)] pub struct Entry { @@ -210,7 +218,7 @@ pub fn invoke_build(current_dir: &Path) -> Result<()> { .arg("--target") .arg(polkavm_linker::target_json_path(args).unwrap()); - if let Ok(toolchain) = env::var("PALLET_REVIVE_FIXTURES_RUSTUP_TOOLCHAIN") { + if let Ok(toolchain) = env::var(OVERRIDE_RUSTUP_TOOLCHAIN_ENV_VAR) { build_command.env("RUSTUP_TOOLCHAIN", &toolchain); } @@ -284,8 +292,8 @@ pub fn compile_rust_to_polkavm( /// Post-process the compiled code. pub fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { - let strip = env::var("PALLET_REVIVE_FIXTURES_STRIP").map_or(false, |value| value == "1"); - let optimize = env::var("PALLET_REVIVE_FIXTURES_OPTIMIZE").map_or(true, |value| value == "1"); + let strip = env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1"); + let optimize = env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1"); let mut config = polkavm_linker::Config::default(); config.set_strip(strip); @@ -318,14 +326,11 @@ fn compile_with_standard_json( "runs": 200 }, "remappings": remappings, - "outputSelection": - - serde_json::json!({ - "*": { - "*": ["evm.bytecode", "evm.deployedBytecode"] - } - }), - + "outputSelection": serde_json::json!({ + "*": { + "*": ["evm.bytecode", "evm.deployedBytecode"] + } + }), } }); @@ -342,6 +347,8 @@ fn compile_with_standard_json( let compiler_output = Command::new(compiler) .current_dir(contracts_dir) + .arg("--allow-paths") + .arg(INTERFACE_DIR) .arg("--standard-json") .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) From d3d11043286e339d4d46128b7e815c99c47c35d5 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:03:17 +0100 Subject: [PATCH 17/23] pallet-revive-fixtures: clippy --- substrate/frame/revive/fixtures/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index b5780c111ed58..b820f2ea52f46 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -20,6 +20,7 @@ alloy-core = { workspace = true, default-features = true, features = ["sol-types anyhow = { workspace = true, default-features = true, optional = true } cargo_metadata = { workspace = true, optional = true } hex = { workspace = true, features = ["alloc"], optional = true } +pallet-revive-uapi = { workspace = true, optional = true } polkavm-linker = { version = "0.30.0", optional = true } serde_json = { workspace = true, optional = true } sp-core = { workspace = true, default-features = true, optional = true } From c1f319068ff59fb181faaafd75870a417f408f88 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:40:57 +0100 Subject: [PATCH 18/23] fix build --- substrate/frame/revive/fixtures/Cargo.toml | 2 +- substrate/frame/revive/fixtures/src/builder.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index b820f2ea52f46..022e782b372d9 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -20,7 +20,7 @@ alloy-core = { workspace = true, default-features = true, features = ["sol-types anyhow = { workspace = true, default-features = true, optional = true } cargo_metadata = { workspace = true, optional = true } hex = { workspace = true, features = ["alloc"], optional = true } -pallet-revive-uapi = { workspace = true, optional = true } +pallet-revive-uapi = { workspace = true } polkavm-linker = { version = "0.30.0", optional = true } serde_json = { workspace = true, optional = true } sp-core = { workspace = true, default-features = true, optional = true } diff --git a/substrate/frame/revive/fixtures/src/builder.rs b/substrate/frame/revive/fixtures/src/builder.rs index f7dc8a94c126b..ffe7fcca6b88c 100644 --- a/substrate/frame/revive/fixtures/src/builder.rs +++ b/substrate/frame/revive/fixtures/src/builder.rs @@ -19,7 +19,6 @@ //! Used by both build.rs and test code. use anyhow::{bail, Context, Result}; -#[cfg(feature = "std")] use cargo_metadata::MetadataCommand; use pallet_revive_uapi::precompiles::INTERFACE_DIR; use std::{ From be83ba26bde33d371be72e214ac015ec0bb30304 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:00:24 +0100 Subject: [PATCH 19/23] fix prdoc --- prdoc/pr_10385.prdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 5a52f06f770a4..8ca75fb7323a1 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -22,3 +22,7 @@ crates: bump: patch - name: substrate-wasm-builder bump: minor +- name: sc-executor-common + bump: minor +- name: sc-executor-polkavm + bump: minor From fd2c14e972dd27031441af6e76f4afdb573b92f9 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:47:14 +0100 Subject: [PATCH 20/23] fix prdoc --- prdoc/pr_10385.prdoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 8ca75fb7323a1..76335ddd6e0b1 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -21,8 +21,6 @@ crates: - name: pallet-revive-uapi bump: patch - name: substrate-wasm-builder - bump: minor + bump: major - name: sc-executor-common bump: minor -- name: sc-executor-polkavm - bump: minor From 50ea4d27c3e51df8e99be697e4f990da5cb564ea Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:23:00 +0100 Subject: [PATCH 21/23] fix prdoc --- prdoc/pr_10385.prdoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prdoc/pr_10385.prdoc b/prdoc/pr_10385.prdoc index 76335ddd6e0b1..4f42c5ac6a691 100644 --- a/prdoc/pr_10385.prdoc +++ b/prdoc/pr_10385.prdoc @@ -21,6 +21,8 @@ crates: - name: pallet-revive-uapi bump: patch - name: substrate-wasm-builder - bump: major + bump: minor - name: sc-executor-common + bump: major +- name: sc-executor-polkavm bump: minor From 519114e156ed3f1360464f6e952726aac29be45c Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:43:54 +0100 Subject: [PATCH 22/23] pallet-revive-fixtures: bump polkavm in _Cargo.toml template --- substrate/frame/revive/fixtures/build/_Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/revive/fixtures/build/_Cargo.toml b/substrate/frame/revive/fixtures/build/_Cargo.toml index af898cec525ef..955c1d29c8bf6 100644 --- a/substrate/frame/revive/fixtures/build/_Cargo.toml +++ b/substrate/frame/revive/fixtures/build/_Cargo.toml @@ -14,7 +14,7 @@ edition = "2021" [dependencies] uapi = { package = 'pallet-revive-uapi', features = ["unstable-hostfn"], default-features = false } hex-literal = { version = "0.4.1", default-features = false } -polkavm-derive = { version = "0.27.0" } +polkavm-derive = { version = "0.30.0" } [profile.release] opt-level = 3 From c0bd94e07b510ac61e5589cdaaa590d5e8eb5fa6 Mon Sep 17 00:00:00 2001 From: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:50:15 +0100 Subject: [PATCH 23/23] pallet-revive: add some comment --- substrate/frame/revive/src/limits.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index edac4e2cdf62e..6cc838576e81f 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -210,6 +210,8 @@ pub mod code { log::debug!(target: LOG_TARGET, "invalid instruction at offset {}", inst.offset); return Err(>::InvalidInstruction.into()) }, + // Since polkavm `0.30.0` linker will fail if it detects sbrk instruction. + // So this branch is never reached for programs built with polkavm >= 0.30.0. Instruction::sbrk(_, _) => { log::debug!(target: LOG_TARGET, "sbrk instruction is not allowed. offset {}", inst.offset); return Err(>::InvalidInstruction.into())