|
17 | 17 | //! Interface to the Substrate Executor |
18 | 18 |
|
19 | 19 | use polkadot_primitives::{ExecutorParam, ExecutorParams}; |
20 | | -use sc_executor_common::wasm_runtime::HeapAllocStrategy; |
21 | | -use sc_executor_wasmtime::{Config, DeterministicStackLimit, Semantics}; |
| 20 | +use sc_executor_common::{ |
| 21 | + error::WasmError, |
| 22 | + runtime_blob::RuntimeBlob, |
| 23 | + wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmModule as _}, |
| 24 | +}; |
| 25 | +use sc_executor_wasmtime::{Config, DeterministicStackLimit, Semantics, WasmtimeRuntime}; |
| 26 | +use sp_core::storage::{ChildInfo, TrackedStorageKey}; |
| 27 | +use sp_externalities::MultiRemovalResults; |
| 28 | +use std::any::{Any, TypeId}; |
22 | 29 |
|
23 | 30 | // Memory configuration |
24 | 31 | // |
@@ -112,3 +119,255 @@ pub fn params_to_wasmtime_semantics(par: &ExecutorParams) -> Result<Semantics, S |
112 | 119 | sem.deterministic_stack_limit = Some(stack_limit); |
113 | 120 | Ok(sem) |
114 | 121 | } |
| 122 | + |
| 123 | +/// A WASM executor with a given configuration. It is instantiated once per execute worker and is |
| 124 | +/// specific to that worker. |
| 125 | +#[derive(Clone)] |
| 126 | +pub struct Executor { |
| 127 | + config: Config, |
| 128 | +} |
| 129 | + |
| 130 | +impl Executor { |
| 131 | + pub fn new(params: ExecutorParams) -> Result<Self, String> { |
| 132 | + let mut config = DEFAULT_CONFIG.clone(); |
| 133 | + config.semantics = params_to_wasmtime_semantics(¶ms)?; |
| 134 | + |
| 135 | + Ok(Self { config }) |
| 136 | + } |
| 137 | + |
| 138 | + /// Executes the given PVF in the form of a compiled artifact and returns the result of execution |
| 139 | + /// upon success. |
| 140 | + /// |
| 141 | + /// # Safety |
| 142 | + /// |
| 143 | + /// The caller must ensure that the compiled artifact passed here was: |
| 144 | + /// 1) produced by [`prepare`], |
| 145 | + /// 2) was not modified, |
| 146 | + /// |
| 147 | + /// Failure to adhere to these requirements might lead to crashes and arbitrary code execution. |
| 148 | + pub unsafe fn execute( |
| 149 | + &self, |
| 150 | + compiled_artifact_blob: &[u8], |
| 151 | + params: &[u8], |
| 152 | + ) -> Result<Vec<u8>, String> { |
| 153 | + let mut extensions = sp_externalities::Extensions::new(); |
| 154 | + |
| 155 | + extensions.register(sp_core::traits::ReadRuntimeVersionExt::new(ReadRuntimeVersion)); |
| 156 | + |
| 157 | + let mut ext = ValidationExternalities(extensions); |
| 158 | + |
| 159 | + match sc_executor::with_externalities_safe(&mut ext, || { |
| 160 | + let runtime = self.create_runtime_from_bytes(compiled_artifact_blob)?; |
| 161 | + runtime.new_instance()?.call(InvokeMethod::Export("validate_block"), params) |
| 162 | + }) { |
| 163 | + Ok(Ok(ok)) => Ok(ok), |
| 164 | + Ok(Err(err)) | Err(err) => Err(err), |
| 165 | + } |
| 166 | + .map_err(|err| format!("execute error: {:?}", err)) |
| 167 | + } |
| 168 | + |
| 169 | + /// Constructs the runtime for the given PVF, given the artifact bytes. |
| 170 | + /// |
| 171 | + /// # Safety |
| 172 | + /// |
| 173 | + /// The caller must ensure that the compiled artifact passed here was: |
| 174 | + /// 1) produced by [`prepare`], |
| 175 | + /// 2) was not modified, |
| 176 | + /// |
| 177 | + /// Failure to adhere to these requirements might lead to crashes and arbitrary code execution. |
| 178 | + pub unsafe fn create_runtime_from_bytes( |
| 179 | + &self, |
| 180 | + compiled_artifact_blob: &[u8], |
| 181 | + ) -> Result<WasmtimeRuntime, WasmError> { |
| 182 | + sc_executor_wasmtime::create_runtime_from_artifact_bytes::<HostFunctions>( |
| 183 | + compiled_artifact_blob, |
| 184 | + self.config.clone(), |
| 185 | + ) |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +/// Available host functions. We leave out: |
| 190 | +/// |
| 191 | +/// 1. storage related stuff (PVF doesn't have a notion of a persistent storage/trie) |
| 192 | +/// 2. tracing |
| 193 | +/// 3. off chain workers (PVFs do not have such a notion) |
| 194 | +/// 4. runtime tasks |
| 195 | +/// 5. sandbox |
| 196 | +type HostFunctions = ( |
| 197 | + sp_io::misc::HostFunctions, |
| 198 | + sp_io::crypto::HostFunctions, |
| 199 | + sp_io::hashing::HostFunctions, |
| 200 | + sp_io::allocator::HostFunctions, |
| 201 | + sp_io::logging::HostFunctions, |
| 202 | + sp_io::trie::HostFunctions, |
| 203 | +); |
| 204 | + |
| 205 | +/// The validation externalities that will panic on any storage related access. (PVFs should not |
| 206 | +/// have a notion of a persistent storage/trie.) |
| 207 | +struct ValidationExternalities(sp_externalities::Extensions); |
| 208 | + |
| 209 | +impl sp_externalities::Externalities for ValidationExternalities { |
| 210 | + fn storage(&self, _: &[u8]) -> Option<Vec<u8>> { |
| 211 | + panic!("storage: unsupported feature for parachain validation") |
| 212 | + } |
| 213 | + |
| 214 | + fn storage_hash(&self, _: &[u8]) -> Option<Vec<u8>> { |
| 215 | + panic!("storage_hash: unsupported feature for parachain validation") |
| 216 | + } |
| 217 | + |
| 218 | + fn child_storage_hash(&self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> { |
| 219 | + panic!("child_storage_hash: unsupported feature for parachain validation") |
| 220 | + } |
| 221 | + |
| 222 | + fn child_storage(&self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> { |
| 223 | + panic!("child_storage: unsupported feature for parachain validation") |
| 224 | + } |
| 225 | + |
| 226 | + fn kill_child_storage( |
| 227 | + &mut self, |
| 228 | + _child_info: &ChildInfo, |
| 229 | + _maybe_limit: Option<u32>, |
| 230 | + _maybe_cursor: Option<&[u8]>, |
| 231 | + ) -> MultiRemovalResults { |
| 232 | + panic!("kill_child_storage: unsupported feature for parachain validation") |
| 233 | + } |
| 234 | + |
| 235 | + fn clear_prefix( |
| 236 | + &mut self, |
| 237 | + _prefix: &[u8], |
| 238 | + _maybe_limit: Option<u32>, |
| 239 | + _maybe_cursor: Option<&[u8]>, |
| 240 | + ) -> MultiRemovalResults { |
| 241 | + panic!("clear_prefix: unsupported feature for parachain validation") |
| 242 | + } |
| 243 | + |
| 244 | + fn clear_child_prefix( |
| 245 | + &mut self, |
| 246 | + _child_info: &ChildInfo, |
| 247 | + _prefix: &[u8], |
| 248 | + _maybe_limit: Option<u32>, |
| 249 | + _maybe_cursor: Option<&[u8]>, |
| 250 | + ) -> MultiRemovalResults { |
| 251 | + panic!("clear_child_prefix: unsupported feature for parachain validation") |
| 252 | + } |
| 253 | + |
| 254 | + fn place_storage(&mut self, _: Vec<u8>, _: Option<Vec<u8>>) { |
| 255 | + panic!("place_storage: unsupported feature for parachain validation") |
| 256 | + } |
| 257 | + |
| 258 | + fn place_child_storage(&mut self, _: &ChildInfo, _: Vec<u8>, _: Option<Vec<u8>>) { |
| 259 | + panic!("place_child_storage: unsupported feature for parachain validation") |
| 260 | + } |
| 261 | + |
| 262 | + fn storage_root(&mut self, _: sp_core::storage::StateVersion) -> Vec<u8> { |
| 263 | + panic!("storage_root: unsupported feature for parachain validation") |
| 264 | + } |
| 265 | + |
| 266 | + fn child_storage_root(&mut self, _: &ChildInfo, _: sp_core::storage::StateVersion) -> Vec<u8> { |
| 267 | + panic!("child_storage_root: unsupported feature for parachain validation") |
| 268 | + } |
| 269 | + |
| 270 | + fn next_child_storage_key(&self, _: &ChildInfo, _: &[u8]) -> Option<Vec<u8>> { |
| 271 | + panic!("next_child_storage_key: unsupported feature for parachain validation") |
| 272 | + } |
| 273 | + |
| 274 | + fn next_storage_key(&self, _: &[u8]) -> Option<Vec<u8>> { |
| 275 | + panic!("next_storage_key: unsupported feature for parachain validation") |
| 276 | + } |
| 277 | + |
| 278 | + fn storage_append(&mut self, _key: Vec<u8>, _value: Vec<u8>) { |
| 279 | + panic!("storage_append: unsupported feature for parachain validation") |
| 280 | + } |
| 281 | + |
| 282 | + fn storage_start_transaction(&mut self) { |
| 283 | + panic!("storage_start_transaction: unsupported feature for parachain validation") |
| 284 | + } |
| 285 | + |
| 286 | + fn storage_rollback_transaction(&mut self) -> Result<(), ()> { |
| 287 | + panic!("storage_rollback_transaction: unsupported feature for parachain validation") |
| 288 | + } |
| 289 | + |
| 290 | + fn storage_commit_transaction(&mut self) -> Result<(), ()> { |
| 291 | + panic!("storage_commit_transaction: unsupported feature for parachain validation") |
| 292 | + } |
| 293 | + |
| 294 | + fn wipe(&mut self) { |
| 295 | + panic!("wipe: unsupported feature for parachain validation") |
| 296 | + } |
| 297 | + |
| 298 | + fn commit(&mut self) { |
| 299 | + panic!("commit: unsupported feature for parachain validation") |
| 300 | + } |
| 301 | + |
| 302 | + fn read_write_count(&self) -> (u32, u32, u32, u32) { |
| 303 | + panic!("read_write_count: unsupported feature for parachain validation") |
| 304 | + } |
| 305 | + |
| 306 | + fn reset_read_write_count(&mut self) { |
| 307 | + panic!("reset_read_write_count: unsupported feature for parachain validation") |
| 308 | + } |
| 309 | + |
| 310 | + fn get_whitelist(&self) -> Vec<TrackedStorageKey> { |
| 311 | + panic!("get_whitelist: unsupported feature for parachain validation") |
| 312 | + } |
| 313 | + |
| 314 | + fn set_whitelist(&mut self, _: Vec<TrackedStorageKey>) { |
| 315 | + panic!("set_whitelist: unsupported feature for parachain validation") |
| 316 | + } |
| 317 | + |
| 318 | + fn set_offchain_storage(&mut self, _: &[u8], _: std::option::Option<&[u8]>) { |
| 319 | + panic!("set_offchain_storage: unsupported feature for parachain validation") |
| 320 | + } |
| 321 | + |
| 322 | + fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> { |
| 323 | + panic!("get_read_and_written_keys: unsupported feature for parachain validation") |
| 324 | + } |
| 325 | +} |
| 326 | + |
| 327 | +impl sp_externalities::ExtensionStore for ValidationExternalities { |
| 328 | + fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> { |
| 329 | + self.0.get_mut(type_id) |
| 330 | + } |
| 331 | + |
| 332 | + fn register_extension_with_type_id( |
| 333 | + &mut self, |
| 334 | + type_id: TypeId, |
| 335 | + extension: Box<dyn sp_externalities::Extension>, |
| 336 | + ) -> Result<(), sp_externalities::Error> { |
| 337 | + self.0.register_with_type_id(type_id, extension) |
| 338 | + } |
| 339 | + |
| 340 | + fn deregister_extension_by_type_id( |
| 341 | + &mut self, |
| 342 | + type_id: TypeId, |
| 343 | + ) -> Result<(), sp_externalities::Error> { |
| 344 | + if self.0.deregister(type_id) { |
| 345 | + Ok(()) |
| 346 | + } else { |
| 347 | + Err(sp_externalities::Error::ExtensionIsNotRegistered(type_id)) |
| 348 | + } |
| 349 | + } |
| 350 | +} |
| 351 | + |
| 352 | +struct ReadRuntimeVersion; |
| 353 | + |
| 354 | +impl sp_core::traits::ReadRuntimeVersion for ReadRuntimeVersion { |
| 355 | + fn read_runtime_version( |
| 356 | + &self, |
| 357 | + wasm_code: &[u8], |
| 358 | + _ext: &mut dyn sp_externalities::Externalities, |
| 359 | + ) -> Result<Vec<u8>, String> { |
| 360 | + let blob = RuntimeBlob::uncompress_if_needed(wasm_code) |
| 361 | + .map_err(|e| format!("Failed to read the PVF runtime blob: {:?}", e))?; |
| 362 | + |
| 363 | + match sc_executor::read_embedded_version(&blob) |
| 364 | + .map_err(|e| format!("Failed to read the static section from the PVF blob: {:?}", e))? |
| 365 | + { |
| 366 | + Some(version) => { |
| 367 | + use parity_scale_codec::Encode; |
| 368 | + Ok(version.encode()) |
| 369 | + }, |
| 370 | + None => Err("runtime version section is not found".to_string()), |
| 371 | + } |
| 372 | + } |
| 373 | +} |
0 commit comments