Subxt Metadata: #[no_std] compatibility (#1401)

* no-std tests and porting of subxt-metadata

* update pipeline

* fix generate custom metadata test

* fix cargo run command

* adjust pipeline

* remove prelude from subxt-metadata

* revert autoformatting of Cargo.toml

* remove alloc::format! again, still causes linker errors

* add no-std-build for thumbv7em-none-eabi target

* remove std feature flag

* remove libc and add small readme with test instructions

* change ci for nightly no std
This commit is contained in:
Tadeo Hepperle
2024-02-15 10:49:15 +01:00
committed by GitHub
parent e39791a5bc
commit 5447035716
21 changed files with 851 additions and 175 deletions
+12 -6
View File
@@ -2,31 +2,37 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use alloc::string::String;
use derive_more::Display;
mod v14;
mod v15;
/// An error emitted if something goes wrong converting [`frame_metadata`]
/// types into [`crate::Metadata`].
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
#[derive(Debug, Display, PartialEq, Eq)]
#[non_exhaustive]
pub enum TryFromError {
/// Type missing from type registry
#[error("Type id {0} is expected but not found in the type registry")]
#[display(fmt = "Type id {_0} is expected but not found in the type registry")]
TypeNotFound(u32),
/// Type was not a variant/enum type
#[error("Type {0} was not a variant/enum type, but is expected to be one")]
#[display(fmt = "Type {_0} was not a variant/enum type, but is expected to be one")]
VariantExpected(u32),
/// An unsupported metadata version was provided.
#[error("Cannot convert v{0} metadata into Metadata type")]
#[display(fmt = "Cannot convert v{_0} metadata into Metadata type")]
UnsupportedMetadataVersion(u32),
/// Type name missing from type registry
#[error("Type name {0} is expected but not found in the type registry")]
#[display(fmt = "Type name {_0} is expected but not found in the type registry")]
TypeNameNotFound(String),
/// Invalid type path.
#[error("Type has an invalid path {0}")]
#[display(fmt = "Type has an invalid path {_0}")]
InvalidTypePath(String),
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromError {}
impl From<crate::Metadata> for frame_metadata::RuntimeMetadataPrefixed {
fn from(value: crate::Metadata) -> Self {
let m: frame_metadata::v15::RuntimeMetadataV15 = value.into();
+19 -12
View File
@@ -2,11 +2,15 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::collections::HashMap;
use super::TryFromError;
use crate::Metadata;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::Write;
use frame_metadata::{v14, v15};
use hashbrown::HashMap;
use scale_info::TypeDef;
impl TryFrom<v14::RuntimeMetadataV14> for Metadata {
@@ -31,27 +35,27 @@ fn v15_to_v14(mut metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14
let extrinsic_type = scale_info::Type {
path: scale_info::Path {
segments: vec![
"primitives".to_string(),
"runtime".to_string(),
"generic".to_string(),
"UncheckedExtrinsic".to_string(),
"primitives".to_owned(),
"runtime".to_owned(),
"generic".to_owned(),
"UncheckedExtrinsic".to_owned(),
],
},
type_params: vec![
scale_info::TypeParameter::<scale_info::form::PortableForm> {
name: "Address".to_string(),
name: "Address".to_owned(),
ty: Some(metadata.extrinsic.address_ty),
},
scale_info::TypeParameter::<scale_info::form::PortableForm> {
name: "Call".to_string(),
name: "Call".to_owned(),
ty: Some(metadata.extrinsic.call_ty),
},
scale_info::TypeParameter::<scale_info::form::PortableForm> {
name: "Signature".to_string(),
name: "Signature".to_owned(),
ty: Some(metadata.extrinsic.signature_ty),
},
scale_info::TypeParameter::<scale_info::form::PortableForm> {
name: "Extra".to_string(),
name: "Extra".to_owned(),
ty: Some(metadata.extrinsic.extra_ty),
},
],
@@ -342,7 +346,7 @@ fn generate_outer_enums(
let Some(last) = call_path.last_mut() else {
return Err(TryFromError::InvalidTypePath("RuntimeCall".into()));
};
*last = "RuntimeError".to_string();
*last = "RuntimeError".to_owned();
generate_outer_error_enum_type(metadata, call_path)
};
@@ -368,7 +372,10 @@ fn generate_outer_error_enum_type(
return None;
};
let path = format!("{}Error", pallet.name);
// Note: using the `alloc::format!` macro like in `let path = format!("{}Error", pallet.name);`
// leads to linker errors about extern function `_Unwind_Resume` not being defined.
let mut path = String::new();
write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;");
let ty = error.ty.id.into();
Some(scale_info::Variant {
+3 -1
View File
@@ -3,6 +3,7 @@
// see LICENSE for license details.
use super::TryFromError;
use crate::utils::variant_index::VariantIndex;
use crate::{
utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata,
@@ -10,9 +11,10 @@ use crate::{
RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata,
StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
};
use alloc::borrow::ToOwned;
use frame_metadata::v15;
use hashbrown::HashMap;
use scale_info::form::PortableForm;
use std::collections::HashMap;
// Converting from V15 metadata into our Subxt repr.
mod from_v15 {
+8 -3
View File
@@ -14,14 +14,19 @@
//! 2. Obtaining [`frame_metadata::RuntimeMetadataPrefixed`], and then
//! using `.try_into()` to convert it into [`Metadata`].
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
extern crate alloc;
mod from_into;
mod utils;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use hashbrown::HashMap;
use scale_info::{form::PortableForm, PortableRegistry, Variant};
use std::collections::HashMap;
use std::sync::Arc;
use utils::variant_index::VariantIndex;
use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes};
@@ -200,7 +205,7 @@ impl Metadata {
// its name to ensure that every unique type has a unique path, too.
if *visited_count > 1 {
if let Some(name) = ty.ty.path.segments.last_mut() {
*name = format!("{name}{visited_count}");
*name = alloc::format!("{name}{visited_count}");
}
}
}
+9 -7
View File
@@ -2,7 +2,9 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::collections::HashMap;
use alloc::vec::Vec;
use core::mem;
use hashbrown::HashMap;
/// A minimal ordered map to let one search for
/// things by key or get the values in insert order.
@@ -23,7 +25,7 @@ impl<K, V> Default for OrderedMap<K, V> {
impl<K, V> OrderedMap<K, V>
where
K: PartialEq + Eq + std::hash::Hash,
K: PartialEq + Eq + core::hash::Hash,
{
/// Create a new, empty [`OrderedMap`].
pub fn new() -> Self {
@@ -47,8 +49,8 @@ where
where
F: FnMut(&V) -> bool,
{
let values = std::mem::take(&mut self.values);
let map = std::mem::take(&mut self.map);
let values = mem::take(&mut self.values);
let map = mem::take(&mut self.map);
// Filter the values, storing a map from old to new positions:
let mut new_values = Vec::new();
@@ -78,8 +80,8 @@ where
/// Get an item by its key.
pub fn get_by_key<Q>(&self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
K: alloc::borrow::Borrow<Q>,
Q: core::hash::Hash + Eq + ?Sized,
{
self.map.get(key).and_then(|&v| self.values.get(v))
}
@@ -107,7 +109,7 @@ where
impl<K, V> FromIterator<(K, V)> for OrderedMap<K, V>
where
K: PartialEq + Eq + std::hash::Hash,
K: PartialEq + Eq + core::hash::Hash,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut map = OrderedMap::new();
+2 -1
View File
@@ -8,8 +8,9 @@ use crate::{
ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner,
StorageEntryType,
};
use alloc::collections::BTreeMap;
use hashbrown::HashSet;
use scale_info::TypeDef;
use std::collections::{BTreeMap, HashSet};
/// Collect all type IDs needed to represent the provided pallet.
fn collect_pallet_types(pallet: &PalletMetadataInner, type_ids: &mut HashSet<u32>) {
+2 -1
View File
@@ -8,9 +8,10 @@ use crate::{
CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata,
RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType,
};
use alloc::vec::Vec;
use hashbrown::HashMap;
use outer_enum_hashes::OuterEnumHashes;
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant};
use std::collections::HashMap;
pub mod outer_enum_hashes;
@@ -1,6 +1,6 @@
//! Hash representations of the `frame_metadata::v15::OuterEnums`.
use std::collections::HashMap;
use hashbrown::HashMap;
use scale_info::{PortableRegistry, TypeDef};
+5 -3
View File
@@ -2,8 +2,10 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use alloc::borrow::ToOwned;
use alloc::string::String;
use hashbrown::HashMap;
use scale_info::{form::PortableForm, PortableRegistry, TypeDef, Variant};
use std::collections::HashMap;
/// Given some type ID and type registry, build a couple of
/// indexes to look up variants by index or name. If the ID provided
@@ -62,8 +64,8 @@ impl VariantIndex {
types: &'a PortableRegistry,
) -> Option<&'a Variant<PortableForm>>
where
String: std::borrow::Borrow<K>,
K: std::hash::Hash + Eq + ?Sized,
String: alloc::borrow::Borrow<K>,
K: core::hash::Hash + Eq + ?Sized,
{
let pos = *self.by_name.get(name)?;
let variants = Self::get(variant_id, types)?;