Skip to content

Commit 3866737

Browse files
authored
Add support for generating metadata from runtime wasm files (#1720)
closes #1660
1 parent 290bee3 commit 3866737

File tree

14 files changed

+1082
-80
lines changed

14 files changed

+1082
-80
lines changed

Cargo.lock

Lines changed: 936 additions & 75 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ which = "5.0.0"
116116
strip-ansi-escapes = "0.2.0"
117117
proptest = "1.5.0"
118118
hex-literal = "0.4.1"
119+
sc-executor = "0.40.0"
120+
sc-executor-common = "0.35.0"
119121

120122
# Light client support:
121123
smoldot = { version = "0.16.0", default-features = false }
@@ -140,6 +142,9 @@ sp-core = { version = "31.0.0", default-features = false }
140142
sp-crypto-hashing = { version = "0.1.0", default-features = false }
141143
sp-runtime = "34.0.0"
142144
sp-keyring = "34.0.0"
145+
sp-maybe-compressed-blob = "11.0.0"
146+
sp-state-machine = "0.43.0"
147+
sp-io = "38.0.0"
143148

144149
# Subxt workspace crates:
145150
subxt = { version = "0.37.0", path = "subxt", default-features = false }
@@ -167,6 +172,7 @@ base64 = { version = "0.22.1", default-features = false }
167172
scrypt = { version = "0.11.0", default-features = false }
168173
crypto_secretbox = { version = "0.1.1", default-features = false }
169174

175+
170176
[profile.dev.package.smoldot-light]
171177
opt-level = 2
172178
[profile.test.package.smoldot-light]
Binary file not shown.

artifacts/westend_runtime.wasm

9.37 MB
Binary file not shown.

codegen/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub enum CodegenError {
5757
/// Cannot generate types.
5858
#[error("Type Generation failed: {0}")]
5959
TypeGeneration(#[from] TypegenError),
60+
/// Error when generating metadata from Wasm-runtime
61+
#[error("Failed to generate metadata from wasm file. reason: {0}")]
62+
Wasm(String),
6063
}
6164

6265
impl CodegenError {

macro/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ description = "Generate types and helpers for interacting with Substrate runtime
1515

1616
[features]
1717
web = ["subxt-codegen/web"]
18+
runtime-path = ["sp-io", "sc-executor-common", "sp-state-machine", "sp-maybe-compressed-blob", "sc-executor"]
1819

1920
[lib]
2021
proc-macro = true
@@ -27,6 +28,11 @@ syn = { workspace = true }
2728
quote = { workspace = true }
2829
subxt-codegen = { workspace = true, features = ["fetch-metadata"] }
2930
scale-typegen = { workspace = true }
31+
sc-executor = { workspace = true, optional = true }
32+
sp-maybe-compressed-blob = { workspace = true, optional = true }
33+
sp-state-machine = { workspace = true, optional = true }
34+
sp-io = { workspace = true, optional = true }
35+
sc-executor-common = { workspace = true, optional = true }
3036

3137
[lints]
3238
workspace = true

macro/src/lib.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ use subxt_codegen::{
2323
};
2424
use syn::{parse_macro_input, punctuated::Punctuated};
2525

26+
#[cfg(feature = "runtime-path")]
27+
mod wasm_loader;
28+
2629
#[derive(Clone, Debug)]
2730
struct OuterAttribute(syn::Attribute);
2831

@@ -60,6 +63,9 @@ struct RuntimeMetadataArgs {
6063
no_default_substitutions: bool,
6164
#[darling(default)]
6265
unstable_metadata: darling::util::Flag,
66+
#[cfg(feature = "runtime-path")]
67+
#[darling(default)]
68+
runtime_path: Option<String>,
6369
}
6470

6571
#[derive(Debug, FromMeta)]
@@ -206,6 +212,22 @@ fn validate_type_path(path: &syn::Path, metadata: &Metadata) {
206212
fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result<subxt_codegen::Metadata, TokenStream> {
207213
// Do we want to fetch unstable metadata? This only works if fetching from a URL.
208214
let unstable_metadata = args.unstable_metadata.is_present();
215+
216+
#[cfg(feature = "runtime-path")]
217+
if let Some(path) = &args.runtime_path {
218+
if args.runtime_metadata_insecure_url.is_some() || args.runtime_metadata_path.is_some() {
219+
abort_call_site!(
220+
"Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or `runtime_path` must be provided"
221+
);
222+
};
223+
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
224+
let root_path = std::path::Path::new(&root);
225+
let path = root_path.join(path);
226+
227+
let metadata = wasm_loader::from_wasm_file(&path).map_err(|e| e.into_compile_error())?;
228+
return Ok(metadata);
229+
};
230+
209231
let metadata = match (
210232
&args.runtime_metadata_path,
211233
&args.runtime_metadata_insecure_url,
@@ -239,12 +261,26 @@ fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result<subxt_codegen::Metadata,
239261
.and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into))
240262
.map_err(|e| e.into_compile_error())?
241263
}
264+
#[cfg(feature = "runtime-path")]
242265
(None, None) => {
243266
abort_call_site!(
244-
"One of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' must be provided"
267+
"At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided"
268+
)
269+
}
270+
#[cfg(not(feature = "runtime-path"))]
271+
(None, None) => {
272+
abort_call_site!(
273+
"At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' can be provided"
245274
)
246275
}
247-
(Some(_), Some(_)) => {
276+
#[cfg(feature = "runtime-path")]
277+
_ => {
278+
abort_call_site!(
279+
"Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided"
280+
)
281+
}
282+
#[cfg(not(feature = "runtime-path"))]
283+
_ => {
248284
abort_call_site!(
249285
"Only one of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' can be provided"
250286
)

macro/src/wasm_loader.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2024 Parity Technologies (UK) Ltd.
2+
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3+
// see LICENSE for license details.
4+
5+
use std::{borrow::Cow, path::Path};
6+
7+
use codec::Decode;
8+
use sc_executor::{WasmExecutionMethod, WasmExecutor};
9+
use sc_executor_common::runtime_blob::RuntimeBlob;
10+
use sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
11+
use subxt_codegen::{fetch_metadata::fetch_metadata_from_file_blocking, CodegenError, Metadata};
12+
13+
/// Result type shorthand
14+
pub type WasmMetadataResult<A> = Result<A, CodegenError>;
15+
16+
/// Uses wasm artifact produced by compiling the runtime to generate metadata
17+
pub fn from_wasm_file(wasm_file_path: &Path) -> WasmMetadataResult<Metadata> {
18+
let wasm_file = fetch_metadata_from_file_blocking(wasm_file_path)
19+
.map_err(Into::<CodegenError>::into)
20+
.and_then(maybe_decompress)?;
21+
call_and_decode(wasm_file)
22+
}
23+
24+
fn call_and_decode(wasm_file: Vec<u8>) -> WasmMetadataResult<Metadata> {
25+
let mut ext: sp_state_machine::BasicExternalities = Default::default();
26+
27+
let executor: WasmExecutor<sp_io::SubstrateHostFunctions> = WasmExecutor::builder()
28+
.with_execution_method(WasmExecutionMethod::default())
29+
.with_offchain_heap_alloc_strategy(sc_executor::HeapAllocStrategy::Dynamic {
30+
maximum_pages: Some(64),
31+
})
32+
.with_max_runtime_instances(1)
33+
.with_runtime_cache_size(1)
34+
.build();
35+
36+
let runtime_blob =
37+
RuntimeBlob::new(&wasm_file).map_err(|e| CodegenError::Wasm(e.to_string()))?;
38+
let metadata_encoded = executor
39+
.uncached_call(runtime_blob, &mut ext, true, "Metadata_metadata", &[])
40+
.map_err(|_| CodegenError::Wasm("method \"Metadata_metadata\" doesnt exist".to_owned()))?;
41+
42+
let metadata = <Vec<u8>>::decode(&mut &metadata_encoded[..]).map_err(CodegenError::Decode)?;
43+
44+
decode(metadata)
45+
}
46+
47+
fn decode(encoded_metadata: Vec<u8>) -> WasmMetadataResult<Metadata> {
48+
Metadata::decode(&mut encoded_metadata.as_ref()).map_err(Into::into)
49+
}
50+
51+
fn maybe_decompress(file_contents: Vec<u8>) -> WasmMetadataResult<Vec<u8>> {
52+
sp_maybe_compressed_blob::decompress(file_contents.as_ref(), CODE_BLOB_BOMB_LIMIT)
53+
.map_err(|e| CodegenError::Wasm(e.to_string()))
54+
.map(Cow::into_owned)
55+
}

subxt/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ unstable-metadata = []
7070
# Note that this feature is experimental and things may break or not work as expected.
7171
unstable-light-client = ["subxt-lightclient"]
7272

73+
# Activate this to expose the ability to generate metadata from Wasm runtime files.
74+
runtime-path = ["subxt-macro/runtime-path"]
75+
7376
[dependencies]
7477
async-trait = { workspace = true }
7578
codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] }

testing/ui-tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ hex = { workspace = true }
1414
scale-info = { workspace = true, features = ["bit-vec"] }
1515
frame-metadata = { workspace = true }
1616
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
17-
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
17+
subxt = { workspace = true, features = ["native", "jsonrpsee", "runtime-path"] }
1818
subxt-metadata = { workspace = true }
1919
generate-custom-metadata = { path = "../generate-custom-metadata" }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#[subxt::subxt(runtime_path = "../../../../artifacts/westend_runtime.wasm")]
2+
mod runtime {}
3+
4+
#[subxt::subxt(runtime_path = "../../../../artifacts/westend_runtime.compact.compressed.wasm")]
5+
mod runtime_compressed {}
6+
7+
fn main() {
8+
use runtime;
9+
use runtime_compressed;
10+
11+
let _ = runtime::system::events::CodeUpdated;
12+
let _ = runtime_compressed::system::events::CodeUpdated;
13+
}

testing/ui-tests/src/incorrect/need_url_or_path.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: One of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' must be provided
1+
error: At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided
22
--> src/incorrect/need_url_or_path.rs:1:1
33
|
44
1 | #[subxt::subxt()]

testing/ui-tests/src/incorrect/url_and_path_provided.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,11 @@
44
)]
55
pub mod node_runtime {}
66

7+
#[subxt::subxt(
8+
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
9+
runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443",
10+
runtime_path = "../../../../artifacts/westend_runtime.wasm"
11+
)]
12+
pub mod node_runtime2 {}
13+
714
fn main() {}

testing/ui-tests/src/incorrect/url_and_path_provided.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: Only one of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' can be provided
1+
error: Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided
22
--> src/incorrect/url_and_path_provided.rs:1:1
33
|
44
1 | / #[subxt::subxt(
@@ -8,3 +8,15 @@ error: Only one of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' ca
88
| |__^
99
|
1010
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error: Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or `runtime_path` must be provided
13+
--> src/incorrect/url_and_path_provided.rs:7:1
14+
|
15+
7 | / #[subxt::subxt(
16+
8 | | runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
17+
9 | | runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443",
18+
10 | | runtime_path = "../../../../artifacts/westend_runtime.wasm"
19+
11 | | )]
20+
| |__^
21+
|
22+
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)