Skip to content

Commit

Permalink
Load latest metadata version from Wasm blobs. (#1859)
Browse files Browse the repository at this point in the history
* use "metadata_at_version"

closes #1858
  • Loading branch information
pkhry authored Nov 12, 2024
1 parent e502121 commit 362aea3
Showing 1 changed file with 116 additions and 19 deletions.
135 changes: 116 additions & 19 deletions macro/src/wasm_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::{borrow::Cow, path::Path};

use codec::Decode;
use codec::{Decode, Encode};
use polkadot_sdk::{
sc_executor::{self, WasmExecutionMethod, WasmExecutor},
sc_executor_common::runtime_blob::RuntimeBlob,
Expand All @@ -14,6 +14,8 @@ use polkadot_sdk::{
};
use subxt_codegen::{CodegenError, Metadata};

static SUPPORTED_METADATA_VERSIONS: [u32; 2] = [14, 15];

/// Result type shorthand
pub type WasmMetadataResult<A> = Result<A, CodegenError>;

Expand All @@ -26,26 +28,18 @@ pub fn from_wasm_file(wasm_file_path: &Path) -> WasmMetadataResult<Metadata> {
}

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 mut executor = Executor::new(&wasm_file)?;

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()))?;
if let Ok(versions) = executor.versions() {
let version = versions
.into_iter()
.max()
.expect("This is checked earlier and can't fail.");

let metadata = <Vec<u8>>::decode(&mut &metadata_encoded[..]).map_err(CodegenError::Decode)?;

decode(metadata)
executor.load_metadata_at_version(version)
} else {
executor.load_legacy_metadata()
}
}

fn decode(encoded_metadata: Vec<u8>) -> WasmMetadataResult<Metadata> {
Expand All @@ -57,3 +51,106 @@ fn maybe_decompress(file_contents: Vec<u8>) -> WasmMetadataResult<Vec<u8>> {
.map_err(|e| CodegenError::Wasm(e.to_string()))
.map(Cow::into_owned)
}

struct Executor {
runtime_blob: RuntimeBlob,
executor: WasmExecutor<sp_io::SubstrateHostFunctions>,
externalities: sp_state_machine::BasicExternalities,
}

impl Executor {
fn new(wasm_file: &[u8]) -> WasmMetadataResult<Self> {
let externalities: 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()))?;

Ok(Self {
runtime_blob,
executor,
externalities,
})
}

fn versions(&mut self) -> WasmMetadataResult<Vec<u32>> {
let version = self
.executor
.uncached_call(
self.runtime_blob.clone(),
&mut self.externalities,
true,
"Metadata_metadata_versions",
&[],
)
.map_err(|_| {
CodegenError::Wasm("method \"Metadata_metadata_versions\" doesnt exist".to_owned())
})?;
let versions = <Vec<u32>>::decode(&mut &version[..])
.map_err(CodegenError::Decode)
.map(|x| {
x.into_iter()
.filter(|version| SUPPORTED_METADATA_VERSIONS.contains(version))
.collect::<Vec<u32>>()
})?;

if versions.is_empty() {
return Err(CodegenError::Other(
"No supported metadata versions were returned".to_owned(),
));
}

Ok(versions)
}

fn load_legacy_metadata(&mut self) -> WasmMetadataResult<Metadata> {
let encoded_metadata = self
.executor
.uncached_call(
self.runtime_blob.clone(),
&mut self.externalities,
false,
"Metadata_metadata",
&[],
)
.map_err(|_| {
CodegenError::Wasm("method \"Metadata_metadata\" doesnt exist".to_owned())
})?;
let encoded_metadata =
<Vec<u8>>::decode(&mut &encoded_metadata[..]).map_err(CodegenError::Decode)?;
decode(encoded_metadata)
}

fn load_metadata_at_version(&mut self, version: u32) -> WasmMetadataResult<Metadata> {
let encoded_metadata = self
.executor
.uncached_call(
self.runtime_blob.clone(),
&mut self.externalities,
false,
"Metadata_metadata_at_version",
&version.encode(),
)
.map_err(|_| {
CodegenError::Wasm(
"method \"Metadata_metadata_at_version\" doesnt exist".to_owned(),
)
})?;
let Some(encoded_metadata) =
<Option<Vec<u8>>>::decode(&mut &encoded_metadata[..]).map_err(CodegenError::Decode)?
else {
return Err(CodegenError::Other(
format!("Received empty metadata at version: v{version}").to_owned(),
));
};
decode(encoded_metadata)
}
}

0 comments on commit 362aea3

Please sign in to comment.