diff --git a/Cargo.lock b/Cargo.lock index 1905090baa10..b49b88a13676 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7502,6 +7502,7 @@ dependencies = [ "sp-core", "sp-externalities", "sp-io", + "sp-maybe-compressed-blob", "sp-panic-handler", "sp-runtime", "sp-runtime-interface", @@ -9477,10 +9478,12 @@ version = "3.0.0" dependencies = [ "impl-serde", "parity-scale-codec", + "parity-wasm 0.42.2", "serde", "sp-runtime", "sp-std", "sp-version-proc-macro", + "thiserror", ] [[package]] diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 27e90ddcc85e..1f10c65e12f9 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -46,6 +46,7 @@ substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" sp-state-machine = { version = "0.9.0", path = "../../primitives/state-machine" } sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" } sp-tracing = { version = "3.0.0", path = "../../primitives/tracing" } +sp-maybe-compressed-blob = { version = "3.0.0", path = "../../primitives/maybe-compressed-blob" } sc-tracing = { version = "3.0.0", path = "../tracing" } tracing = "0.1.25" tracing-subscriber = "0.2.18" diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index d01132da180a..73540aff0800 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -526,4 +526,35 @@ mod tests { let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(3, version.transaction_version); } + + #[test] + fn embed_runtime_version_works() { + let wasm = sp_maybe_compressed_blob::decompress( + substrate_test_runtime::wasm_binary_unwrap(), + sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT, + ).expect("Decompressing works"); + + let runtime_version = RuntimeVersion { + spec_name: "test_replace".into(), + impl_name: "test_replace".into(), + authoring_version: 100, + spec_version: 100, + impl_version: 100, + apis: sp_api::create_apis_vec!([(>::ID, 3)]), + transaction_version: 100, + }; + + let embedded = sp_version::embed::embed_runtime_version( + &wasm, + runtime_version.clone(), + ).expect("Embedding works"); + + let blob = RuntimeBlob::new(&embedded).expect("Embedded blob is valid"); + let read_version = read_embedded_version(&blob) + .ok() + .flatten() + .expect("Reading embedded version works"); + + assert_eq!(runtime_version, read_version); + } } diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index b50da9e9eacf..877897c54c24 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -21,6 +21,8 @@ codec = { package = "parity-scale-codec", version = "2.0.0", default-features = sp-std = { version = "3.0.0", default-features = false, path = "../std" } sp-runtime = { version = "3.0.0", default-features = false, path = "../runtime" } sp-version-proc-macro = { version = "3.0.0", default-features = false, path = "proc-macro" } +parity-wasm = { version = "0.42.2", optional = true } +thiserror = { version = "1.0.21", optional = true } [features] default = ["std"] @@ -30,4 +32,6 @@ std = [ "codec/std", "sp-std/std", "sp-runtime/std", + "parity-wasm", + "thiserror", ] diff --git a/primitives/version/src/embed.rs b/primitives/version/src/embed.rs new file mode 100644 index 000000000000..f32bc73d883a --- /dev/null +++ b/primitives/version/src/embed.rs @@ -0,0 +1,56 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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. + +//! Provides functionality to embed a [`RuntimeVersion`](crate::RuntimeVersion) as custom section +//! into a WASM file. + +use codec::Encode; +use parity_wasm::elements::{Module, deserialize_buffer, serialize}; + +#[derive(Clone, Copy, Eq, PartialEq, Debug, thiserror::Error)] +pub enum Error { + #[error("Deserializing wasm failed")] + Deserialize, + #[error("Serializing wasm failed")] + Serialize, +} + +/// Embed the given `version` to the given `wasm` blob. +/// +/// If there was already a runtime version embedded, this will be overwritten. +/// +/// Returns the new WASM blob. +pub fn embed_runtime_version( + wasm: &[u8], + mut version: crate::RuntimeVersion, +) -> Result, Error> { + let mut module: Module = deserialize_buffer(wasm).map_err(|_| Error::Deserialize)?; + + let apis = version.apis + .iter() + .map(Encode::encode) + .map(|v| v.into_iter()) + .flatten() + .collect::>(); + + module.set_custom_section("runtime_apis", apis); + + version.apis.to_mut().clear(); + module.set_custom_section("runtime_version", version.encode()); + + serialize(module).map_err(|_| Error::Serialize) +} diff --git a/primitives/version/src/lib.rs b/primitives/version/src/lib.rs index 15b4a128924f..aa7ae3da89d5 100644 --- a/primitives/version/src/lib.rs +++ b/primitives/version/src/lib.rs @@ -35,6 +35,9 @@ pub use sp_std; #[cfg(feature = "std")] use sp_runtime::{traits::Block as BlockT, generic::BlockId}; +#[cfg(feature = "std")] +pub mod embed; + /// An attribute that accepts a version declaration of a runtime and generates a custom wasm section /// with the equivalent contents. ///