Skip to content

Commit

Permalink
Static Metadata Validation (#478)
Browse files Browse the repository at this point in the history
* metadata: Implement MetadataHashable for deterministic hashing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Hash `scale_info::Field`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Hash `scale_info::Variant`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Hash `scale_info::TypeDef`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Hash pallet metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Avoid data representation collision via unique identifiers

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Finalize hashing on recursive types

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Cache recursive calls

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Move `MetadataHashable` to codegen to avoid cyclic dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add pallet unique hash

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Wrap metadata as owned

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Use MetadataHashable wrapper for clients

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Generate runtime pallet uid from metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Validate metadata compatibility at the pallet level

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Modify examples and tests for the new API

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Implement metadata uid

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update polkadot with TryFrom implementation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* client: Change `to_runtime_api` to reflect TryFrom changes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* client: Skip full metadata validation option

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add option to skip pallet validation for TransactionApi

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add option to skip pallet validation for StorageApi

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update polkadot.rs with ability to skip pallet validation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Change `MetadataHashable` to per function implementation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Use metadata hashes functions

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Use metadata hashes functions

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Make `get_type_uid` private

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Rename metadata functions `*_uid` to `*_hash`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Update `get_field_hash` to use `codec::Encode`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Move metadata check from client to subxt::Metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Rename metadata check functions to follow `*_hash` naming

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Update polkadot.rs to reflect naming changes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Use `encode_to` for metadata generation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Update polkadot.rs to reflect `encode_to` changes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Specific name for visited set

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Provide cache to hashing functions

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Compute metadata hash by sorted pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Get extrinsic hash

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Extend metadata hash with extrinsic and metadata type

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add cache as metadata parameter

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Update metadata hash to use cache

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Implement Default trait for MetadataHasherCache

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add cache for pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Move functionality to metadata crate

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Use subxt-metadata crate

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Remove metdata hashing functionality

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix vector capacity to include extrinisc and type hash

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add empty CLI

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Fetch metadata from substrate nodes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Log metadata hashes of provided nodes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Group compatible nodes by metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Simplify hash map insertion

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Move full metadata check to function

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-cli: Group metadata validation at the pallet level

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Persist metadata cache

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Move compatibility cli from subxt-metadata to subxt-cli

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Remove cli from subxt-metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* cli: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix compatible metadata when pallets are registered in different order

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Handle result of pallet hashing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Remove type cache for deterministic hashing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add test assets from `substrate-node-template` tag `polkadot-v0.9.17`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-tests: Check cache hashing for Balances pallet

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix `get_type_hash` clippy issue

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-tests: Compare one time cache with persistent cache

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-test: Check metadata hash populates cache for pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-tests: Simplify `cache_deterministic_hash` test

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata-tests: Check deterministic metadata for different order pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Implement TransactionApiUnchecked for skipping pallet validation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Implement StorageApiUnchecked for skipping pallet validation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Remove skip_pallet_validation boolean

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Implement ClientUnchecked for skipping metadata validation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update examples of rpc_call to skip metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Remove heck dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add pallet name as an identifier for pallet hashing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Implement MetadataHashDetails

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Adjust testing to `MetadataHashDetails` interface

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Remove extra `pallet_name`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Fix clippy issue

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Change StorageApi to support `_unchecked` methods

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Change TransactionApi to support `_unchecked` methods

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Switch back from `TryFrom` to `From` for `subxt::Client`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen, subxt: Remove `ClientUnchecked`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Expose `validate_metadata` as validation of compatibility method

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Update to the new interface

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Update test integration to latest interface

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Check different pallet index order

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Check recursive type hashing

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Check recursive types registered in different order

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Fix recursive types warning

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Remove test assets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Extend tests to verify cached pallet values

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Add metadata compatiblity example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Revert balance_transfer to initial form

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add ConstantsApi metadata check

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Modify tests to accomodate ConstantsApi changes

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Modify verified version

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Generate polkadot.rs from `0.9.18-4542a603cc-aarch64-macos`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Update polkadot_metadata.scale from `0.9.18-4542a603cc-aarch64-macos`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Update documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* tests: Modify default pallet usage

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Remove hex dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Add MetadataTestType to capture complex types

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Update tests to use complex types

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Check metadata correctness via extending pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Extend pallet hash with Events

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Extend pallet hash with constants

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Extend pallet hash with error

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Extend metadata compatibiliy with StorageApi and ConstantsApi

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Modify comments and documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Benchmarks for full validation and pallet validation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/benches: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Hash metadata just by inspecting the provided pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Make pallets generic over T for `AsRef<str>`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Expose the name of the pallets composing the metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Update polkadot.rs with pallets name

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Obtain metadata hash only by inspecting pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen,subxt: Extend the metadata hash to utilize just pallets

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Update polkadot.rs with client metadata has per pallet

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Test `get_metadata_per_pallet_hash` correctness

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/benches: Fix decode of metadata

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* [static metadata] validate storage, calls and constants per call (#507)

* validate storage, calls and constants per call

* fix typo

* cache per-thing hashing, move an Arc, remove some unused bits

* create hash_cache to simplify metadata call/constant/storage caching

* simplify/robustify the caching logic to help prevent mismatch between get and set

* cargo fmt

* Fix clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* bench the per-call metadata functions

* metadata: Add test for `node_template_runtime_variant`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* ensure criteron cli opts work

* group benchmarks and avoid unwrap issues

* metadata: Check template runtime for handling the pallet swap order case

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Remove debug logs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Optimise by removing field's name and type_name and type's path

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Refactor `get_type_hash` to break recursion earlier

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add tests for `hash_cache`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add tests for checking Metadata Inner cache

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Check semantic changes inside enum and struct fields

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add enums named differently with compatible semantic meaning

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Guard testing of release versions for `node_template_runtime`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Improve documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update polkadot.rs

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata/tests: Manually construct type of `node_template_runtimeL::Call`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* no more special Call handling, avoid a little cloning, and actually sort by name

* remove unused deps and fmt

* RuntimeMetadataLastVersion => RuntimeMetadataV14

* remove a bunch of allocations in the metadata hashing, speed up from ~17ms to ~5ms

* update release docs to release metadata crate too

* subxt: Remove codegen dependency

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Replace std RwLock with parking_lot

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Add ws address to `TestNodeProcess`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Add metadata validation integration test

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Allow setting metadata on the ClientBuilder

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Check incompatible metadatas

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Fix constant hashing for deterministic output

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Check metadata validation for constants

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Test validation for calls

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/tests: Test validation for storage

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Expose `set_metadata` for testing only

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Guard metadata tests under integration-tests

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

Co-authored-by: James Wilson <james@jsdw.me>
  • Loading branch information
lexnv and jsdw committed Apr 28, 2022
1 parent 5605fbd commit 1fd1eee
Show file tree
Hide file tree
Showing 44 changed files with 17,843 additions and 4,707 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"codegen",
"examples",
"macro",
"metadata",
"subxt",
"test-runtime"
]
3 changes: 2 additions & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ We also assume that ongoing work done is being merged directly to the `master` b
a little time in between each to let crates.io catch up with what we've published).

```
(cd codegen && cargo publish) && \
(cd metadata && cargo publish) && \
(cd codegen && cargo publish) && \
sleep 10 && \
(cd macro && cargo publish) && \
sleep 10 && \
Expand Down
4 changes: 4 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ path = "src/main.rs"
[dependencies]
# perform subxt codegen
subxt-codegen = { version = "0.20.0", path = "../codegen" }
# perform node compatibility
subxt-metadata = { version = "0.20.0", path = "../metadata" }
# information of portable registry
scale-info = "2.0.0"
# parse command line args
structopt = "0.3.25"
# make the request to a substrate node to get the metadata
Expand Down
127 changes: 126 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@ use color_eyre::eyre::{
self,
WrapErr,
};
use frame_metadata::RuntimeMetadataPrefixed;
use frame_metadata::{
RuntimeMetadata,
RuntimeMetadataPrefixed,
RuntimeMetadataV14,
META_RESERVED,
};
use scale::{
Decode,
Input,
};
use serde::{
Deserialize,
Serialize,
};
use std::{
collections::HashMap,
fs,
io::{
self,
Expand All @@ -34,6 +44,10 @@ use std::{
};
use structopt::StructOpt;
use subxt_codegen::GeneratedTypeDerives;
use subxt_metadata::{
get_metadata_hash,
get_pallet_hash,
};

/// Utilities for working with substrate metadata for subxt.
#[derive(Debug, StructOpt)]
Expand Down Expand Up @@ -75,6 +89,18 @@ enum Command {
#[structopt(long = "derive")]
derives: Vec<String>,
},
/// Verify metadata compatibility between substrate nodes.
Compatibility {
/// Urls of the substrate nodes to verify for metadata compatibility.
#[structopt(name = "nodes", long, use_delimiter = true, parse(try_from_str))]
nodes: Vec<url::Url>,
/// Check the compatibility of metadata for a particular pallet.
///
/// ### Note
/// The validation will omit the full metadata check and focus instead on the pallet.
#[structopt(long, parse(try_from_str))]
pallet: Option<String>,
},
}

fn main() -> color_eyre::Result<()> {
Expand Down Expand Up @@ -126,6 +152,105 @@ fn main() -> color_eyre::Result<()> {
codegen(&mut &bytes[..], derives)?;
Ok(())
}
Command::Compatibility { nodes, pallet } => {
match pallet {
Some(pallet) => handle_pallet_metadata(nodes.as_slice(), pallet.as_str()),
None => handle_full_metadata(nodes.as_slice()),
}
}
}
}

fn handle_pallet_metadata(nodes: &[url::Url], name: &str) -> color_eyre::Result<()> {
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
struct CompatibilityPallet {
pallet_present: HashMap<String, Vec<String>>,
pallet_not_found: Vec<String>,
}

let mut compatibility: CompatibilityPallet = Default::default();
for node in nodes.iter() {
let metadata = fetch_runtime_metadata(node)?;

match metadata.pallets.iter().find(|pallet| pallet.name == name) {
Some(pallet_metadata) => {
let hash = get_pallet_hash(&metadata.types, pallet_metadata);
let hex_hash = hex::encode(hash);
println!(
"Node {:?} has pallet metadata hash {:?}",
node.as_str(),
hex_hash
);

compatibility
.pallet_present
.entry(hex_hash)
.or_insert_with(Vec::new)
.push(node.as_str().to_string());
}
None => {
compatibility
.pallet_not_found
.push(node.as_str().to_string());
}
}
}

println!(
"\nCompatible nodes by pallet\n{}",
serde_json::to_string_pretty(&compatibility)
.context("Failed to parse compatibility map")?
);

Ok(())
}

fn handle_full_metadata(nodes: &[url::Url]) -> color_eyre::Result<()> {
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
for node in nodes.iter() {
let metadata = fetch_runtime_metadata(node)?;
let hash = get_metadata_hash(&metadata);
let hex_hash = hex::encode(hash);
println!("Node {:?} has metadata hash {:?}", node.as_str(), hex_hash,);

compatibility_map
.entry(hex_hash)
.or_insert_with(Vec::new)
.push(node.as_str().to_string());
}

println!(
"\nCompatible nodes\n{}",
serde_json::to_string_pretty(&compatibility_map)
.context("Failed to parse compatibility map")?
);

Ok(())
}

fn fetch_runtime_metadata(url: &url::Url) -> color_eyre::Result<RuntimeMetadataV14> {
let (_, bytes) = fetch_metadata(url)?;

let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
if metadata.0 != META_RESERVED {
return Err(eyre::eyre!(
"Node {:?} has invalid metadata prefix: {:?} expected prefix: {:?}",
url.as_str(),
metadata.0,
META_RESERVED
))
}

match metadata.1 {
RuntimeMetadata::V14(v14) => Ok(v14),
_ => {
Err(eyre::eyre!(
"Node {:?} with unsupported metadata version: {:?}",
url.as_str(),
metadata.1
))
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ proc-macro-error = "1.0.4"
quote = "1.0.8"
syn = "1.0.58"
scale-info = { version = "2.0.0", features = ["bit-vec"] }
sp-core = { version = "6.0.0" }
subxt-metadata = { version = "0.20.0", path = "../metadata" }

[dev-dependencies]
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
Expand Down
26 changes: 17 additions & 9 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::types::{
TypeGenerator,
};
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletCallMetadata,
PalletMetadata,
};
Expand All @@ -35,6 +36,7 @@ use quote::{
use scale_info::form::PortableForm;

pub fn generate_calls(
metadata: &RuntimeMetadataV14,
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
call: &PalletCallMetadata<PortableForm>,
Expand All @@ -48,7 +50,7 @@ pub fn generate_calls(
);
let (call_structs, call_fns): (Vec<_>, Vec<_>) = struct_defs
.iter_mut()
.map(|struct_def| {
.map(|(variant_name, struct_def)| {
let (call_fn_args, call_args): (Vec<_>, Vec<_>) =
match struct_def.fields {
CompositeDefFields::Named(ref named_fields) => {
Expand All @@ -74,30 +76,36 @@ pub fn generate_calls(
};

let pallet_name = &pallet.name;
let call_struct_name = &struct_def.name;
let function_name = struct_def.name.to_string().to_snake_case();
let fn_name = format_ident!("{}", function_name);
let call_name = &variant_name;
let struct_name = &struct_def.name;
let call_hash = subxt_metadata::get_call_hash(metadata, pallet_name, call_name)
.unwrap_or_else(|_| abort_call_site!("Metadata information for the call {}_{} could not be found", pallet_name, call_name));

let fn_name = format_ident!("{}", variant_name.to_snake_case());
// Propagate the documentation just to `TransactionApi` methods, while
// draining the documentation of inner call structures.
let docs = struct_def.docs.take();
// The call structure's documentation was stripped above.
let call_struct = quote! {
#struct_def

impl ::subxt::Call for #call_struct_name {
impl ::subxt::Call for #struct_name {
const PALLET: &'static str = #pallet_name;
const FUNCTION: &'static str = #function_name;
const FUNCTION: &'static str = #call_name;
}
};
let client_fn = quote! {
#docs
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> ::subxt::SubmittableExtrinsic<'a, T, X, #call_struct_name, DispatchError, root_mod::Event> {
let call = #call_struct_name { #( #call_args, )* };
::subxt::SubmittableExtrinsic::new(self.client, call)
) -> Result<::subxt::SubmittableExtrinsic<'a, T, X, #struct_name, DispatchError, root_mod::Event>, ::subxt::BasicError> {
if self.client.metadata().call_hash::<#struct_name>()? == [#(#call_hash,)*] {
let call = #struct_name { #( #call_args, )* };
Ok(::subxt::SubmittableExtrinsic::new(self.client, call))
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
}
}
};
(call_struct, client_fn)
Expand Down
18 changes: 14 additions & 4 deletions codegen/src/api/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@

use crate::types::TypeGenerator;
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletConstantMetadata,
PalletMetadata,
};
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error::abort_call_site;
use quote::{
format_ident,
quote,
};
use scale_info::form::PortableForm;

pub fn generate_constants(
metadata: &RuntimeMetadataV14,
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
constants: &[PalletConstantMetadata<PortableForm>],
Expand All @@ -37,16 +40,23 @@ pub fn generate_constants(
let fn_name = format_ident!("{}", constant.name.to_snake_case());
let pallet_name = &pallet.name;
let constant_name = &constant.name;
let constant_hash = subxt_metadata::get_constant_hash(metadata, pallet_name, constant_name)
.unwrap_or_else(|_| abort_call_site!("Metadata information for the constant {}_{} could not be found", pallet_name, constant_name));

let return_ty = type_gen.resolve_type_path(constant.ty.id(), &[]);
let docs = &constant.docs;

quote! {
#( #[doc = #docs ] )*
pub fn #fn_name(&self) -> ::core::result::Result<#return_ty, ::subxt::BasicError> {
let pallet = self.client.metadata().pallet(#pallet_name)?;
let constant = pallet.constant(#constant_name)?;
let value = ::subxt::codec::Decode::decode(&mut &constant.value[..])?;
Ok(value)
if self.client.metadata().constant_hash(#pallet_name, #constant_name)? == [#(#constant_hash,)*] {
let pallet = self.client.metadata().pallet(#pallet_name)?;
let constant = pallet.constant(#constant_name)?;
let value = ::subxt::codec::Decode::decode(&mut &constant.value[..])?;
Ok(value)
} else {
Err(::subxt::MetadataError::IncompatibleMetadata.into())
}
}
}
});
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ pub fn generate_events(
|name| name.into(),
"Event",
);
let event_structs = struct_defs.iter().map(|struct_def| {
let event_structs = struct_defs.iter().map(|(variant_name, struct_def)| {
let pallet_name = &pallet.name;
let event_struct = &struct_def.name;
let event_name = struct_def.name.to_string();
let event_name = variant_name;

quote! {
#struct_def
Expand Down

0 comments on commit 1fd1eee

Please sign in to comment.