1use std::borrow::Cow;
2use std::path::{Path, PathBuf};
3
4use jrsonnet_evaluator::{
5 error::ErrorKind::RuntimeError, function::builtin, typed::Typed, val::ThunkValue, ObjValue,
6 Result, Val,
7};
8use jrsonnet_gcmodule::{Cc, Trace};
9use parity_scale_codec::Decode;
10use sc_executor::{RuntimeVersionOf, WasmExecutor};
11use sp_core::blake2_256;
12use sp_core::traits::{CodeExecutor, RuntimeCode, WrappedRuntimeCode};
13
14type HostFunctions = (
15 sp_io::SubstrateHostFunctions,
16 cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
17);
18
19use crate::Hex;
20
21#[derive(Trace)]
22pub struct RuntimeContainer {
23 #[trace(skip)]
24 code: WrappedRuntimeCode<'static>,
25 #[trace(skip)]
26 hash: [u8; 32],
27 #[trace(skip)]
28 executor: WasmExecutor<HostFunctions>,
29}
30
31#[derive(Typed)]
32pub struct RuntimeVersion {
33 spec_name: String,
34 spec_version: u32,
35 state_version: u8,
36 transaction_version: u32,
37}
38
39impl RuntimeContainer {
40 pub fn new(code: Vec<u8>, cache_path: Option<&Path>) -> Self {
41 let mut executor = <WasmExecutor<HostFunctions>>::builder()
42 .with_max_runtime_instances(1)
44 .with_allow_missing_host_functions(true);
46 if let Some(cache_path) = cache_path {
47 executor = executor.with_cache_path(cache_path);
48 };
49 let executor = executor.build();
50 Self {
51 hash: blake2_256(&code),
52 code: WrappedRuntimeCode(Cow::Owned(code)),
53 executor,
54 }
55 }
56 fn runtime_code(&self) -> RuntimeCode<'_> {
57 RuntimeCode {
58 code_fetcher: &self.code,
59 heap_pages: Some(100),
60 hash: self.hash.to_vec(),
61 }
62 }
63 pub fn version(&self) -> Result<RuntimeVersion> {
64 let mut ext = sp_state_machine::BasicExternalities::new_empty();
65 let version =
66 RuntimeVersionOf::runtime_version(&self.executor, &mut ext, &self.runtime_code())
67 .map_err(|e| RuntimeError(format!("{e}").into()))?;
68
69 Ok(RuntimeVersion {
70 spec_name: version.spec_name.to_string(),
71 spec_version: version.spec_version,
72 state_version: match version.state_version() {
73 sp_runtime::StateVersion::V0 => 0,
74 sp_runtime::StateVersion::V1 => 1,
75 },
76 transaction_version: version.transaction_version,
77 })
78 }
79 pub fn metadata(&self) -> Result<Vec<u8>> {
80 let mut ext = sp_state_machine::BasicExternalities::new_empty();
81 let (result, _native_used) = self.executor.call(
82 &mut ext,
83 &self.runtime_code(),
84 "Metadata_metadata",
85 &[],
86 sp_core::traits::CallContext::Onchain,
87 );
88 let result = result.expect("metadata is implemented for substrate chains");
89 let result = <Vec<u8>>::decode(&mut result.as_slice()).expect("valid output");
90 Ok(result)
91 }
92}
93
94#[builtin(fields(
95 cache_path: Option<PathBuf>,
96))]
97pub fn builtin_runtime_wasm(this: &builtin_runtime_wasm, data: Hex) -> Result<ObjValue> {
98 let runtime = Cc::new(RuntimeContainer::new(data.0, this.cache_path.as_deref()));
99
100 #[derive(Trace)]
101 struct RuntimeVersionThunk {
102 runtime: Cc<RuntimeContainer>,
103 }
104 impl ThunkValue for RuntimeVersionThunk {
105 type Output = Val;
106 fn get(self: Box<Self>) -> Result<Val> {
107 RuntimeVersion::into_untyped(self.runtime.version()?)
108 }
109 }
110 #[derive(Trace)]
111 struct MetadataThunk {
112 runtime: Cc<RuntimeContainer>,
113 }
114 impl ThunkValue for MetadataThunk {
115 type Output = Val;
116 fn get(self: Box<Self>) -> Result<Val> {
117 self.runtime.metadata().map(Hex).and_then(Hex::into_untyped)
118 }
119 }
120
121 let mut out = ObjValue::builder();
122
123 out.field("version").try_thunk(RuntimeVersionThunk {
124 runtime: runtime.clone(),
125 })?;
126 out.field("metadata").try_thunk(MetadataThunk {
127 runtime: runtime.clone(),
128 })?;
129
130 Ok(out.build())
131}