Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Metadata with complete type information #1328

Closed
wants to merge 88 commits into from

Conversation

xlc
Copy link
Contributor

@xlc xlc commented Dec 24, 2018

This fixes #917, which is one of the biggest pain point to me when working with Substrate (and polkadot.js), that any type information needs to be implemented twice and incompatible type information simply cause polkadot.js crashes without much helpful clues (other than it is mostly likely a codec error).

This introduce type_registry to RuntimeMetadata which contains all the type informations i.e. the metadata itself stores the information about how to decode the storage types.

Ideally, this will remove the needs to manually maintain type information in polkadot.js (currently implemented at https://github.com/polkadot-js/api/tree/master/packages/types/src). Those JS classes can be generated at runtime by parsing the metadata. TS files can be generated ahead-of-time if needed. This will also make implement (and support) other languages SDK much easily.

Some code copied from parity-codec.

Fixes #917
Enable the proper fix for polkadot-js/api#416
Could be the foundation work for polkadot-js/api#429

This is the debug print of the resulting metadata:
https://gist.github.com/xlc/19d010e55f84bf6d43afafdc5e5b5df6#file-metadata-L5525

@parity-cla-bot
Copy link

It looks like @xlc signed our Contributor License Agreement. 👍

Many thanks,

Parity Technologies CLA Bot

@bkchr
Copy link
Member

bkchr commented Dec 25, 2018

Nice and ty. That was something I was thinking to do as well. Good that you started it :)
The main problem with your implementation is the blow up of human readable static stuff in the binary. In the first implementation of the metadata, we used json to represent it, but we switched to binary to get rid of all static strings lying in the runtime and increasing its size. So I would not accept your current implementation.
As I thought about it, I came to the idea of exposing fundamental types as a fixed binary representation. So, u64 could be b1 and u32 could be b01. Limiting this to 256 different values should be enough. The procedural macro could use a fixed list to generate the correct binary representation per type. And if a type is not supported, we would see this at compilation and could prepare an update of the Javascript side as well.

@xlc
Copy link
Contributor Author

xlc commented Dec 25, 2018

I can remove the name field and change TypeMetadata::Primative to TypeMetadata::Primative(u8), and possibility remove enum variant name and field name which should solve the concern of having human readable string in the wasm file.
However this means it can be harder to generate a developer friendly sdk / typescript file from it because there will be no more names. Maybe use some std feature flag to still have a verbose mode for native compilation?

Your idea will make breaking changes more explicit but still not solving my original issue, that update runtime module should have minimal changes to js sdk.

Let's say I want to make a runtime upgrade that introduce a new type, how to make it non-breaking change regards to existing js clients? This will happen pretty frequently when there is one team working on building a runtime module and upgrading runtime frequently. With existing Substrate and polkadot.js architecture, it is hard to quickly iterate things because someone needs to make sure polkadot.js api have the new type information, all client apps are updated to use latest api, and then an upgrade can happen. But this to me feels like defects the whole purpose or upgradeable runtime, that upgrades should be non-breaking and clients should be able to self-discovery the available features.

