diff --git a/macro/src/wasm_loader.rs b/macro/src/wasm_loader.rs index f8be106dce..24b261419e 100644 --- a/macro/src/wasm_loader.rs +++ b/macro/src/wasm_loader.rs @@ -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, @@ -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 = Result; @@ -26,26 +28,18 @@ pub fn from_wasm_file(wasm_file_path: &Path) -> WasmMetadataResult { } fn call_and_decode(wasm_file: Vec) -> WasmMetadataResult { - let mut ext: sp_state_machine::BasicExternalities = Default::default(); + let mut executor = Executor::new(&wasm_file)?; - let executor: WasmExecutor = 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(); + if let Ok(versions) = executor.versions() { + let version = versions + .into_iter() + .max() + .expect("This is checked earlier and can't fail."); - 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 = >::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) -> WasmMetadataResult { @@ -57,3 +51,106 @@ fn maybe_decompress(file_contents: Vec) -> WasmMetadataResult> { .map_err(|e| CodegenError::Wasm(e.to_string())) .map(Cow::into_owned) } + +struct Executor { + runtime_blob: RuntimeBlob, + executor: WasmExecutor, + externalities: sp_state_machine::BasicExternalities, +} + +impl Executor { + fn new(wasm_file: &[u8]) -> WasmMetadataResult { + let externalities: sp_state_machine::BasicExternalities = Default::default(); + + let executor: WasmExecutor = 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> { + 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 = >::decode(&mut &version[..]) + .map_err(CodegenError::Decode) + .map(|x| { + x.into_iter() + .filter(|version| SUPPORTED_METADATA_VERSIONS.contains(version)) + .collect::>() + })?; + + 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 { + 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 = + >::decode(&mut &encoded_metadata[..]).map_err(CodegenError::Decode)?; + decode(encoded_metadata) + } + + fn load_metadata_at_version(&mut self, version: u32) -> WasmMetadataResult { + 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) = + >>::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) + } +}