Git Product home page Git Product logo

desub's Introduction

tests

De[code] Sub[strate]

โ€  This software is experimental, and not intended for production use yet. Use at your own risk.

Encompassing decoder for substrate/polkadot/kusama types.

Gets type definitions from polkadot-js via JSON and decodes them into components that outline types and make decoding byte-strings possible, as long as the module/generic type name are known.

Supports Metadata versions from v8, which means all of Kusama (from CC1). Older networks are not supported (E.G Alexander).

  • makes decoding generic types from the substrate rpc possible
  • requires parsing JSON with type definitions, and implementing traits TypeDetective and Decoder in order to work for arbitrary chains. However, if the JSON follows the same format as PolkadotJS definitions (look at definitions.json and overrides.json) it would be possible to simply deserialize into Polkadot structs and utilize those. The decoding itself is generic enough to allow it.
  • types must adhere to the conventions set out by polkadot decoding
    • type definitions for Polkadot (Kusama) are taken from Polkadot.js and deserialized into Rust (extras/polkadot)

Currently Supported Metadata Versions (From Kusama CC1):

  • V8
  • V9
  • V10
  • V11
  • V12
  • V13
  • V14

(Tentative) Release & Maintenence

Note: Release description is in no way complete because of current & active development for legacy desub types & scale-info based types. it is purely here as a record for things that should be taken into account in the future

  • Depending on changes in legacy desub code, bump version in Cargo.toml for desub/, desub-current/, desub-legacy/, desub-common/, desub-json-resolver/
  • note upgrade-blocks present here and modify the hard-coded upgrade blocks as necessary in the desub runtimes.rs file.
  • Take note of PR's that have been merged since the last release.
    • look over CHANGELOG. Make sure to include any PR's that were missed in the Unreleased section.
    • Move changes in Unreleased section to a new section corresponding to the version being released, making sure to keep the Unreleased header.
  • make a PR with these changes
  • once PR is merged, push a tag in the form vX.X.X (E.G v0.1.0)
git tag v0.1.0
git push --tags origin master
  • Once tags are pushed, a github workflow will start that will draft a release. You should be able to find the workflow running under Actions in the github repository.
    • NOTE: If something goes wrong it is OK. Delete the tag from the repo, re-create the tag locally and re-push. The workflow will run whenever a tag with the correct form is pushed. If more changes need to be made to the repo that will require another PR.
  • Once the workflow finishes, make changes to the resulting draft release if necessary, and hit publish.
  • Once published on github, publish each crate that has changed to crates.io. Refer to this for how to publish to crates.io.

desub's People

Contributors

dependabot[bot] avatar dvdplm avatar gnunicorn avatar insipx avatar jsdw avatar kianenigma avatar nuke-web3 avatar sergejparity avatar yjhmelody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

desub's Issues

Repo settings have been changed.

Repository's settings have been updated in the following ways:

  • protected branch set to "master"
  • status checks must pass in order for a pull request to be merged

other settings:

branch: "master",
required_status_checks: {
  contexts: ["cla"],
  strict: false
},
enforce_admins: true,
required_pull_request_reviews: null,
restrictions: null

For more information, see octokit documentation

This issue was created for informational purposes only. Feel free to close this issue at any time.

Regards,
cla-bot-2020

Create a Release Doc/Release GH Actions

Create RELEASE.md describing how to release desub. Should also create a release workflow with GitHub actions that creates a changelog and publishes a release on github.

The tricky part here is figuring out an ergonomic "vendoring flow" where the git dependencies are vendored and Cargo.toml rewritten, then the crate released. We'll publish sp-core and sp-runtime and that should sort out this problem.

Use trait for Metadata

instead of a struct that older metadata versions are converted into, use a trait which older metadata versions implement

This will cause less future compilation issues, and create an easier api to use

Instead of converting each metadata version into one superstruct, we could define a trait with common functions such as module_by_index and impl trait

Keep more verbose information about each extrinsic

