Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilize Metadata V15 #1041

Merged
merged 67 commits into from
Jul 17, 2023
Merged

Utilize Metadata V15 #1041

merged 67 commits into from
Jul 17, 2023

Conversation

lexnv
Copy link
Collaborator

@lexnv lexnv commented Jun 29, 2023

This PR makes use of metadata V15, marking the stabilization of the metadata in Subxt.

  • V15 is the preferred metadata version in subxt (after unstable)
  • codegen utilizes the RuntimeError, RuntimeEvent and RuntimeCall directly from the metadata
  • hashing and stripping of metadata are modified to reflect the outer enum types
  • converting from V14 to V15 and vice-versa builds and appends the needed PortableTypes to the metadata registry
  • CI link for the substrate binary is updated -- prior we were using a 9-week old release
  • tests are adjusted for the latest substrate binary

Testing

// Main outer enum types
"outer_enums": {
"call_enum_ty": 140,
"event_enum_ty": 20,
"error_enum_ty": 877
},

// RuntimeCall type
  "id": 140,
"type": {"path": [ "kitchensink_runtime", "RuntimeCall" ],
    "def": { "variant": { "variants":  { "name": "System",..

// System variant of the RuntimeCall. 
id": 141,
"type": { "path": [ "frame_system", "pallet", "Call",
    "def": { "variant": { "variants": [ { "name": "remark", ...

// RuntimeEvent type
"id": 20,
"type": {"path": [kitchensink_runtime", "RuntimeEvent"],
    "def": {"variant": { "variants": [ { "name": "System", ..
          
// System variant of the RuntimeEvent. 
  "id": 21,
    "type": {  "path": [ "frame_system", "pallet","Event" ],
        "def": { "variant": { "variants": [ { "name": "ExtrinsicSuccess", ..

// RuntimeError type
"id": 877,
"type": { "path": [ "kitchensink_runtime", "RuntimeError" ],
    "def": { "variant": { "variants": [ { "name": "System", ...

// System variant of the RuntimeError. 
"id": 450,
    "type": { "path": [ "frame_system","pallet", "Error"],
        "def": {"variant": { "variants": [ { "name": "InvalidSpecName", ..
             

lexnv added 15 commits June 30, 2023 13:25
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
@lexnv lexnv marked this pull request as ready for review July 4, 2023 12:07
@lexnv lexnv requested a review from a team as a code owner July 4, 2023 12:07
@@ -19,7 +19,7 @@ concurrency:
env:
CARGO_TERM_COLOR: always
# TODO: Currently pointing at latest substrate; is there a suitable binary we can pin to here?
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:stretch/latest/substrate/substrate
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:bullseye/latest/substrate/substrate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next time it'd prolly be good to get the CI fixes in a separate PR too so we can merge them more easily, but I think this will be able to merge shortly anyway hopefully now :)

@@ -382,118 +381,6 @@ impl RuntimeGenerator {
})
.collect::<Result<Vec<_>, CodegenError>>()?;

let outer_event_variants = self.metadata.pallets().filter_map(|p| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wooo, even more red than last time :D

Comment on lines 370 to 371
let instances = ",Instance".repeat(error_ty.type_params.len() - 1);
let path = format!("{}<Runtime{}>", error_ty.path, instances);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks super hacky, and I think it's just to populate the "type_name" thing? My question is; do we actually need this? Would anything break if we set type_name to be eg {PalletName}Error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we could use the PalletError name here, have removed it :D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work indeed, I have manually modified the test-runtime to convert v15 -> metadata -> v14 -> prefixed bytes; to have access in our testing to the converted version.

I was mainly looking for this test to pass, since it uses as_root_error:

async fn decode_a_module_error() {

Changes to build.rs (with runtime-api tests disabled -- since we lose that info when going from v15 to v14):

    let metadata = RuntimeMetadataPrefixed::decode(&mut &metadata_bytes[..])
        .expect("Cannot decode frame-metadata bytes");
    let v15 = match metadata.1 {
        RuntimeMetadata::V15(v15) => v15,
        _ => panic!("Unexpected metadata version"),
    };
    let metadata: Metadata = v15.try_into().expect("Cannot convert v15 to metadata");
    let metadata: RuntimeMetadataV14 = metadata.try_into().expect("Cannot convert v15 to metadata");
    let metadata: RuntimeMetadataPrefixed = metadata.into();
    let metadata_bytes = metadata.encode();

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have let the tests run with this custom patch that uses v15 converted to v14 converted back to v15 metadata, all good

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok awesome! Yeah, I had a look and type_name isn't really relevant; it's just for "disgnostic" info: https://github.com/paritytech/scale-info/blob/cc97f4c6496acf0f92d9caa45856331f094a7f38/src/ty/fields.rs#L55

The name of the field is the enum variant and the thing that decoding/encoding cares about :)

}

#[cfg(test)]
mod tests {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the tests; converting to v14 and back to v15 again and checking that things line up still is a good way to check :)

.expect("Metadata should contain enum type in registry");

if let TypeDef::Variant(variant) = &ty.ty.type_def {
get_type_def_variant_hash(registry, variant, only_these_variants, visited_ids)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pondering the use of visited_ids a bit now; eg is it right to pass the same visited_ids set into the call for each enum? What's the effect of doing so versus not doing so.

Doing so means that hashing things that share types might be faster; any type that's been seen will hash to some fixed "recursive" hash. The downside though is that the individual hashes become "weaker"; a bunch of more primitive types all are assigned the "recursive" hash when hashing the second and third enum. This makes them all effectively interchangeable; we'll no longer catch changes to them.

The point of visited_ids is only to prevent infinite loops, but given the above, let's pass an empty visited_ids set into each new top level call; so here get_enum_hash could create an empty Set each time for instance so that the types are entirely independently hashed.

@@ -481,8 +557,19 @@ impl<'a> MetadataHasher<'a> {

let extrinsic_hash = get_extrinsic_hash(&metadata.types, &metadata.extrinsic);
let runtime_hash = get_type_hash(&metadata.types, metadata.runtime_ty(), &mut visited_ids);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The visited_ids passed in here is only used in this one place anyway, which is good (see prev comment). Let's just pass in an &mut HashSet::new() to this function to ensure that we don't use it in some other place in the future and be consistent on "pass new visited_ids into each new top level hashing call".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(looking around this file more I think we should re-think the visited_ids thing a bit or at least check that we're not overly using the same one; something I've probably done in other places too. I think ultimately we will want get_type_hash to not take in visited_ids and instead construct it internally and then an internal version of get_type_hash can accept it for the recursive calls to it. Basically, each unique type that we hash should have start from a fresh set of visited IDs. I'll open an issue to do this more thoroughly)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #1066 to generally fix/prevent misuse of visited_type_ids

@jsdw
Copy link
Collaborator

jsdw commented Jul 13, 2023

This looks awesome, nice work!

Two minor comments left for me:

  • The hacky type_name thing; do we need it? (maybe? but maybe not?)
  • Get rid of RawModuleError since it doesn't align with Substrate anyway now.

I wouldn't worry too much about the visited_ids stuff; we can handle that in #1066 :)

Copy link
Collaborator

@jsdw jsdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome stuff!

@lexnv lexnv merged commit 78a106f into master Jul 17, 2023
@lexnv lexnv deleted the lexnv/stable_v15 branch July 17, 2023 09:52
@jsdw jsdw mentioned this pull request Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants