Add support for generating metadata from runtime wasm files (#1720)

closes #1660
This commit is contained in:
Pavlo Khrystenko
2024-09-02 11:07:50 +02:00
committed by GitHub
parent 290bee3486
commit 3866737b29
14 changed files with 1075 additions and 73 deletions
+38 -2
View File
@@ -23,6 +23,9 @@ use subxt_codegen::{
};
use syn::{parse_macro_input, punctuated::Punctuated};
#[cfg(feature = "runtime-path")]
mod wasm_loader;
#[derive(Clone, Debug)]
struct OuterAttribute(syn::Attribute);
@@ -60,6 +63,9 @@ struct RuntimeMetadataArgs {
no_default_substitutions: bool,
#[darling(default)]
unstable_metadata: darling::util::Flag,
#[cfg(feature = "runtime-path")]
#[darling(default)]
runtime_path: Option<String>,
}
#[derive(Debug, FromMeta)]
@@ -206,6 +212,22 @@ fn validate_type_path(path: &syn::Path, metadata: &Metadata) {
fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result<subxt_codegen::Metadata, TokenStream> {
// Do we want to fetch unstable metadata? This only works if fetching from a URL.
let unstable_metadata = args.unstable_metadata.is_present();
#[cfg(feature = "runtime-path")]
if let Some(path) = &args.runtime_path {
if args.runtime_metadata_insecure_url.is_some() || args.runtime_metadata_path.is_some() {
abort_call_site!(
"Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or `runtime_path` must be provided"
);
};
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
let root_path = std::path::Path::new(&root);
let path = root_path.join(path);
let metadata = wasm_loader::from_wasm_file(&path).map_err(|e| e.into_compile_error())?;
return Ok(metadata);
};
let metadata = match (
&args.runtime_metadata_path,
&args.runtime_metadata_insecure_url,
@@ -239,12 +261,26 @@ fn fetch_metadata(args: &RuntimeMetadataArgs) -> Result<subxt_codegen::Metadata,
.and_then(|b| subxt_codegen::Metadata::decode(&mut &*b).map_err(Into::into))
.map_err(|e| e.into_compile_error())?
}
#[cfg(feature = "runtime-path")]
(None, None) => {
abort_call_site!(
"One of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' must be provided"
"At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided"
)
}
#[cfg(not(feature = "runtime-path"))]
(None, None) => {
abort_call_site!(
"At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' can be provided"
)
}
(Some(_), Some(_)) => {
#[cfg(feature = "runtime-path")]
_ => {
abort_call_site!(
"Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided"
)
}
#[cfg(not(feature = "runtime-path"))]
_ => {
abort_call_site!(
"Only one of 'runtime_metadata_path' or 'runtime_metadata_insecure_url' can be provided"
)
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::{borrow::Cow, path::Path};
use codec::Decode;
use sc_executor::{WasmExecutionMethod, WasmExecutor};
use sc_executor_common::runtime_blob::RuntimeBlob;
use sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
use subxt_codegen::{fetch_metadata::fetch_metadata_from_file_blocking, CodegenError, Metadata};
/// Result type shorthand
pub type WasmMetadataResult<A> = Result<A, CodegenError>;
/// Uses wasm artifact produced by compiling the runtime to generate metadata
pub fn from_wasm_file(wasm_file_path: &Path) -> WasmMetadataResult<Metadata> {
let wasm_file = fetch_metadata_from_file_blocking(wasm_file_path)
.map_err(Into::<CodegenError>::into)
.and_then(maybe_decompress)?;
call_and_decode(wasm_file)
}
fn call_and_decode(wasm_file: Vec<u8>) -> WasmMetadataResult<Metadata> {
let mut ext: sp_state_machine::BasicExternalities = Default::default();
let executor: WasmExecutor<sp_io::SubstrateHostFunctions> = WasmExecutor::builder()
.with_execution_method(WasmExecutionMethod::default())
.with_offchain_heap_alloc_strategy(sc_executor::HeapAllocStrategy::Dynamic {
maximum_pages: Some(64),
})
.with_max_runtime_instances(1)
.with_runtime_cache_size(1)
.build();
let runtime_blob =
RuntimeBlob::new(&wasm_file).map_err(|e| CodegenError::Wasm(e.to_string()))?;
let metadata_encoded = executor
.uncached_call(runtime_blob, &mut ext, true, "Metadata_metadata", &[])
.map_err(|_| CodegenError::Wasm("method \"Metadata_metadata\" doesnt exist".to_owned()))?;
let metadata = <Vec<u8>>::decode(&mut &metadata_encoded[..]).map_err(CodegenError::Decode)?;
decode(metadata)
}
fn decode(encoded_metadata: Vec<u8>) -> WasmMetadataResult<Metadata> {
Metadata::decode(&mut encoded_metadata.as_ref()).map_err(Into::into)
}
fn maybe_decompress(file_contents: Vec<u8>) -> WasmMetadataResult<Vec<u8>> {
sp_maybe_compressed_blob::decompress(file_contents.as_ref(), CODE_BLOB_BOMB_LIMIT)
.map_err(|e| CodegenError::Wasm(e.to_string()))
.map(Cow::into_owned)
}