mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
Add proper test Custom values (#1147)
* add proper tests for custom values * remove try operators * use sustrate compat for import of hash * add license and hex * add script to artifacts.sh * custom values with ids not in metadata can be accessed in static interface * fmt and clippy * access bytes of custom values directly, even if type id wrong * final fixes * removing substrate-compat flag from ui tests * Update subxt/src/custom_values/custom_values_client.rs Co-authored-by: James Wilson <james@jsdw.me> * remove types access in type generator * 2 extra lines --------- Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -10,8 +10,13 @@ publish = false
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
scale-info = { workspace = true, features = ["bit-vec"] }
|
||||
frame-metadata ={ workspace = true }
|
||||
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
|
||||
frame-metadata = { workspace = true }
|
||||
codec = { package = "parity-scale-codec", workspace = true, features = [
|
||||
"derive",
|
||||
"bit-vec",
|
||||
] }
|
||||
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
|
||||
subxt-metadata = { workspace = true }
|
||||
generate-custom-metadata = { path = "../generate-custom-metadata" }
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
use codec::{Decode};
|
||||
use subxt::{config::substrate::H256, OfflineClient, PolkadotConfig};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "../../../../artifacts/metadata_with_custom_values.scale", derive_for_all_types = "Eq, PartialEq")]
|
||||
pub mod node {}
|
||||
use node::runtime_types::generate_custom_metadata::Foo;
|
||||
|
||||
fn main() {
|
||||
let api = construct_offline_client();
|
||||
|
||||
let expected_foo = Foo {
|
||||
a: 42,
|
||||
b: "Have a great day!".into(),
|
||||
};
|
||||
|
||||
// static query:
|
||||
let foo_address = node::custom().foo();
|
||||
let foo = api.custom_values().at(&foo_address).unwrap();
|
||||
assert_eq!(foo, expected_foo);
|
||||
|
||||
// dynamic query:
|
||||
let foo_value = api.custom_values().at("Foo").unwrap();
|
||||
let foo: Foo = foo_value.as_type().unwrap();
|
||||
assert_eq!(foo, expected_foo);
|
||||
|
||||
// static query for some custom value that has an invalid type id: (we can still access the bytes)
|
||||
let custom_bytes = api.custom_values().bytes_at("InvalidTypeId").unwrap();
|
||||
assert_eq!(vec![0,1,2,3], custom_bytes);
|
||||
}
|
||||
|
||||
fn construct_offline_client() -> OfflineClient<PolkadotConfig> {
|
||||
let genesis_hash = {
|
||||
let h = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3";
|
||||
let bytes = hex::decode(h).unwrap();
|
||||
H256::from_slice(&bytes)
|
||||
};
|
||||
let runtime_version = subxt::backend::RuntimeVersion {
|
||||
spec_version: 9370,
|
||||
transaction_version: 20,
|
||||
};
|
||||
let metadata = {
|
||||
let bytes = std::fs::read("../../../../artifacts/metadata_with_custom_values.scale").unwrap();
|
||||
Metadata::decode(&mut &*bytes).unwrap()
|
||||
};
|
||||
OfflineClient::<PolkadotConfig>::new(genesis_hash, runtime_version, metadata)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use codec::Encode;
|
||||
use frame_metadata::v15::{CustomMetadata, ExtrinsicMetadata, OuterEnums, RuntimeMetadataV15};
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use scale_info::form::PortableForm;
|
||||
use scale_info::TypeInfo;
|
||||
use scale_info::{meta_type, IntoPortable};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Generate metadata which contains a `Foo { a: u8, b: &str }` custom value.
|
||||
pub fn metadata_custom_values_foo() -> RuntimeMetadataPrefixed {
|
||||
let mut registry = scale_info::Registry::new();
|
||||
|
||||
// create foo value and type:
|
||||
|
||||
#[derive(TypeInfo, Encode)]
|
||||
struct Foo {
|
||||
a: u8,
|
||||
b: &'static str,
|
||||
}
|
||||
|
||||
let foo_value_metadata: frame_metadata::v15::CustomValueMetadata<PortableForm> = {
|
||||
let value = Foo { a: 0, b: "Hello" };
|
||||
let foo_ty = scale_info::MetaType::new::<Foo>();
|
||||
let foo_ty_id = registry.register_type(&foo_ty);
|
||||
frame_metadata::v15::CustomValueMetadata {
|
||||
ty: foo_ty_id,
|
||||
value: value.encode(),
|
||||
}
|
||||
};
|
||||
|
||||
// We don't care about the extrinsic type.
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
let pallets = vec![];
|
||||
let extrinsic = extrinsic.into_portable(&mut registry);
|
||||
|
||||
let unit_ty = registry.register_type(&meta_type::<()>());
|
||||
|
||||
// Metadata needs to contain this DispatchError, since codegen looks for it.
|
||||
registry.register_type(&meta_type::<crate::utils::dispatch_error::ArrayDispatchError>());
|
||||
|
||||
let metadata = RuntimeMetadataV15 {
|
||||
types: registry.into(),
|
||||
pallets,
|
||||
extrinsic,
|
||||
ty: unit_ty,
|
||||
apis: vec![],
|
||||
outer_enums: OuterEnums {
|
||||
call_enum_ty: unit_ty,
|
||||
event_enum_ty: unit_ty,
|
||||
error_enum_ty: unit_ty,
|
||||
},
|
||||
custom: CustomMetadata {
|
||||
// provide foo twice, to make sure nothing breaks in these cases:
|
||||
map: BTreeMap::from_iter([
|
||||
("Foo".into(), foo_value_metadata.clone()),
|
||||
("foo".into(), foo_value_metadata.clone()),
|
||||
("12".into(), foo_value_metadata.clone()),
|
||||
("&Hello".into(), foo_value_metadata),
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
RuntimeMetadataPrefixed::from(metadata)
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::utils::{
|
||||
dispatch_error::{ArrayDispatchError, LegacyDispatchError, NamedFieldDispatchError},
|
||||
generate_metadata_from_pallets_custom_dispatch_error,
|
||||
use crate::utils::generate_metadata_from_pallets_custom_dispatch_error;
|
||||
|
||||
use generate_custom_metadata::dispatch_error::{
|
||||
ArrayDispatchError, LegacyDispatchError, NamedFieldDispatchError,
|
||||
};
|
||||
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
|
||||
pub fn metadata_array_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
//! Use with `TRYBUILD=overwrite` after updating codebase (see `trybuild` docs for more details on that)
|
||||
//! to automatically regenerate `stderr` files, but don't forget to check that new files make sense.
|
||||
|
||||
mod custom_values;
|
||||
mod dispatch_errors;
|
||||
mod storage;
|
||||
mod utils;
|
||||
@@ -21,13 +20,13 @@ use crate::utils::MetadataTestRunner;
|
||||
// Each of these tests leads to some rust code being compiled and
|
||||
// executed to test that compilation is successful (or errors in the
|
||||
// way that we'd expect).
|
||||
|
||||
#[test]
|
||||
fn ui_tests() {
|
||||
let mut m = MetadataTestRunner::default();
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
t.pass("src/correct/*.rs");
|
||||
|
||||
// Check that storage maps with no keys are handled properly.
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
@@ -52,12 +51,6 @@ fn ui_tests() {
|
||||
.build(dispatch_errors::metadata_array_dispatch_error()),
|
||||
);
|
||||
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("custom_values_foo")
|
||||
.build(custom_values::metadata_custom_values_foo()),
|
||||
);
|
||||
|
||||
// Test retaining only specific pallets and ensure that works.
|
||||
for pallet in ["Babe", "Claims", "Grandpa", "Balances"] {
|
||||
let mut metadata = MetadataTestRunner::load_metadata();
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use scale_info::{
|
||||
build::{Fields, Variants},
|
||||
Path, Type, TypeInfo,
|
||||
};
|
||||
|
||||
/// See the `ModuleErrorType` in `subxt_codegen` for more info on the different DispatchError
|
||||
/// types that we've encountered. We need the path to match `sp_runtime::DispatchError`, otherwise
|
||||
/// we could just implement roughly the correct types and derive TypeInfo on them.
|
||||
|
||||
/// This type has TypeInfo compatible with the `NamedField` version of the DispatchError.
|
||||
/// This is the oldest version that subxt supports:
|
||||
/// `DispatchError::Module { index: u8, error: u8 }`
|
||||
pub enum NamedFieldDispatchError {}
|
||||
impl TypeInfo for NamedFieldDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(
|
||||
Fields::named()
|
||||
.field(|b| b.name("error").ty::<u8>())
|
||||
.field(|b| b.name("index").ty::<u8>()),
|
||||
)
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This type has TypeInfo compatible with the `LegacyError` version of the DispatchError.
|
||||
/// This is the version wasn't around for long:
|
||||
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: u8 } )`
|
||||
pub enum LegacyDispatchError {}
|
||||
impl TypeInfo for LegacyDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
struct ModuleError;
|
||||
impl TypeInfo for ModuleError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("ModuleError", "sp_runtime"))
|
||||
.composite(
|
||||
Fields::named()
|
||||
.field(|b| b.name("index").ty::<u8>())
|
||||
.field(|b| b.name("error").ty::<u8>()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(Fields::unnamed().field(|b| b.ty::<ModuleError>()))
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This type has TypeInfo compatible with the `ArrayError` version of the DispatchError.
|
||||
/// This is the current version:
|
||||
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: [u8; 4] } )`
|
||||
pub enum ArrayDispatchError {}
|
||||
impl TypeInfo for ArrayDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
struct ModuleError;
|
||||
impl TypeInfo for ModuleError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("ModuleError", "sp_runtime"))
|
||||
.composite(
|
||||
Fields::named()
|
||||
.field(|b| b.name("index").ty::<u8>())
|
||||
.field(|b| b.name("error").ty::<[u8; 4]>()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(Fields::unnamed().field(|b| b.ty::<ModuleError>()))
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
pub mod dispatch_error;
|
||||
mod metadata_test_runner;
|
||||
|
||||
use frame_metadata::{
|
||||
@@ -12,6 +11,7 @@ use frame_metadata::{
|
||||
},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use generate_custom_metadata::dispatch_error::ArrayDispatchError;
|
||||
use scale_info::{meta_type, IntoPortable, TypeInfo};
|
||||
|
||||
pub use metadata_test_runner::MetadataTestRunner;
|
||||
@@ -78,9 +78,7 @@ pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeI
|
||||
/// We default to a useless extrinsic type, and register a fake `DispatchError`
|
||||
/// type so that codegen is happy with the metadata generated.
|
||||
pub fn generate_metadata_from_pallets(pallets: Vec<PalletMetadata>) -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<dispatch_error::ArrayDispatchError>(
|
||||
pallets,
|
||||
)
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<ArrayDispatchError>(pallets)
|
||||
}
|
||||
|
||||
/// Given some storage entries, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
|
||||
Reference in New Issue
Block a user