subxt_metadata/from/legacy/
mod.rs1mod portable_registry_builder;
2#[cfg(test)]
3mod tests;
4
5use crate::Metadata;
6use crate::utils::ordered_map::OrderedMap;
7use crate::utils::variant_index::VariantIndex;
8use alloc::borrow::ToOwned;
9use alloc::collections::BTreeMap;
10use alloc::format;
11use alloc::string::ToString;
12use alloc::vec::Vec;
13use frame_decode::constants::{ConstantEntryInfo, ConstantTypeInfo};
14use frame_decode::extrinsics::ExtrinsicTypeInfo;
15use frame_decode::runtime_apis::RuntimeApiTypeInfo;
16use frame_decode::storage::{StorageEntryInfo, StorageTypeInfo};
17use frame_metadata::v15;
18use portable_registry_builder::PortableRegistryBuilder;
19use scale_info_legacy::TypeRegistrySet;
20use scale_info_legacy::type_registry::RuntimeApiName;
21
22pub(crate) struct Opts {
24 pub sanitize_paths: bool,
25 pub ignore_not_found: bool,
26}
27
28impl Opts {
29 pub(crate) fn compat() -> Self {
31 Opts {
32 sanitize_paths: true,
33 ignore_not_found: true,
34 }
35 }
36}
37
38macro_rules! from_historic {
39 ($vis:vis fn $fn_name:ident($metadata:path $(, builtin_index: $builtin_index:ident)? )) => {
40 $vis fn $fn_name(metadata: &$metadata, types: &TypeRegistrySet<'_>, opts: Opts) -> Result<Metadata, Error> {
41 let mut portable_registry_builder = PortableRegistryBuilder::new(&types);
43 portable_registry_builder.ignore_not_found(opts.ignore_not_found);
44 portable_registry_builder.sanitize_paths(opts.sanitize_paths);
45
46
47 let unknown_type_id = portable_registry_builder.add_type_str("special::Unknown", None)
49 .map_err(|e| Error::add_type("constructing 'Unknown' type", e))?;
50
51 let mut call_index = 0u8;
53 let mut error_index = 0u8;
54 let mut event_index = 0u8;
55
56 let new_pallets = as_decoded(&metadata.modules).iter().map(|pallet| {
57 let (call_index, event_index, error_index) = {
64 let out = (call_index, event_index, error_index);
65 if pallet.calls.is_some() {
66 call_index += 1;
67 }
68 if pallet.event.is_some() {
69 event_index += 1;
70 }
71 error_index += 1;
72
73 out
74 };
75
76 $(
79 let $builtin_index = true;
80 let (call_index, event_index, error_index) = if $builtin_index {
81 (pallet.index, pallet.index, pallet.index)
82 } else {
83 (call_index, event_index, error_index)
84 };
85 )?
86
87 let pallet_name = as_decoded(&pallet.name).to_string();
88
89 let storage = pallet.storage.as_ref().map(|s| {
91 let storage = as_decoded(s);
92 let prefix = as_decoded(&storage.prefix);
93 let entries = metadata.storage_in_pallet(&pallet_name).map(|entry_name| {
94 let info = metadata
95 .storage_info(&pallet_name, &entry_name)
96 .map_err(|e| Error::StorageInfoError(e.into_owned()))?;
97 let entry_name = entry_name.into_owned();
98
99 let info = info.map_ids(|old_id| {
100 portable_registry_builder.add_type(old_id)
101 }).map_err(|e| {
102 let ctx = format!("adding type used in storage entry {pallet_name}.{entry_name}");
103 Error::add_type(ctx, e)
104 })?;
105
106 let entry = crate::StorageEntryMetadata {
107 name: entry_name.clone(),
108 info: info.into_owned(),
109 docs: Vec::new(),
111 };
112
113 Ok((entry_name, entry))
114 }).collect::<Result<OrderedMap<_, _>, _>>()?;
115 Ok(crate::StorageMetadata {
116 prefix: prefix.clone(),
117 entries,
118 })
119 }).transpose()?;
120
121 let error_ty = portable_registry_builder.add_type_str(&format!("builtin::module::error::{pallet_name}"), None)
123 .map_err(|e| {
124 let ctx = format!("converting the error enum for pallet {pallet_name}");
125 Error::add_type(ctx, e)
126 })?;
127
128 let call_ty = pallet.calls.as_ref().map(|_| {
130 portable_registry_builder.add_type_str(&format!("builtin::module::call::{pallet_name}"), None)
131 .map_err(|e| {
132 let ctx = format!("converting the call enum for pallet {pallet_name}");
133 Error::add_type(ctx, e)
134 })
135 }).transpose()?;
136
137 let event_ty = pallet.event.as_ref().map(|_| {
139 portable_registry_builder.add_type_str(&format!("builtin::module::event::{pallet_name}"), None)
140 .map_err(|e| {
141 let ctx = format!("converting the event enum for pallet {pallet_name}");
142 Error::add_type(ctx, e)
143 })
144 }).transpose()?;
145
146 let call_variant_index =
147 VariantIndex::build(call_ty, portable_registry_builder.types());
148 let error_variant_index =
149 VariantIndex::build(Some(error_ty), portable_registry_builder.types());
150 let event_variant_index =
151 VariantIndex::build(event_ty, portable_registry_builder.types());
152
153 let constants = metadata.constants_in_pallet(&pallet_name).map(|name| {
154 let name = name.into_owned();
155 let info = metadata.constant_info(&pallet_name, &name)
156 .map_err(|e| Error::ConstantInfoError(e.into_owned()))?;
157 let new_type_id = portable_registry_builder.add_type(info.type_id)
158 .map_err(|e| {
159 let ctx = format!("converting the constant {name} for pallet {pallet_name}");
160 Error::add_type(ctx, e)
161 })?;
162
163 let constant = crate::ConstantMetadata {
164 name: name.clone(),
165 ty: new_type_id,
166 value: info.bytes.to_vec(),
167 docs: Vec::new(),
169 };
170
171 Ok((name, constant))
172 }).collect::<Result<_,Error>>()?;
173
174 let pallet_metadata = crate::PalletMetadataInner {
175 name: pallet_name.clone(),
176 call_index,
177 event_index,
178 error_index,
179 storage,
180 error_ty: Some(error_ty),
181 call_ty,
182 event_ty,
183 call_variant_index,
184 error_variant_index,
185 event_variant_index,
186 constants,
187 view_functions: Default::default(),
188 associated_types: Default::default(),
189 docs: Default::default(),
191 };
192
193 Ok((pallet_name, pallet_metadata))
194 }).collect::<Result<OrderedMap<_,_>,Error>>()?;
195
196 let new_extrinsic = {
198 let signature_info = metadata
199 .extrinsic_signature_info()
200 .map_err(|e| Error::ExtrinsicInfoError(e.into_owned()))?;
201
202 let address_ty_id = portable_registry_builder.add_type(signature_info.address_id)
203 .map_err(|_| Error::CannotFindAddressType)?;
204
205 let signature_ty_id = portable_registry_builder.add_type(signature_info.signature_id)
206 .map_err(|_| Error::CannotFindCallType)?;
207
208 let transaction_extensions = metadata
209 .extrinsic_extension_info(None)
210 .map_err(|e| Error::ExtrinsicInfoError(e.into_owned()))?
211 .extension_ids
212 .into_iter()
213 .map(|ext| {
214 let ext_name = ext.name.into_owned();
215 let ext_type = portable_registry_builder.add_type(ext.id)
216 .map_err(|e| {
217 let ctx = format!("converting the signed extension {ext_name}");
218 Error::add_type(ctx, e)
219 })?;
220
221 Ok(crate::TransactionExtensionMetadataInner {
222 identifier: ext_name,
223 extra_ty: ext_type,
224 additional_ty: unknown_type_id.into()
228 })
229 })
230 .collect::<Result<Vec<_>,Error>>()?;
231
232 let transaction_extensions_by_version = BTreeMap::from_iter([(
233 0,
234 (0..transaction_extensions.len() as u32).collect()
235 )]);
236
237 crate::ExtrinsicMetadata {
238 address_ty: address_ty_id.into(),
239 signature_ty: signature_ty_id.into(),
240 supported_versions: Vec::from_iter([4]),
241 transaction_extensions,
242 transaction_extensions_by_version,
243 }
244 };
245
246 let outer_enums = crate::OuterEnumsMetadata {
248 call_enum_ty: portable_registry_builder.add_type_str("builtin::Call", None)
249 .map_err(|e| {
250 let ctx = format!("constructing the 'builtin::Call' type to put in the OuterEnums metadata");
251 Error::add_type(ctx, e)
252 })?,
253 event_enum_ty: portable_registry_builder.add_type_str("builtin::Event", None)
254 .map_err(|e| {
255 let ctx = format!("constructing the 'builtin::Event' type to put in the OuterEnums metadata");
256 Error::add_type(ctx, e)
257 })?,
258 error_enum_ty: portable_registry_builder.add_type_str("builtin::Error", None)
259 .map_err(|e| {
260 let ctx = format!("constructing the 'builtin::Error' type to put in the OuterEnums metadata");
261 Error::add_type(ctx, e)
262 })?,
263 };
264
265 let pallets_by_call_index = new_pallets
267 .values()
268 .iter()
269 .enumerate()
270 .map(|(idx,p)| (p.call_index, idx))
271 .collect();
272 let pallets_by_error_index = new_pallets
273 .values()
274 .iter()
275 .enumerate()
276 .map(|(idx,p)| (p.error_index, idx))
277 .collect();
278 let pallets_by_event_index = new_pallets
279 .values()
280 .iter()
281 .enumerate()
282 .map(|(idx,p)| (p.event_index, idx))
283 .collect();
284
285 let dispatch_error_ty = portable_registry_builder
289 .try_add_type_str("hardcoded::DispatchError", None)
290 .or_else(|| portable_registry_builder.try_add_type_str("sp_runtime::DispatchError", None))
291 .or_else(|| portable_registry_builder.try_add_type_str("DispatchError", None))
292 .transpose()
293 .map_err(|e| Error::add_type("constructing DispatchError", e))?;
294
295 let apis = type_registry_to_runtime_apis(&types, &mut portable_registry_builder)?;
297
298 Ok(crate::Metadata {
299 types: portable_registry_builder.finish(),
300 pallets: new_pallets,
301 pallets_by_call_index,
302 pallets_by_error_index,
303 pallets_by_event_index,
304 extrinsic: new_extrinsic,
305 outer_enums,
306 dispatch_error_ty,
307 apis,
308 custom: v15::CustomMetadata { map: Default::default() },
310 })
311 }}
312}
313
314from_historic!(pub fn from_v13(frame_metadata::v13::RuntimeMetadataV13, builtin_index: yes));
315from_historic!(pub fn from_v12(frame_metadata::v12::RuntimeMetadataV12, builtin_index: yes));
316from_historic!(pub fn from_v11(frame_metadata::v11::RuntimeMetadataV11));
317from_historic!(pub fn from_v10(frame_metadata::v10::RuntimeMetadataV10));
318from_historic!(pub fn from_v9(frame_metadata::v9::RuntimeMetadataV9));
319from_historic!(pub fn from_v8(frame_metadata::v8::RuntimeMetadataV8));
320
321fn as_decoded<A, B>(item: &frame_metadata::decode_different::DecodeDifferent<A, B>) -> &B {
322 match item {
323 frame_metadata::decode_different::DecodeDifferent::Encode(_a) => {
324 panic!("Expecting decoded data")
325 }
326 frame_metadata::decode_different::DecodeDifferent::Decoded(b) => b,
327 }
328}
329
330pub fn type_registry_to_runtime_apis(
332 types: &TypeRegistrySet<'_>,
333 portable_registry_builder: &mut PortableRegistryBuilder,
334) -> Result<OrderedMap<String, crate::RuntimeApiMetadataInner>, Error> {
335 let mut apis = OrderedMap::new();
336 let mut trait_name = "";
337 let mut trait_methods = OrderedMap::new();
338
339 for api in types.runtime_apis() {
340 match api {
341 RuntimeApiName::Trait(name) => {
342 if !trait_methods.is_empty() {
343 apis.push_insert(
344 trait_name.into(),
345 crate::RuntimeApiMetadataInner {
346 name: trait_name.into(),
347 methods: trait_methods,
348 docs: Vec::new(),
349 },
350 );
351 }
352 trait_methods = OrderedMap::new();
353 trait_name = name;
354 }
355 RuntimeApiName::Method(name) => {
356 let info = types
357 .runtime_api_info(trait_name, name)
358 .map_err(|e| Error::RuntimeApiInfoError(e.into_owned()))?;
359
360 let info = info.map_ids(|id| {
361 portable_registry_builder.add_type(id).map_err(|e| {
362 let c = format!("converting type for runtime API {trait_name}.{name}");
363 Error::add_type(c, e)
364 })
365 })?;
366
367 trait_methods.push_insert(
368 name.to_owned(),
369 crate::RuntimeApiMethodMetadataInner {
370 name: name.into(),
371 info,
372 docs: Vec::new(),
373 },
374 );
375 }
376 }
377 }
378
379 Ok(apis)
380}
381
382#[allow(missing_docs)]
384#[derive(Debug, thiserror::Error)]
385pub enum Error {
386 #[error("Cannot add type ({context}): {error}")]
388 AddTypeError {
389 context: String,
390 error: portable_registry_builder::PortableRegistryAddTypeError,
391 },
392 #[error("Cannot find 'hardcoded::ExtrinsicAddress' type in legacy types")]
393 CannotFindAddressType,
394 #[error("Cannot find 'hardcoded::ExtrinsicSignature' type in legacy types")]
395 CannotFindSignatureType,
396 #[error(
397 "Cannot find 'builtin::Call' type in legacy types (this should have been automatically added)"
398 )]
399 CannotFindCallType,
400 #[error("Cannot obtain the storage information we need to convert storage entries")]
401 StorageInfoError(frame_decode::storage::StorageInfoError<'static>),
402 #[error("Cannot obtain the extrinsic information we need to convert transaction extensions")]
403 ExtrinsicInfoError(frame_decode::extrinsics::ExtrinsicInfoError<'static>),
404 #[error("Cannot obtain the Runtime API information we need")]
405 RuntimeApiInfoError(frame_decode::runtime_apis::RuntimeApiInfoError<'static>),
406 #[error("Cannot obtain the Constant information we need")]
407 ConstantInfoError(frame_decode::constants::ConstantInfoError<'static>),
408}
409
410impl Error {
411 fn add_type(
413 context: impl Into<String>,
414 error: impl Into<portable_registry_builder::PortableRegistryAddTypeError>,
415 ) -> Self {
416 Error::AddTypeError {
417 context: context.into(),
418 error: error.into(),
419 }
420 }
421}