This may be solvable on polkadot.js side (unable to parse module information for a particular module something shouldn't break everything), or parity-codec side (somehow to make codec breaking changes harder to make, or at least detect upfront they are incompatible), but I think some changes are needed on Substrate side.

@bkchr
Copy link
Member

bkchr commented Dec 26, 2018

First, you would just have a map:

BinaryToType = {
   0 => u64,
   1 -> u32,
    ...
}

For adding a new type, you would just need to add 2 -> i32. The javascript side could just say, values above 1 are unknown.
I think that if we support all fundamental types, there will be no missing types at all. Even if you would encode them as string, how does the javascript side knows how to interpret them?
Every higher order type will just be build up by different combinations of fundamental types. Keys would also be fundamental types with with different number of bits.
As a team will not introduce any new fundamental types to rust, the javascript side should be fairly stable, with a good initial set of known types.

@xlc xlc force-pushed the metadata-reflection-poc branch 2 times, most recently from 64da76c to cdce193 Compare December 26, 2018 22:23
@xlc
Copy link
Contributor Author

xlc commented Dec 26, 2018

@bkchr I have updated it to use enum (u8) to encode primitive types instead of string. Not sure what is the best way to make unrecognized primitive type become a compile-time error instead of runtime error, maybe need to rewrite impl_primatives with procedure macro?
There are more optimization could be done, such that currently if a complex type (struct/enum) is used is multiple places, it will be encoded multiple times. But those are not important for PoC purpose.

Please let me know if you think this is the correct direction to go and the best way to structure the repos. This substrate-metadata module to me can be either part of parity-codec, or a separate lib depends on parity-codec. And some method name is a bit awkward (type_metadat method was originally called metadata, which conflicts do some existing metadata method so I have to rename it), it will be good if we can come up some better and less confusing names.

I want to keep working on this but I don't want to send too much time on it and then found out it towards to a dead end.

srml/balances/src/lib.rs Outdated Show resolved Hide resolved
srml/contract/src/lib.rs Outdated Show resolved Hide resolved
@gavofyork
Copy link
Member

Looks pretty cool overall (though it'll need to work with the Compact encoding stuff)

@xlc xlc force-pushed the metadata-reflection-poc branch 2 times, most recently from b6864bc to e19bc18 Compare January 2, 2019 21:57
@xlc
Copy link
Contributor Author

xlc commented Jan 3, 2019

Needs help
I tried multiple ways but still not able to make it work with HasCompact. My Rust skill is still limited and I have trouble understand some implementation details of parity-codec and why HasCompact is implemented as it is.

Some methods I tried:
Modify parity-codec HasCompact trait, but upgrade parity-codec to master was causing some unexpected compiling error

error[E0277]: the trait bound `<<T as Trait>::Moment as codec::HasCompact>::Type: std::convert::From<<T as Trait>::Moment>` is not satisfied
   --> srml/timestamp/src/lib.rs:162:56
    |
162 |         vec![(T::TIMESTAMP_SET_POSITION, Call::set(next_time.into()))]
    |                                                              ^^^^ the trait `std::convert::From<<T as Trait>::Moment>` is not implemented for `<<T as Trait>::Moment as codec::HasCompact>::Type`
    |
    = help: consider adding a `where <<T as Trait>::Moment as codec::HasCompact>::Type: std::convert::From<<T as Trait>::Moment>` bound
    = note: required because of the requirements on the impl of `std::convert::Into<<<T as Trait>::Moment as codec::HasCompact>::Type>` for `<T as Trait>::Moment`

xlc@7ac594f


I tried duplicate the implementation of HasCompact in substrate-metadata module and change the runtime modules to use the duplicated and modified one, but still getting all compiling error that make me I wonder why the initial version was able to compile at all.

xlc@8b9baa0


I tried replace <T as HasCompac>::Type to Compac<T> and still having many unexpected compiling issues.

97c4251


I am also thinking move the metadata code into parity-codec, which should simplify some implementation details. Do you guys think is this something part of parity-codec?

I am stuck and need help to continue.

@bkchr
Copy link
Member

bkchr commented Jan 3, 2019

Metadata just uses parity-codec and does not belongs to parity-codec.
I will look into your branch next week, after my holidays.

@xlc
Copy link
Contributor Author

xlc commented Feb 26, 2019

@xlc xlc force-pushed the metadata-reflection-poc branch 3 times, most recently from aa9808f to 49fa7e8 Compare March 7, 2019 07:30
@xlc xlc force-pushed the metadata-reflection-poc branch from 49fa7e8 to 88275c0 Compare March 7, 2019 07:57
Copy link
Contributor

@Demi-Marie Demi-Marie left a comment

Choose a reason for hiding this comment

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

Nits, but I am not too familiar with this code.

@@ -0,0 +1,206 @@
use quote::{quote, quote_spanned};
Copy link
Contributor

Choose a reason for hiding this comment

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

I have no experience writing procedural macros. @rphmeier can you help review this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have much experience with proc-macros or this code in particular either.

@@ -186,12 +188,14 @@ impl<Address, Index, Call, Signature> fmt::Debug for UncheckedMortalCompactExtri
}
}

#[cfg(test)]
Copy link
Contributor

Choose a reason for hiding this comment

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

is this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. probably a merge error

@gavofyork gavofyork added the A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it. label May 14, 2019
@gavofyork gavofyork closed this May 24, 2019
@siman
Copy link
Contributor

siman commented May 24, 2019

No automatically generated type metadata anymore?

@xlc
Copy link
Contributor Author

xlc commented May 24, 2019

We still plan to do it but unlikely within next two weeks.

@folsen
Copy link
Contributor

folsen commented May 30, 2019

Just a note here, the closing is not because we don't want the feature, but because the PR had gotten stale enough that it's easier to re-open fresh when ready than trying to resolve all merge conflicts, we regularly close stale issues and PRs because of uncertainty around status, so again, not an indication of desire to have it or not.

@siman
Copy link
Contributor

siman commented May 30, 2019

Great!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A3-in_progress Pull request is in progress. No review needed at this stage. A5-stale Pull request did not receive any updates in a long time. No review needed at this stage. Close it.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fully self-descriptive metadata types