A way to show the tree of calls along with argument names, type names, and docs for each argument. The calls can be nested (if you use batch or proxy) so that the full tree might be rather large. But the goal is to basically give someone a lot of confidence that the thing they plan to sign is in fact what they intended to do.

Originally posted by @Noch3 in #58 (comment)

@jsdw would be good to get your input

Decode storage entries in V14

Be able to decode storage (key/value pairs base on StorageEntry and related metadata).

This will include generating a hash-lookup table based on the possible storage key values in the metadata

Rename crates to something more descriptive

the plan is to rename the core, core_v14, and possibly extras crate to better reflect their roles in the codebase. It should be kept in mind that at some point there will be a facade crate that ties what currently is core and core_v14 together.

my suggestion is:

  • rename core to legacy
  • rename core_v14 to core
  • facade name is ??? (something cool maybe, maybe just desub?, desub-typeinfo?)

facade will be the crate that has the latest metadata decoding enabled by default, and then will have features that enables legacy decoding as well.

Check bounds on cursor in `decode_single`

If the cursor indexes out-of-bounds on our data element (the encoded extrinsic) in decode_single desub panics. It would be better to provide a contextual error message on where the out-of-bounds occurred, and possibly an error backtrace if possible and return to the caller instead.

debug output of ValueDef doesn't show type structure

Which of these nodes is a Variant or a Primitive or a Composite or Named or Unnamed? I can guess... but it would be nice if the debug helped more by pointing out what ValueDef it is. It's not easy to match on stuff.

V1 (
    Value {
        value:  {
            parents: Value {
                value: U8(
                    0,
                ),
                context: TypeId(
                    2,
                ),
            },
            interior: Value {
                value: X1 (
                    Value {
                        value: Parachain (
                            Value {
                                value: U32(
                                    2012,
                                ),
                                context: TypeId(
                                    114,
                                ),
                            },
                        ),
                        context: TypeId(
                            113,
                        ),
                    },
                ),
                context: TypeId(
                    112,
                ),
            },
        },
        context: TypeId(
            111,
        ),
    },
)

Make all decoders `pub`

I'm continually running into "I need this sub-decoder but it's not pub" problem. How do you feel about exposing these?

Allow usage of Decoder without parsing metadata twice

Because of how Decoder is written, I can't construct a Decoder without consuming Metadata. And once I've done that, I can't get the metadata back out without consuming the Decoder. Since virtually all of the methods on Decoder are already parameterized by lifetime, perhaps Decoder itself should be parameterized on lifetime. Or perhaps all those method should simply be a trait on Metadata instead and get rid of Decoder.

Desub V2

