referrerpolicy=no-referrer-when-downgrade

substrate_wasm_builder/
metadata_hash.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::builder::MetadataExtraInfo;
19use codec::{Decode, Encode};
20use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
21use merkleized_metadata::{generate_metadata_digest, ExtraInfo};
22use sc_executor::WasmExecutor;
23use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode, WrappedRuntimeCode};
24use std::path::Path;
25
26/// The host functions that we provide when calling into the wasm file.
27///
28/// Any other host function will return an error.
29type HostFunctions = (
30	// The allocator functions.
31	sp_io::allocator::HostFunctions,
32	// Logging is good to have for debugging issues.
33	sp_io::logging::HostFunctions,
34	// Give access to the "state", actually the state will be empty, but some chains put constants
35	// into the state and this would panic at metadata generation. Thus, we give them an empty
36	// state to not panic.
37	sp_io::storage::HostFunctions,
38	// The hashing functions.
39	sp_io::hashing::HostFunctions,
40);
41
42/// Generate the metadata hash.
43///
44/// The metadata hash is generated as specced in
45/// [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html).
46///
47/// Returns the metadata hash.
48pub fn generate_metadata_hash(wasm: &Path, extra_info: MetadataExtraInfo) -> [u8; 32] {
49	sp_tracing::try_init_simple();
50
51	let wasm = std::fs::read(wasm).expect("Wasm file was just created and should be readable.");
52
53	let executor = WasmExecutor::<HostFunctions>::builder()
54		.with_allow_missing_host_functions(true)
55		.build();
56
57	let runtime_code = RuntimeCode {
58		code_fetcher: &WrappedRuntimeCode(wasm.into()),
59		heap_pages: None,
60		// The hash is only used for caching and thus, not that important for our use case here.
61		hash: vec![1, 2, 3],
62	};
63
64	let metadata = executor
65		.call(
66			&mut sp_io::TestExternalities::default().ext(),
67			&runtime_code,
68			"Metadata_metadata_at_version",
69			&15u32.encode(),
70			CallContext::Offchain,
71		)
72		.0
73		.expect("`Metadata::metadata_at_version` should exist.");
74
75	let metadata = Option::<Vec<u8>>::decode(&mut &metadata[..])
76		.ok()
77		.flatten()
78		.expect("Metadata V15 support is required.");
79
80	let metadata = RuntimeMetadataPrefixed::decode(&mut &metadata[..])
81		.expect("Invalid encoded metadata?")
82		.1;
83
84	let runtime_version = executor
85		.call(
86			&mut sp_io::TestExternalities::default().ext(),
87			&runtime_code,
88			"Core_version",
89			&[],
90			CallContext::Offchain,
91		)
92		.0
93		.expect("`Core_version` should exist.");
94	let runtime_version = sp_version::RuntimeVersion::decode(&mut &runtime_version[..])
95		.expect("Invalid `RuntimeVersion` encoding");
96
97	let base58_prefix = extract_ss58_prefix(&metadata);
98
99	let extra_info = ExtraInfo {
100		spec_version: runtime_version.spec_version,
101		spec_name: runtime_version.spec_name.into(),
102		base58_prefix,
103		decimals: extra_info.decimals,
104		token_symbol: extra_info.token_symbol,
105	};
106
107	generate_metadata_digest(&metadata, extra_info)
108		.expect("Failed to generate the metadata digest")
109		.hash()
110}
111
112/// Extract the `SS58` from the constants in the given `metadata`.
113fn extract_ss58_prefix(metadata: &RuntimeMetadata) -> u16 {
114	let RuntimeMetadata::V15(ref metadata) = metadata else {
115		panic!("Metadata version 15 required")
116	};
117
118	let system = metadata
119		.pallets
120		.iter()
121		.find(|p| p.name == "System")
122		.expect("Each FRAME runtime has the `System` pallet; qed");
123
124	system
125		.constants
126		.iter()
127		.find_map(|c| {
128			(c.name == "SS58Prefix")
129				.then(|| u16::decode(&mut &c.value[..]).expect("SS58 is an `u16`; qed"))
130		})
131		.expect("`SS58PREFIX` exists in the `System` constants; qed")
132}