I'd like to revive Desub. Given the various things that have changed in the last couple of years, I'm proposing to restructure and rework the library with the following rough goals:

  • To expose an as-simple-as-possible interface for decoding arbitrary blocks/state. With the right configuration it should be possible to tie this in with pulling from a DB or from RPCs to index chain state/blocks in some arbitrary way.
  • To be able to reuse the extrinsic/state decoding logic in eg Subxt (without pulling in a load of unnecessary stuff).
  • To lean on the modern libraries we have nowadays like scale_value and scale_decode.
  • Excellent errors, examples, and a CLI utility to help one to iteratively construct/add type mappings for a chain (but we'll provide default mappings from PJS which should get things going).

Some more specific details:

Decoding arbitrary bytes

This is the core thing that needs doing, regardless of metadata version.
To be general over how we decode types, TypeDecoder is a trait that can be implemented on anything capable of decoding bytes into outputs.

It might look something like:

trait TypeDecoder {
    /// Something that identifies a type. in V14/V15 metadata
    /// this might be a u32, and in earlier versions it might be
    /// some struct with chain and type name (maybe spec too).
    type TypeId;

    /// Error type we'll return if decoding fails.
    type Error;

    /// Given a type ID and a cursor, attempt to decode the type into a
    /// `scale_value::Value`, consuming from the cursor. The Value will
    /// contain the `TypeId` as context, so that we have information on the
    /// type that the Value came from.
    fn decode_type<V: scale_decode::Visitor>(
        &self,
        type_id: &Self::TypeId,
        bytes: &mut &[u8],
        visitor: V
    ) -> Result<V::Value, Self::Error>;
}

This could be implemented directly on a scale_info::PortableRegistry. For older versions of metadata, we'll need a set of manual type mappings and we'll implement it on that.

Hopefully decode_type can take a scale_decode::Visitor, and will call that as needed. This would allow us to do things like skip over bytes for more decode flexibility, or decode to scale_value::Values or whatever, and generally leverage the Visitor stuff better. In V14 and V15 this would all "just work", but we'd need to implement a visitor based decoder to work with legacy type mappings.

Decoding extrinsics

We can use the type decoder above, as well as something capable of getting the relevant type IDs, to decode extrinsics

/// Provide the type information needed to decode extrinsics. This allows
/// us to write `decode_Extrinsic_v4` once for all metadata versions
trait ExtrinsicTypes {
    /// Expected to be compatible with a `TypeDecoder`, so that we can
    /// use the type IDs to actually decode some things.
    type TypeId;

    // Signature type
    fn signature_type(&self) -> Self::TypeId;

    // Address type
    fn address_type(&self) -> Self::TypeId;

    // Info on how to decode each of the signed extra types.
    fn signed_extra_types(&self) -> impl Iterator<Item = (&str, Self::TypeId)>;

    // Names of each argument and the type IDs to help decode them.
    fn argument_types(&self, pallet_id: u8, call_id: u8) -> impl Iterator<Item = (&str, Self::TypeId)>;

}

// Now, combining the above with a compatible type decoder should let us decode extrinsics.
fn decode_extrinsic<D, E, Id>(extrinsic_types: &E decoder: &D, bytes: &mut &[u8]) -> Result<ExtrinsicDetails<Id>, Error<D::Error>>
where
    D: TypeDecoder<TypeId = Id>,
    E: ExtrinsicTypes<TypeId = Id>
{
    // We should be able to write this logic once, and then reuse it for any V4 extrinsic
}

Potentially decode_extrinsic could take a visitor too to define how the args are decoded, or it just stores byte offsets for things after decoding everything with an IgnoreVisitor and allows the user to then decode each signed extension etc into Values or concrete types or whatever (one might want to decode eg CheckMortality into a concrete type, but take the call data into Values or whatever.

Decoding storage

I imagine that we can follow the same pattern as for decoding extrinsics; a trait to fetch whatever information we need and hand it back in some generic way, and then a decode_storage type call which can decode into some StorageDetails struct.

General structure

We might end up with the following general crate/export structure:

// The core decode logic.
use desub::{
    // The core traits:
    TypeDecoder,
    ExtrinsicTypes,
    StorageTypes,
    // The core functionality we expose is quite simple; start with eg:
    decode_extrinsic,
    decode_storage_entry,
    Error,
    // Define the type mapping stuff that legacy metadatas need to be
    // able to decode types, and impl the above traits on the relevant
    // decoding and old metadatas.
    #[cfg(feature = "legacy")]
    legacy::{
        LegacyTypeDecoder
        // legacy impls for the above live here too; if using this in
        // Subxt we should be able to not include the legacy stuff
    }
    #[cfg(feature = "current")]
    current::{
        // Probably just impls of the traits for V14/V15 metadatas here.
    }
};

After this initial restructuring is done, we might also end up wanting to add:

  • A desub CLI utility tool which can:
    • Scan a node to find where all of the spec version change (or runtime update) blocks are and which metadata version is in use for each spec version (binary chopping to locate the blocks with the diffs shouldnt take toooo long). Could dump this all to a file, and dump the metadatas too. Would be very easy to then scan and decode all blocks (assuming correct type mappings).
    • Check whether an RPC connection is an archive node (maybe by querying for block 1 hash and then seeing whether we can get state there)
    • Sample and attempt to decode blocks in each different spec version pre-V14 to help a user to construct and validate type mappings and spot errors. (maybe allow to scan in one spec version at a time to help the building of mappings)

We can raise separate issues to implement these bits later.

desub compile to wasm

Would be cool to be able to use this in web contexts. I think I can get away with not needing subxt on the web but I definitely need desub capabilities.

Repo settings have been changed.

Repository's settings have been updated in the following ways:

  • protected branch set to "master"
  • status checks must pass in order for a pull request to be merged

other settings:

branch: "master",
required_status_checks: {
  contexts: ["cla"],
  strict: false
},
enforce_admins: true,
required_pull_request_reviews: null,
restrictions: null

For more information, see octokit documentation

This issue was created for informational purposes only. Feel free to close this issue at any time.

Regards,
cla-bot-2020

Should SubstrateType more consistent with RustTypeMarker ?

I noticed that SubstrateType is a stateful version of RustTypeMarker. And I think them should keep similiar as possible.

I think the following common types should be defined in a new enum type.

pub enum SubstrateType {
	/// 512-bit hash type
	H512(primitives::H512),
	/// 256-bit hash type
	H256(primitives::H256),

	/// Recursive Call Type
	Call(Vec<(String, SubstrateType)>),
	/// Era
	Era(runtime_primitives::generic::Era),

	/// Vote
	#[serde(with = "RemoteVote")]
	GenericVote(pallet_democracy::Vote),
 ...

And SubstrateType should be:

pub enum SubstrateType<T> {
     Common(T),
 ...

Other people can compose their own implementations more easily.
And can ensure that the core changes as little as possible

How about your thinking?

Fix CI

Coverage isn't properly setup through GH actions so it always fails

Why can't we use this project at the production level yet?

โ€  This software is experimental, and not intended for production use yet. Use at your own risk.

What's the problem? If I do, when can I use it at the production level?

If V15 comes out one day, I'm curious if you'll apply for it :)

Investigate a way to incorporate tx-decoder into CI

comment from #42

In the future I think tx-decoder could be a really useful tool for both regression and unit testing. It would require an archive server running somewhere with just blocks synced, and github actions could connect to it via a read-only user. Not sure if there is some precedent for that already (maybe iffy connected to a postgres server from actions?) but It could keep westend synced and then decode through all the extrinsics for the latest/previous runtime to check for any errors before releasing/deploying to master

join pretty and detailed_pretty

pretty and detailed_pretty display functions aim to accomplish the same thing in desub_core::decoder::Metadata. This should just be the default for the trait std::fmt::Display

JSON Definitions should be imported at runtime

Currently JSON definitions are declared statically and included in the binary. Ideally, however, there should be an API to load JSON definitions at runtime instead of using the static definitions. Definitions loaded at runtime would have to follow the same spec as defined in the static definitions in order to deserialize correctly.

It would be nice to have this specification outlined in the Wiki, either here or in Substrate Archive.

Benchmarks for type decoding

It would be good to have a few benchmarks for decoding types from JSON, perhaps taking inspiration from resolver::tests::should_get_types_from_json and other slow/heavy duty tests. Ideally we should run the benches in CI and store the results to check for regressions (fine to do in separate PRs though).

Create Benchmarks

Can be in a folder next to integration_tests.

Benchmarks + Profiling would be useful for seeing where desub can be sped up, and comparing how the library stands up to Polkadot.js/other decoding methods.

Repo settings have been changed.

Repository's settings have been updated in the following ways:

  • protected branch set to "master"
  • status checks must pass in order for a pull request to be merged

other settings:

branch: "master",
required_status_checks: {
  contexts: ["cla"],
  strict: false
},
enforce_admins: true,
required_pull_request_reviews: null,
restrictions: null

For more information, see octokit documentation

This issue was created for informational purposes only. Feel free to close this issue at any time.

Regards,
cla-bot-2020

Feature-Gate legacy types

JSON Definitions from PolkadotJS are already feature-gated under 'extras', but codepaths for legacy (pre V13) metadata should be feature gated once V14 is supported

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.