mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 00:48:01 +00:00
Merge remote-tracking branch 'origin/master' into lexnv/light_client_support
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
@@ -35,7 +35,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Cargo test
|
||||
uses: actions-rs/cargo@v1.0.3
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Install cargo-hack
|
||||
uses: baptiste0928/cargo-install@v2
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
components: rustfmt
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Cargo fmt
|
||||
uses: actions-rs/cargo@v1.0.3
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Check internal documentation links
|
||||
run: RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc -vv --workspace --no-deps --document-private-items
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Install cargo-nextest
|
||||
run: cargo install cargo-nextest
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
uses: browser-actions/setup-chrome@latest
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@060bda31e0be4f453bb6ed2d7e5427b31734ad01 # v2.3.0
|
||||
uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0
|
||||
|
||||
- name: Download Substrate
|
||||
run: |
|
||||
|
||||
Generated
+91
-109
@@ -382,9 +382,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
checksum = "3f1e31e207a6b8fb791a38ea3105e6cb541f55e4d029902d3039a4ad07cc4105"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
@@ -452,7 +452,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -550,9 +550,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.2"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
@@ -683,9 +683,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.2.7"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
||||
checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -694,23 +694,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.2.7"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
||||
checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex 0.4.1",
|
||||
"clap_lex 0.5.0",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.2.0"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
||||
checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -729,9 +729,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
@@ -1260,9 +1260,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common",
|
||||
@@ -1824,7 +1824,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2056,9 +2056,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
@@ -2293,9 +2293,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -2693,7 +2693,7 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2702,7 +2702,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3061,13 +3061,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.1",
|
||||
"regex-syntax 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3087,9 +3087,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@@ -3135,9 +3135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.13"
|
||||
version = "0.36.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658"
|
||||
checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -3157,7 +3157,7 @@ dependencies = [
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.7",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@@ -3191,7 +3191,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3434,9 +3434,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1"
|
||||
checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -3544,7 +3544,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3553,7 +3553,7 @@ version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
@@ -3608,7 +3608,7 @@ dependencies = [
|
||||
"async-lock",
|
||||
"async-std",
|
||||
"atomic",
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.1",
|
||||
"bip39",
|
||||
"blake2-rfc",
|
||||
"bs58",
|
||||
@@ -3723,34 +3723,34 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-application-crypto"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-arithmetic"
|
||||
version = "6.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"integer-sqrt",
|
||||
"num-traits",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-core"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"bitflags",
|
||||
@@ -3778,11 +3778,11 @@ dependencies = [
|
||||
"secp256k1",
|
||||
"secrecy",
|
||||
"serde",
|
||||
"sp-core-hashing 5.0.0",
|
||||
"sp-core-hashing",
|
||||
"sp-debug-derive",
|
||||
"sp-externalities",
|
||||
"sp-runtime-interface",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-storage",
|
||||
"ss58-registry",
|
||||
"substrate-bip39",
|
||||
@@ -3794,47 +3794,32 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-core-hashing"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
"sp-std 5.0.0",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-core-hashing"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
"digest 0.10.6",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
"sp-std 7.0.0",
|
||||
"sp-std",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-core-hashing-proc-macro"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sp-core-hashing 5.0.0",
|
||||
"sp-core-hashing",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-debug-derive"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3844,18 +3829,18 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-externalities"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"environmental",
|
||||
"parity-scale-codec",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-storage",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-io"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"ed25519",
|
||||
@@ -3871,7 +3856,7 @@ dependencies = [
|
||||
"sp-keystore",
|
||||
"sp-runtime-interface",
|
||||
"sp-state-machine",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-tracing",
|
||||
"sp-trie",
|
||||
"tracing",
|
||||
@@ -3881,7 +3866,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-keyring"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"sp-core",
|
||||
@@ -3892,7 +3877,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-keystore"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"parity-scale-codec",
|
||||
@@ -3905,7 +3890,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-panic-handler"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"lazy_static",
|
||||
@@ -3915,7 +3900,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-runtime"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"either",
|
||||
"hash256-std-hasher",
|
||||
@@ -3930,14 +3915,14 @@ dependencies = [
|
||||
"sp-arithmetic",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-weights",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-runtime-interface"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"impl-trait-for-tuples",
|
||||
@@ -3945,7 +3930,7 @@ dependencies = [
|
||||
"primitive-types",
|
||||
"sp-externalities",
|
||||
"sp-runtime-interface-proc-macro",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-storage",
|
||||
"sp-tracing",
|
||||
"sp-wasm-interface",
|
||||
@@ -3955,7 +3940,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-runtime-interface-proc-macro"
|
||||
version = "6.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro-crate",
|
||||
@@ -3967,7 +3952,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-state-machine"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"log",
|
||||
@@ -3978,7 +3963,7 @@ dependencies = [
|
||||
"sp-core",
|
||||
"sp-externalities",
|
||||
"sp-panic-handler",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-trie",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
@@ -3987,34 +3972,28 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-std"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
|
||||
[[package]]
|
||||
name = "sp-std"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
|
||||
[[package]]
|
||||
name = "sp-storage"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
"sp-debug-derive",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-tracing"
|
||||
version = "6.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber 0.2.25",
|
||||
@@ -4023,7 +4002,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-trie"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"hash-db",
|
||||
@@ -4036,7 +4015,7 @@ dependencies = [
|
||||
"scale-info",
|
||||
"schnellru",
|
||||
"sp-core",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"trie-db",
|
||||
@@ -4046,7 +4025,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-version"
|
||||
version = "5.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
@@ -4055,7 +4034,7 @@ dependencies = [
|
||||
"serde",
|
||||
"sp-core-hashing-proc-macro",
|
||||
"sp-runtime",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"sp-version-proc-macro",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -4063,7 +4042,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-version-proc-macro"
|
||||
version = "4.0.0-dev"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"proc-macro2",
|
||||
@@ -4074,13 +4053,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-wasm-interface"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"impl-trait-for-tuples",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
"wasmi 0.13.2",
|
||||
"wasmtime",
|
||||
]
|
||||
@@ -4088,7 +4067,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sp-weights"
|
||||
version = "4.0.0"
|
||||
source = "git+https://github.com/paritytech/substrate.git?branch=lexnv/update_wasm#6ce93ba1277441551277680f8ce41dd49ea00b5c"
|
||||
source = "git+https://github.com/paritytech/substrate.git#b0a16c2c83ea2b2872f2211e625401554d91fb12"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
@@ -4097,7 +4076,7 @@ dependencies = [
|
||||
"sp-arithmetic",
|
||||
"sp-core",
|
||||
"sp-debug-derive",
|
||||
"sp-std 5.0.0",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4214,7 +4193,6 @@ dependencies = [
|
||||
"impl-serde",
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
"parking_lot",
|
||||
"primitive-types",
|
||||
"scale-bits",
|
||||
"scale-decode",
|
||||
@@ -4225,7 +4203,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smoldot-light",
|
||||
"sp-core",
|
||||
"sp-core-hashing 5.0.0",
|
||||
"sp-core-hashing",
|
||||
"sp-keyring",
|
||||
"sp-runtime",
|
||||
"sp-version",
|
||||
@@ -4241,14 +4219,17 @@ dependencies = [
|
||||
name = "subxt-cli"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"clap 4.3.0",
|
||||
"color-eyre",
|
||||
"frame-metadata",
|
||||
"hex",
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"scale-value",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"subxt",
|
||||
"subxt-codegen",
|
||||
"subxt-metadata",
|
||||
"syn 2.0.16",
|
||||
@@ -4305,7 +4286,8 @@ dependencies = [
|
||||
"frame-metadata",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core-hashing 8.0.0",
|
||||
"sp-core-hashing",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4528,9 +4510,9 @@ checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.9"
|
||||
version = "0.19.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f"
|
||||
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -4693,8 +4675,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest 0.10.6",
|
||||
"rand 0.7.3",
|
||||
"digest 0.10.7",
|
||||
"rand 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
@@ -5152,7 +5134,7 @@ checksum = "7ab182d5ab6273a133ab88db94d8ca86dc3e57e43d70baaa4d98f94ddbd7d10a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"rustix 0.36.13",
|
||||
"rustix 0.36.14",
|
||||
"wasmtime-asm-macros",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
@@ -5217,7 +5199,7 @@ dependencies = [
|
||||
"memoffset",
|
||||
"paste",
|
||||
"rand 0.8.5",
|
||||
"rustix 0.36.13",
|
||||
"rustix 0.36.14",
|
||||
"wasmtime-asm-macros",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-fiber",
|
||||
|
||||
+9
-10
@@ -33,7 +33,7 @@ assert_matches = "1.5.0"
|
||||
base58 = { version = "0.2.0" }
|
||||
bitvec = { version = "1", default-features = false }
|
||||
blake2 = { version = "0.10.4", default-features = false }
|
||||
clap = { version = "4.2.5", features = ["derive", "cargo"] }
|
||||
clap = { version = "4.3.0", features = ["derive", "cargo"] }
|
||||
criterion = "0.4"
|
||||
codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false }
|
||||
color-eyre = "0.6.1"
|
||||
@@ -48,13 +48,13 @@ hex = "0.4.3"
|
||||
heck = "0.4.1"
|
||||
impl-serde = { version = "0.4.0" }
|
||||
jsonrpsee = { version = "0.16" }
|
||||
parking_lot = "0.12.0"
|
||||
pretty_assertions = "1.0.0"
|
||||
primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "scale-info", "serde"] }
|
||||
proc-macro2 = "1.0.55"
|
||||
proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.58"
|
||||
quote = "1.0.27"
|
||||
regex = "1.8.1"
|
||||
scale-info = "2.6.0"
|
||||
scale-info = "2.7.0"
|
||||
scale-value = "0.7.0"
|
||||
scale-bits = "0.3"
|
||||
scale-decode = "0.5.0"
|
||||
@@ -68,7 +68,6 @@ tracing = "0.1.34"
|
||||
tracing-wasm = "0.2.1"
|
||||
tracing-subscriber = "0.3.17"
|
||||
trybuild = "1.0.79"
|
||||
proc-macro-error = "1.0.4"
|
||||
wabt = "0.10.0"
|
||||
wasm-bindgen-test = "0.3.24"
|
||||
which = "4.4.0"
|
||||
@@ -79,11 +78,11 @@ tokio-stream = "0.1.14"
|
||||
futures-util = "0.3.28"
|
||||
|
||||
# Substrate crates:
|
||||
sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "lexnv/update_wasm", default-features = false }
|
||||
sp-core-hashing = { git = "https://github.com/paritytech/substrate.git", branch = "lexnv/update_wasm" }
|
||||
sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "lexnv/update_wasm" }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "lexnv/update_wasm" }
|
||||
sp-version = { git = "https://github.com/paritytech/substrate.git", branch = "lexnv/update_wasm" }
|
||||
sp-core = { git = "https://github.com/paritytech/substrate.git", default-features = false }
|
||||
sp-core-hashing = { git = "https://github.com/paritytech/substrate.git" }
|
||||
sp-runtime = { git = "https://github.com/paritytech/substrate.git" }
|
||||
sp-keyring = { git = "https://github.com/paritytech/substrate.git" }
|
||||
sp-version = { git = "https://github.com/paritytech/substrate.git" }
|
||||
|
||||
# Subxt workspace crates:
|
||||
subxt = { version = "0.28.0", path = "subxt" }
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,6 +19,7 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
subxt-codegen = { workspace = true }
|
||||
subxt-metadata = { workspace = true }
|
||||
subxt = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
color-eyre = { workspace = true }
|
||||
@@ -26,6 +27,8 @@ serde_json = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
frame-metadata = { workspace = true }
|
||||
codec = { package = "parity-scale-codec", workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
scale-value = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
jsonrpsee = { workspace = true, features = ["async-client", "client-ws-transport", "http-client"] }
|
||||
tokio = { workspace = true }
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
|
||||
use clap::Parser as ClapParser;
|
||||
use codec::Decode;
|
||||
use color_eyre::eyre::{self, WrapErr};
|
||||
use frame_metadata::{
|
||||
v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed, META_RESERVED,
|
||||
};
|
||||
use color_eyre::eyre::WrapErr;
|
||||
use jsonrpsee::client_transport::ws::Uri;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use subxt_codegen::utils::MetadataVersion;
|
||||
use subxt_metadata::{get_pallet_hash, metadata_v14_to_latest, MetadataHasher};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
/// Verify metadata compatibility between substrate nodes.
|
||||
#[derive(Debug, ClapParser)]
|
||||
@@ -66,9 +63,9 @@ async fn handle_pallet_metadata(
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node, version).await?;
|
||||
|
||||
match metadata.pallets.iter().find(|pallet| pallet.name == name) {
|
||||
match metadata.pallet_by_name(name) {
|
||||
Some(pallet_metadata) => {
|
||||
let hash = get_pallet_hash(&metadata.types, pallet_metadata);
|
||||
let hash = pallet_metadata.hash();
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {node:?} has pallet metadata hash {hex_hash:?}");
|
||||
|
||||
@@ -97,7 +94,7 @@ async fn handle_full_metadata(nodes: &[Uri], version: MetadataVersion) -> color_
|
||||
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node, version).await?;
|
||||
let hash = MetadataHasher::new().hash(&metadata);
|
||||
let hash = metadata.hasher().hash();
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {node:?} has metadata hash {hex_hash:?}",);
|
||||
|
||||
@@ -119,26 +116,8 @@ async fn handle_full_metadata(nodes: &[Uri], version: MetadataVersion) -> color_
|
||||
async fn fetch_runtime_metadata(
|
||||
url: &Uri,
|
||||
version: MetadataVersion,
|
||||
) -> color_eyre::Result<RuntimeMetadataV15> {
|
||||
) -> color_eyre::Result<Metadata> {
|
||||
let bytes = subxt_codegen::utils::fetch_metadata_bytes(url, version).await?;
|
||||
|
||||
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,
|
||||
metadata.0,
|
||||
META_RESERVED
|
||||
));
|
||||
}
|
||||
|
||||
match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => Ok(metadata_v14_to_latest(v14)),
|
||||
RuntimeMetadata::V15(v15) => Ok(v15),
|
||||
_ => Err(eyre::eyre!(
|
||||
"Node {:?} with unsupported metadata version: {:?}",
|
||||
url,
|
||||
metadata.1
|
||||
)),
|
||||
}
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
use clap::Args;
|
||||
use color_eyre::eyre::eyre;
|
||||
use scale_info::form::PortableForm;
|
||||
use scale_info::{PortableRegistry, Type, TypeDef, TypeDefVariant};
|
||||
use scale_value::{Composite, ValueDef};
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
use std::write;
|
||||
|
||||
use subxt::tx;
|
||||
use subxt::utils::H256;
|
||||
use subxt::{
|
||||
config::SubstrateConfig,
|
||||
metadata::{types::PalletMetadata, Metadata},
|
||||
OfflineClient,
|
||||
};
|
||||
|
||||
use crate::utils::type_description::print_type_description;
|
||||
use crate::utils::type_example::print_type_examples;
|
||||
use crate::utils::with_indent;
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct CallsSubcommand {
|
||||
call: Option<String>,
|
||||
#[clap(required = false)]
|
||||
trailing_args: Vec<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn explore_calls(
|
||||
command: CallsSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: PalletMetadata,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name();
|
||||
|
||||
// get the enum that stores the possible calls:
|
||||
let (calls_enum_type_def, _calls_enum_type) =
|
||||
get_calls_enum_type(pallet_metadata, metadata.types())?;
|
||||
|
||||
// if no call specified, show user the calls to choose from:
|
||||
let Some(call_name) = command.call else {
|
||||
let available_calls = print_available_calls(calls_enum_type_def, pallet_name);
|
||||
println!("Usage:\n subxt explore {pallet_name} calls <CALL>\n explore a specific call within this pallet\n\n{available_calls}", );
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// if specified call is wrong, show user the calls to choose from (but this time as an error):
|
||||
let Some(call) = calls_enum_type_def.variants.iter().find(|variant| variant.name.to_lowercase() == call_name.to_lowercase()) else {
|
||||
let available_calls = print_available_calls(calls_enum_type_def, pallet_name);
|
||||
let description = format!("Usage:\n subxt explore {pallet_name} calls <CALL>\n explore a specific call within this pallet\n\n{available_calls}", );
|
||||
return Err(eyre!("\"{call_name}\" call not found in \"{pallet_name}\" pallet!\n\n{description}"));
|
||||
};
|
||||
|
||||
// collect all the trailing arguments into a single string that is later into a scale_value::Value
|
||||
let trailing_args = command.trailing_args.join(" ");
|
||||
|
||||
// if no trailing arguments specified show user the expected type of arguments with examples:
|
||||
if trailing_args.is_empty() {
|
||||
let mut type_description = print_type_description(&call.fields, metadata.types())?;
|
||||
type_description = with_indent(type_description, 4);
|
||||
let mut type_examples = print_type_examples(&call.fields, metadata.types(), "SCALE_VALUE")?;
|
||||
type_examples = with_indent(type_examples, 4);
|
||||
let mut output = String::new();
|
||||
write!(output, "Usage:\n subxt explore {pallet_name} calls {call_name} <SCALE_VALUE>\n construct the call by providing a valid argument\n\n")?;
|
||||
write!(
|
||||
output,
|
||||
"The call expect expects a <SCALE_VALUE> with this shape:\n{type_description}\n\n{}\n\nYou may need to surround the value in single quotes when providing it as an argument."
|
||||
, &type_examples[4..])?;
|
||||
println!("{output}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// parse scale_value from trailing arguments and try to create an unsigned extrinsic with it:
|
||||
let value = scale_value::stringify::from_str(&trailing_args).0.map_err(|err| eyre!("scale_value::stringify::from_str led to a ParseError.\n\ntried parsing: \"{}\"\n\n{}", trailing_args, err))?;
|
||||
let value_as_composite = value_into_composite(value);
|
||||
let offline_client = mocked_offline_client(metadata.clone());
|
||||
let payload = tx::dynamic(pallet_name, call_name, value_as_composite);
|
||||
let unsigned_extrinsic = offline_client.tx().create_unsigned(&payload)?;
|
||||
let hex_bytes = format!("0x{}", hex::encode(unsigned_extrinsic.encoded()));
|
||||
println!("Encoded call data:\n {hex_bytes}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_available_calls(pallet_calls: &TypeDefVariant<PortableForm>, pallet_name: &str) -> String {
|
||||
if pallet_calls.variants.is_empty() {
|
||||
return format!("No <CALL>'s available in the \"{pallet_name}\" pallet.");
|
||||
}
|
||||
let mut output = format!("Available <CALL>'s in the \"{pallet_name}\" pallet:");
|
||||
|
||||
let mut strings: Vec<_> = pallet_calls.variants.iter().map(|c| &c.name).collect();
|
||||
strings.sort();
|
||||
for variant in strings {
|
||||
output.push_str("\n ");
|
||||
output.push_str(variant);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn get_calls_enum_type<'a>(
|
||||
pallet: PalletMetadata,
|
||||
registry: &'a PortableRegistry,
|
||||
) -> color_eyre::Result<(&'a TypeDefVariant<PortableForm>, &'a Type<PortableForm>)> {
|
||||
let call_ty = pallet
|
||||
.call_ty_id()
|
||||
.ok_or(eyre!("The \"{}\" pallet has no calls.", pallet.name()))?;
|
||||
let calls_enum_type = registry
|
||||
.resolve(call_ty)
|
||||
.ok_or(eyre!("calls type with id {} not found.", call_ty))?;
|
||||
|
||||
// should always be a variant type, where each variant corresponds to one call.
|
||||
let TypeDef::Variant(calls_enum_type_def) = &calls_enum_type.type_def else {
|
||||
return Err(eyre!("calls type is not a variant"));
|
||||
};
|
||||
Ok((calls_enum_type_def, calls_enum_type))
|
||||
}
|
||||
|
||||
/// The specific values used for construction do not matter too much, we just need any OfflineClient to create unsigned extrinsics
|
||||
fn mocked_offline_client(metadata: Metadata) -> OfflineClient<SubstrateConfig> {
|
||||
let genesis_hash =
|
||||
H256::from_str("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3")
|
||||
.expect("Valid hash; qed");
|
||||
|
||||
let runtime_version = subxt::rpc::types::RuntimeVersion {
|
||||
spec_version: 9370,
|
||||
transaction_version: 20,
|
||||
other: Default::default(),
|
||||
};
|
||||
|
||||
OfflineClient::<SubstrateConfig>::new(genesis_hash, runtime_version, metadata)
|
||||
}
|
||||
|
||||
/// composites stay composites, all other types are converted into a 1-fielded unnamed composite
|
||||
fn value_into_composite(value: scale_value::Value) -> scale_value::Composite<()> {
|
||||
match value.value {
|
||||
ValueDef::Composite(composite) => composite,
|
||||
_ => Composite::Unnamed(vec![value]),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use clap::Args;
|
||||
use color_eyre::eyre::eyre;
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
use subxt::metadata::{types::PalletMetadata, Metadata};
|
||||
|
||||
use crate::utils::type_description::print_type_description;
|
||||
use crate::utils::{print_docs_with_indent, with_indent};
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct ConstantsSubcommand {
|
||||
constant: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn explore_constants(
|
||||
command: ConstantsSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: PalletMetadata,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name();
|
||||
let Some(constant_name) = command.constant else {
|
||||
let available_constants = print_available_constants(pallet_metadata, pallet_name);
|
||||
println!("Usage:\n subxt explore {pallet_name} constants <CONSTANT>\n explore a specific call within this pallet\n\n{available_constants}", );
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// if specified constant is wrong, show user the constants to choose from (but this time as an error):
|
||||
let Some(constant) = pallet_metadata.constants().find(|constant| constant.name().to_lowercase() == constant_name.to_lowercase()) else {
|
||||
let available_constants = print_available_constants(pallet_metadata, pallet_name);
|
||||
let description = format!("Usage:\n subxt explore {pallet_name} constants <CONSTANT>\n explore a specific call within this pallet\n\n{available_constants}", );
|
||||
let err = eyre!("constant \"{constant_name}\" not found in \"{pallet_name}\" pallet!\n\n{description}");
|
||||
return Err(err);
|
||||
};
|
||||
|
||||
// docs
|
||||
let mut output = String::new();
|
||||
let doc_string = print_docs_with_indent(constant.docs(), 4);
|
||||
if !doc_string.is_empty() {
|
||||
write!(output, "Description:\n{doc_string}")?;
|
||||
}
|
||||
|
||||
// shape
|
||||
let mut type_description = print_type_description(&constant.ty(), metadata.types())?;
|
||||
type_description = with_indent(type_description, 4);
|
||||
write!(
|
||||
output,
|
||||
"\n\nThe constant has the following shape:\n{type_description}"
|
||||
)?;
|
||||
|
||||
// value
|
||||
let scale_val =
|
||||
scale_value::scale::decode_as_type(&mut constant.value(), constant.ty(), metadata.types())?;
|
||||
write!(
|
||||
output,
|
||||
"\n\nThe value of the constant is:\n {}",
|
||||
scale_value::stringify::to_string(&scale_val)
|
||||
)?;
|
||||
|
||||
println!("{output}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_available_constants(pallet_metadata: PalletMetadata, pallet_name: &str) -> String {
|
||||
if pallet_metadata.constants().len() == 0 {
|
||||
return format!("No <CONSTANT>'s available in the \"{pallet_name}\" pallet.");
|
||||
}
|
||||
let mut output = format!("Available <CONSTANT>'s in the \"{pallet_name}\" pallet:");
|
||||
let mut strings: Vec<_> = pallet_metadata.constants().map(|c| c.name()).collect();
|
||||
strings.sort();
|
||||
for constant in strings {
|
||||
output.push_str("\n ");
|
||||
output.push_str(constant);
|
||||
}
|
||||
output
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
use crate::utils::{print_docs_with_indent, FileOrUrl};
|
||||
use clap::{Parser as ClapParser, Subcommand};
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use std::write;
|
||||
|
||||
use codec::Decode;
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use crate::commands::explore::calls::{explore_calls, CallsSubcommand};
|
||||
use crate::commands::explore::constants::{explore_constants, ConstantsSubcommand};
|
||||
use crate::commands::explore::storage::{explore_storage, StorageSubcommand};
|
||||
|
||||
use subxt::Metadata;
|
||||
|
||||
mod calls;
|
||||
mod constants;
|
||||
mod storage;
|
||||
|
||||
/// Explore pallets, calls, call parameters, storage entries and constants. Also allows for creating (unsigned) extrinsics.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ## Pallets
|
||||
///
|
||||
/// Show the pallets that are available:
|
||||
/// ```
|
||||
/// subxt explore --file=polkadot_metadata.scale
|
||||
/// ```
|
||||
///
|
||||
/// ## Calls
|
||||
///
|
||||
/// Show the calls in a pallet:
|
||||
/// ```
|
||||
/// subxt explore Balances calls
|
||||
/// ```
|
||||
/// Show the call parameters a call expects:
|
||||
/// ```
|
||||
/// subxt explore Balances calls transfer
|
||||
/// ```
|
||||
/// Create an unsigned extrinsic from a scale value, validate it and output its hex representation
|
||||
/// ```
|
||||
/// subxt explore Grandpa calls note_stalled { "delay": 5, "best_finalized_block_number": 5 }
|
||||
/// # Encoded call data:
|
||||
/// # 0x2c0411020500000005000000
|
||||
/// subxt explore Balances calls transfer "{ \"dest\": v\"Raw\"((255, 255, 255)), \"value\": 0 }"
|
||||
/// # Encoded call data:
|
||||
/// # 0x24040607020cffffff00
|
||||
/// ```
|
||||
/// ## Constants
|
||||
///
|
||||
/// Show the constants in a pallet:
|
||||
/// ```
|
||||
/// subxt explore Balances constants
|
||||
/// ```
|
||||
/// ## Storage
|
||||
///
|
||||
/// Show the storage entries in a pallet
|
||||
/// ```
|
||||
/// subxt explore Alliance storage
|
||||
/// ```
|
||||
/// Show the types and value of a specific storage entry
|
||||
/// ```
|
||||
/// subxt explore Alliance storage Announcements [KEY_SCALE_VALUE]
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, ClapParser)]
|
||||
pub struct Opts {
|
||||
#[command(flatten)]
|
||||
file_or_url: FileOrUrl,
|
||||
pallet: Option<String>,
|
||||
#[command(subcommand)]
|
||||
pallet_subcommand: Option<PalletSubcommand>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
pub enum PalletSubcommand {
|
||||
Calls(CallsSubcommand),
|
||||
Constants(ConstantsSubcommand),
|
||||
Storage(StorageSubcommand),
|
||||
}
|
||||
|
||||
/// cargo run -- explore --file=../artifacts/polkadot_metadata.scale
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
// get the metadata
|
||||
let bytes = opts.file_or_url.fetch().await?;
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
// if no pallet specified, show user the pallets to choose from:
|
||||
let Some(pallet_name) = opts.pallet else {
|
||||
let available_pallets = print_available_pallets(&metadata);
|
||||
println!("Usage:\n subxt explore <PALLET>\n explore a specific pallet\n\n{available_pallets}", );
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// if specified pallet is wrong, show user the pallets to choose from (but this time as an error):
|
||||
let Some(pallet_metadata) = metadata.pallets().find(|pallet| pallet.name().to_lowercase() == pallet_name.to_lowercase()) else {
|
||||
return Err(eyre!("pallet \"{}\" not found in metadata!\n{}", pallet_name, print_available_pallets(&metadata)));
|
||||
};
|
||||
|
||||
// if correct pallet was specified but no subcommand, instruct the user how to proceed:
|
||||
let Some(pallet_subcomand) = opts.pallet_subcommand else {
|
||||
let docs_string = print_docs_with_indent(pallet_metadata.docs(), 4);
|
||||
let mut output = String::new();
|
||||
if !docs_string.is_empty() {
|
||||
write!(output, "Description:\n{docs_string}")?;
|
||||
}
|
||||
write!(output, "Usage:")?;
|
||||
write!(output, "\n subxt explore {pallet_name} calls\n explore the calls that can be made into this pallet")?;
|
||||
write!(output, "\n subxt explore {pallet_name} constants\n explore the constants held in this pallet")?;
|
||||
write!(output, "\n subxt explore {pallet_name} storage\n explore the storage values held in this pallet")?;
|
||||
println!("{output}");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match pallet_subcomand {
|
||||
PalletSubcommand::Calls(command) => explore_calls(command, &metadata, pallet_metadata),
|
||||
PalletSubcommand::Constants(command) => {
|
||||
explore_constants(command, &metadata, pallet_metadata)
|
||||
}
|
||||
PalletSubcommand::Storage(command) => {
|
||||
// if the metadata came from some url, we use that same url to make storage calls against.
|
||||
let node_url = opts.file_or_url.url.map(|url| url.to_string());
|
||||
explore_storage(command, &metadata, pallet_metadata, node_url).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_available_pallets(metadata: &Metadata) -> String {
|
||||
if metadata.pallets().len() == 0 {
|
||||
"There are no <PALLET> values available.".to_string()
|
||||
} else {
|
||||
let mut output = "Available <PALLET> values are:".to_string();
|
||||
let mut strings: Vec<_> = metadata.pallets().map(|p| p.name()).collect();
|
||||
strings.sort();
|
||||
for pallet in strings {
|
||||
write!(output, "\n {}", pallet).unwrap();
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
use clap::Args;
|
||||
use color_eyre::eyre::eyre;
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
|
||||
use subxt::OnlineClient;
|
||||
use subxt::{
|
||||
config::SubstrateConfig,
|
||||
metadata::{
|
||||
types::{PalletMetadata, StorageEntryType, StorageMetadata},
|
||||
Metadata,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::utils::type_description::print_type_description;
|
||||
use crate::utils::type_example::print_type_examples;
|
||||
use crate::utils::{print_docs_with_indent, with_indent};
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct StorageSubcommand {
|
||||
storage_entry: Option<String>,
|
||||
#[clap(required = false)]
|
||||
trailing_args: Vec<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn explore_storage(
|
||||
command: StorageSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: PalletMetadata<'_>,
|
||||
custom_online_client_url: Option<String>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name();
|
||||
let trailing_args = command.trailing_args.join(" ");
|
||||
let trailing_args = trailing_args.trim();
|
||||
|
||||
let Some(storage_metadata) = pallet_metadata.storage() else {
|
||||
println!("The \"{pallet_name}\" pallet has no storage entries.");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// if no storage entry specified, show user the calls to choose from:
|
||||
let Some(entry_name) = command.storage_entry else {
|
||||
let storage_entries = print_available_storage_entries(storage_metadata, pallet_name);
|
||||
println!("Usage:\n subxt explore {pallet_name} storage <STORAGE_ENTRY>\n view details for a specific storage entry\n\n{storage_entries}");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// if specified call storage entry wrong, show user the storage entries to choose from (but this time as an error):
|
||||
let Some(storage) = storage_metadata.entries().find(|entry| entry.name().to_lowercase() == entry_name.to_lowercase()) else {
|
||||
let storage_entries = print_available_storage_entries(storage_metadata, pallet_name);
|
||||
let description = format!("Usage:\n subxt explore {pallet_name} storage <STORAGE_ENTRY>\n view details for a specific storage entry\n\n{storage_entries}");
|
||||
return Err(eyre!("Storage entry \"{entry_name}\" not found in \"{pallet_name}\" pallet!\n\n{description}"));
|
||||
};
|
||||
|
||||
let (return_ty_id, key_ty_id) = match storage.entry_type() {
|
||||
StorageEntryType::Plain(value) => (*value, None),
|
||||
StorageEntryType::Map {
|
||||
value_ty, key_ty, ..
|
||||
} => (*value_ty, Some(*key_ty)),
|
||||
};
|
||||
|
||||
// get the type and type description for the return and key type:
|
||||
let mut output = String::new();
|
||||
|
||||
// only inform user about usage if a key can be provided:
|
||||
if key_ty_id.is_some() && trailing_args.is_empty() {
|
||||
write!(
|
||||
output,
|
||||
"Usage:\n subxt explore {pallet_name} storage {entry_name} <KEY_VALUE>\n\n"
|
||||
)?;
|
||||
}
|
||||
|
||||
let docs_string = print_docs_with_indent(storage.docs(), 4);
|
||||
if !docs_string.is_empty() {
|
||||
write!(output, "Description:\n{docs_string}")?;
|
||||
}
|
||||
|
||||
// inform user about shape of key if it can be provided:
|
||||
if let Some(key_ty_id) = key_ty_id {
|
||||
let mut key_ty_description = print_type_description(&key_ty_id, metadata.types())?;
|
||||
key_ty_description = with_indent(key_ty_description, 4);
|
||||
let mut key_ty_examples = print_type_examples(&key_ty_id, metadata.types(), "<KEY_VALUE>")?;
|
||||
key_ty_examples = with_indent(key_ty_examples, 4);
|
||||
write!(
|
||||
output,
|
||||
"\n\nThe <KEY_VALUE> has the following shape:\n {key_ty_description}\n\n{}",
|
||||
&key_ty_examples[4..]
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
output,
|
||||
"\n\nThe constant can be accessed without providing a key."
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut return_ty_description = print_type_description(&return_ty_id, metadata.types())?;
|
||||
return_ty_description = if return_ty_description.contains('\n') {
|
||||
format!("\n{}", with_indent(return_ty_description, 4))
|
||||
} else {
|
||||
return_ty_description
|
||||
};
|
||||
write!(
|
||||
output,
|
||||
"\n\nThe storage entry has the following shape: {}",
|
||||
return_ty_description
|
||||
)?;
|
||||
|
||||
// construct the vector of scale_values that should be used as a key to the storage (often empty)
|
||||
|
||||
let key_scale_values = if let Some(key_ty_id) = key_ty_id.filter(|_| !trailing_args.is_empty())
|
||||
{
|
||||
let key_scale_value = scale_value::stringify::from_str(trailing_args).0.map_err(|err| eyre!("scale_value::stringify::from_str led to a ParseError.\n\ntried parsing: \"{}\"\n\n{}", trailing_args, err))?;
|
||||
write!(
|
||||
output,
|
||||
"\n\nYou submitted the following value as a key:\n{}",
|
||||
with_indent(scale_value::stringify::to_string(&key_scale_value), 4)
|
||||
)?;
|
||||
let mut key_bytes: Vec<u8> = Vec::new();
|
||||
scale_value::scale::encode_as_type(
|
||||
&key_scale_value,
|
||||
key_ty_id,
|
||||
metadata.types(),
|
||||
&mut key_bytes,
|
||||
)?;
|
||||
let bytes_composite = scale_value::Value::from_bytes(&key_bytes);
|
||||
vec![bytes_composite]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
if key_ty_id.is_none() && !trailing_args.is_empty() {
|
||||
write!(output, "\n\nWarning: You submitted the following value as a key, but it will be ignored, because the storage entry does not require a key: \"{}\"", trailing_args)?;
|
||||
}
|
||||
println!("{output}");
|
||||
|
||||
// construct and submit the storage entry request if either no key is needed or som key was provided as a scale value
|
||||
if key_ty_id.is_none() || !key_scale_values.is_empty() {
|
||||
let online_client = match custom_online_client_url {
|
||||
None => OnlineClient::<SubstrateConfig>::new().await?,
|
||||
Some(url) => OnlineClient::<SubstrateConfig>::from_url(url).await?,
|
||||
};
|
||||
let storage_query = subxt::dynamic::storage(pallet_name, entry_name, key_scale_values);
|
||||
let decoded_value_thunk_or_none = online_client
|
||||
.storage()
|
||||
.at_latest()
|
||||
.await?
|
||||
.fetch(&storage_query)
|
||||
.await?;
|
||||
|
||||
let decoded_value_thunk =
|
||||
decoded_value_thunk_or_none.ok_or(eyre!("Value not found in storage."))?;
|
||||
|
||||
let value = decoded_value_thunk.to_value()?;
|
||||
let mut value_string = scale_value::stringify::to_string(&value);
|
||||
value_string = with_indent(value_string, 4);
|
||||
println!("\nThe value of the storage entry is:\n{value_string}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_available_storage_entries(
|
||||
storage_metadata: &StorageMetadata,
|
||||
pallet_name: &str,
|
||||
) -> String {
|
||||
if storage_metadata.entries().len() == 0 {
|
||||
format!("No <STORAGE_ENTRY>'s available in the \"{pallet_name}\" pallet.")
|
||||
} else {
|
||||
let mut output = format!(
|
||||
"Available <STORAGE_ENTRY>'s in the \"{}\" pallet:",
|
||||
pallet_name
|
||||
);
|
||||
let mut strings: Vec<_> = storage_metadata.entries().map(|s| s.name()).collect();
|
||||
strings.sort();
|
||||
for entry in strings {
|
||||
write!(output, "\n {}", entry).unwrap();
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@
|
||||
use crate::utils::FileOrUrl;
|
||||
use clap::Parser as ClapParser;
|
||||
use codec::{Decode, Encode};
|
||||
use color_eyre::eyre;
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use color_eyre::eyre::{self, bail};
|
||||
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use std::io::{self, Write};
|
||||
use subxt_metadata::{metadata_v14_to_latest, retain_metadata_pallets};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
/// Download metadata from a substrate node, for use with `subxt` codegen.
|
||||
#[derive(Debug, ClapParser)]
|
||||
@@ -25,28 +25,48 @@ pub struct Opts {
|
||||
/// when using the option.
|
||||
#[clap(long, use_value_delimiter = true, value_parser)]
|
||||
pallets: Option<Vec<String>>,
|
||||
/// Generate a subset of the metadata that contains only the
|
||||
/// runtime APIs needed.
|
||||
///
|
||||
/// The returned metadata is updated to the latest available version
|
||||
/// when using the option.
|
||||
#[clap(long, use_value_delimiter = true, value_parser)]
|
||||
runtime_apis: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
let bytes = opts.file_or_url.fetch().await?;
|
||||
let mut metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
let mut metadata = RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;
|
||||
|
||||
if let Some(pallets) = opts.pallets {
|
||||
let mut metadata_v15 = match metadata.1 {
|
||||
RuntimeMetadata::V14(metadata_v14) => metadata_v14_to_latest(metadata_v14),
|
||||
RuntimeMetadata::V15(metadata_v15) => metadata_v15,
|
||||
_ => {
|
||||
return Err(eyre::eyre!(
|
||||
"Unsupported metadata version {:?}, expected V14.",
|
||||
metadata.1
|
||||
))
|
||||
}
|
||||
let version = match &metadata.1 {
|
||||
RuntimeMetadata::V14(_) => Version::V14,
|
||||
RuntimeMetadata::V15(_) => Version::V15,
|
||||
_ => Version::Unknown,
|
||||
};
|
||||
|
||||
if opts.pallets.is_some() || opts.runtime_apis.is_some() {
|
||||
// convert to internal type:
|
||||
let mut md = Metadata::try_from(metadata)?;
|
||||
|
||||
// retain pallets and/or runtime APIs given:
|
||||
let retain_pallets_fn: Box<dyn Fn(&str) -> bool> = match opts.pallets.as_ref() {
|
||||
Some(pallets) => Box::new(|name| pallets.iter().any(|p| &**p == name)),
|
||||
None => Box::new(|_| true),
|
||||
};
|
||||
let retain_runtime_apis_fn: Box<dyn Fn(&str) -> bool> = match opts.runtime_apis.as_ref() {
|
||||
Some(apis) => Box::new(|name| apis.iter().any(|p| &**p == name)),
|
||||
None => Box::new(|_| true),
|
||||
};
|
||||
md.retain(retain_pallets_fn, retain_runtime_apis_fn);
|
||||
|
||||
retain_metadata_pallets(&mut metadata_v15, |pallet_name| {
|
||||
pallets.iter().any(|p| &**p == pallet_name)
|
||||
});
|
||||
metadata = metadata_v15.into();
|
||||
// Convert back to wire format, preserving version:
|
||||
metadata = match version {
|
||||
Version::V14 => RuntimeMetadataV15::from(md).into(),
|
||||
Version::V15 => RuntimeMetadataV15::from(md).into(),
|
||||
Version::Unknown => {
|
||||
bail!("Unsupported metadata version; V14 or V15 metadata is expected.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match opts.format.as_str() {
|
||||
@@ -70,3 +90,9 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
enum Version {
|
||||
V14,
|
||||
V15,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
|
||||
pub mod codegen;
|
||||
pub mod compatibility;
|
||||
pub mod explore;
|
||||
pub mod metadata;
|
||||
pub mod version;
|
||||
|
||||
@@ -15,6 +15,7 @@ enum Command {
|
||||
Codegen(commands::codegen::Opts),
|
||||
Compatibility(commands::compatibility::Opts),
|
||||
Version(commands::version::Opts),
|
||||
Explore(commands::explore::Opts),
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -27,5 +28,6 @@ async fn main() -> color_eyre::Result<()> {
|
||||
Command::Codegen(opts) => commands::codegen::run(opts).await,
|
||||
Command::Compatibility(opts) => commands::compatibility::run(opts).await,
|
||||
Command::Version(opts) => commands::version::run(opts),
|
||||
Command::Explore(opts) => commands::explore::run(opts).await,
|
||||
}
|
||||
}
|
||||
|
||||
+25
-2
@@ -4,18 +4,22 @@
|
||||
|
||||
use clap::Args;
|
||||
use color_eyre::eyre;
|
||||
|
||||
use std::{fs, io::Read, path::PathBuf};
|
||||
use subxt_codegen::utils::{MetadataVersion, Uri};
|
||||
|
||||
pub mod type_description;
|
||||
pub mod type_example;
|
||||
|
||||
/// The source of the metadata.
|
||||
#[derive(Debug, Args)]
|
||||
pub struct FileOrUrl {
|
||||
/// The url of the substrate node to query for metadata for codegen.
|
||||
#[clap(long, value_parser)]
|
||||
url: Option<Uri>,
|
||||
pub(crate) url: Option<Uri>,
|
||||
/// The path to the encoded metadata file.
|
||||
#[clap(long, value_parser)]
|
||||
file: Option<PathBuf>,
|
||||
pub(crate) file: Option<PathBuf>,
|
||||
/// Specify the metadata version.
|
||||
///
|
||||
/// - unstable:
|
||||
@@ -71,3 +75,22 @@ impl FileOrUrl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_docs_with_indent(docs: &[String], indent: usize) -> String {
|
||||
// take at most the first paragraph of documentation, such that it does not get too long.
|
||||
let docs_str = docs
|
||||
.iter()
|
||||
.map(|e| e.trim())
|
||||
.take_while(|e| !e.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
with_indent(docs_str, indent)
|
||||
}
|
||||
|
||||
pub(crate) fn with_indent(s: String, indent: usize) -> String {
|
||||
let indent_str = " ".repeat(indent);
|
||||
s.lines()
|
||||
.map(|line| format!("{indent_str}{line}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use scale_info::{
|
||||
form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefArray, TypeDefBitSequence,
|
||||
TypeDefCompact, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, TypeDefVariant, Variant,
|
||||
};
|
||||
|
||||
/// pretty formatted type description
|
||||
pub fn print_type_description<T>(ty: &T, registry: &PortableRegistry) -> color_eyre::Result<String>
|
||||
where
|
||||
T: TypeDescription,
|
||||
{
|
||||
let type_description = ty.type_description(registry)?;
|
||||
let type_description = format_type_description(&type_description);
|
||||
Ok(type_description)
|
||||
}
|
||||
|
||||
/// a trait for producing human readable type descriptions with a rust-like syntax.
|
||||
pub trait TypeDescription {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String>;
|
||||
}
|
||||
|
||||
impl TypeDescription for u32 {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let ty = registry
|
||||
.resolve(*self)
|
||||
.ok_or(eyre!("Type with id {} not found in registry", *self))?;
|
||||
let ident = ty.path.ident();
|
||||
let prefix = type_def_prefix(&ty.type_def);
|
||||
let mut type_def_description = ty.type_def.type_description(registry)?;
|
||||
if let Some(ident) = ident {
|
||||
type_def_description = format!("{} {}", ident, type_def_description)
|
||||
}
|
||||
if let Some(prefix) = prefix {
|
||||
type_def_description = format!("{} {}", prefix, type_def_description)
|
||||
}
|
||||
Ok(type_def_description)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_def_prefix(type_def: &TypeDef<PortableForm>) -> Option<&str> {
|
||||
match type_def {
|
||||
TypeDef::Composite(_) => Some("struct"),
|
||||
TypeDef::Variant(_) => Some("enum"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDef<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
match self {
|
||||
TypeDef::Composite(composite) => composite.fields.type_description(registry),
|
||||
TypeDef::Variant(variant) => variant.type_description(registry),
|
||||
TypeDef::Sequence(sequence) => sequence.type_description(registry),
|
||||
TypeDef::Array(array) => array.type_description(registry),
|
||||
TypeDef::Tuple(tuple) => tuple.type_description(registry),
|
||||
TypeDef::Primitive(primitive) => primitive.type_description(registry),
|
||||
TypeDef::Compact(compact) => compact.type_description(registry),
|
||||
TypeDef::BitSequence(bit_sequence) => bit_sequence.type_description(registry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefTuple<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let mut output = "(".to_string();
|
||||
let mut iter = self.fields.iter().peekable();
|
||||
while let Some(ty) = iter.next() {
|
||||
let type_description = ty.id.type_description(registry)?;
|
||||
output.push_str(&type_description);
|
||||
if iter.peek().is_some() {
|
||||
output.push(',')
|
||||
}
|
||||
}
|
||||
output.push(')');
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefBitSequence<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let bit_order_type = self.bit_order_type.id.type_description(registry)?;
|
||||
let bit_store_type = self.bit_store_type.id.type_description(registry)?;
|
||||
Ok(format!("BitSequence({bit_order_type}, {bit_store_type})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefSequence<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("Sequence({type_description})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefCompact<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("Compact({type_description})"))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefArray<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.type_param.id.type_description(registry)?;
|
||||
Ok(format!("[{type_description}; {}]", self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefPrimitive {
|
||||
fn type_description(&self, _registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
Ok(match &self {
|
||||
TypeDefPrimitive::Bool => "bool",
|
||||
TypeDefPrimitive::Char => "char",
|
||||
TypeDefPrimitive::Str => "String",
|
||||
TypeDefPrimitive::U8 => "u8",
|
||||
TypeDefPrimitive::U16 => "u16",
|
||||
TypeDefPrimitive::U32 => "u32",
|
||||
TypeDefPrimitive::U64 => "u64",
|
||||
TypeDefPrimitive::U128 => "u128",
|
||||
TypeDefPrimitive::U256 => "u256",
|
||||
TypeDefPrimitive::I8 => "i8",
|
||||
TypeDefPrimitive::I16 => "i16",
|
||||
TypeDefPrimitive::I32 => "i32",
|
||||
TypeDefPrimitive::I64 => "i64",
|
||||
TypeDefPrimitive::I128 => "i128",
|
||||
TypeDefPrimitive::I256 => "i256",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for TypeDefVariant<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
const MIN_VARIANT_COUNT_FOR_TRAILING_COMMA: usize = 100;
|
||||
let add_trailing_comma = self.variants.len() >= MIN_VARIANT_COUNT_FOR_TRAILING_COMMA;
|
||||
|
||||
let mut variants_string = String::new();
|
||||
variants_string.push('{');
|
||||
let mut iter = self.variants.iter().peekable();
|
||||
while let Some(variant) = iter.next() {
|
||||
let variant_string = variant.type_description(registry)?;
|
||||
variants_string.push_str(&variant_string);
|
||||
|
||||
if iter.peek().is_some() || add_trailing_comma {
|
||||
variants_string.push(',');
|
||||
}
|
||||
}
|
||||
variants_string.push('}');
|
||||
Ok(variants_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Variant<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let fields_string = self.fields.type_description(registry)?;
|
||||
let output = if fields_string.is_empty() {
|
||||
self.name.to_string()
|
||||
} else if fields_string.starts_with('(') {
|
||||
format!("{}{}", &self.name, fields_string)
|
||||
} else {
|
||||
format!("{} {}", &self.name, fields_string)
|
||||
};
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Vec<Field<PortableForm>> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
if self.is_empty() {
|
||||
return Ok("()".to_string());
|
||||
}
|
||||
|
||||
const MIN_FIELD_COUNT_FOR_TRAILING_COMMA: usize = 100;
|
||||
let add_trailing_comma = self.len() >= MIN_FIELD_COUNT_FOR_TRAILING_COMMA;
|
||||
|
||||
let all_fields_named = self.iter().all(|f| f.name.is_some());
|
||||
let all_fields_unnamed = self.iter().all(|f| f.name.is_none());
|
||||
let brackets = match (all_fields_named, all_fields_unnamed) {
|
||||
(true, false) => ('{', '}'),
|
||||
(false, true) => ('(', ')'),
|
||||
_ => {
|
||||
return Err(eyre!(
|
||||
"combination of named and unnamed fields in compound type"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut fields_string = String::new();
|
||||
fields_string.push(brackets.0);
|
||||
let mut iter = self.iter().peekable();
|
||||
while let Some(field) = iter.next() {
|
||||
let field_description = field.type_description(registry)?;
|
||||
fields_string.push_str(&field_description);
|
||||
|
||||
if iter.peek().is_some() || add_trailing_comma {
|
||||
fields_string.push(',')
|
||||
}
|
||||
}
|
||||
fields_string.push(brackets.1);
|
||||
Ok(fields_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeDescription for Field<PortableForm> {
|
||||
fn type_description(&self, registry: &PortableRegistry) -> color_eyre::Result<String> {
|
||||
let type_description = self.ty.id.type_description(registry)?;
|
||||
let type_description_maybe_named = if let Some(name) = &self.name {
|
||||
format!("{}: {}", name, type_description)
|
||||
} else {
|
||||
type_description
|
||||
};
|
||||
Ok(type_description_maybe_named)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_type_description(input: &str) -> String {
|
||||
fn add_indentation(output: &mut String, indent_level: i32) {
|
||||
for _ in 0..indent_level {
|
||||
output.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = String::new();
|
||||
let mut indent_level = 0;
|
||||
// in a tuple we will not set line breaks on comma, so we keep track of it here:
|
||||
let mut in_tuple = 0;
|
||||
let mut tokens_since_last_bracket_or_comma: usize = 0;
|
||||
for ch in input.chars() {
|
||||
let mut token_is_bracket_or_comma = true;
|
||||
match ch {
|
||||
'{' => {
|
||||
indent_level += 1;
|
||||
output.push(ch);
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
}
|
||||
'}' => {
|
||||
indent_level -= 1;
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
output.push(ch);
|
||||
}
|
||||
',' => {
|
||||
output.push(ch);
|
||||
// makes small tuples e.g. (u8, u16, u8, u8) not cause line breaks.
|
||||
if in_tuple > 0 && tokens_since_last_bracket_or_comma < 5 {
|
||||
output.push(' ');
|
||||
} else {
|
||||
output.push('\n');
|
||||
add_indentation(&mut output, indent_level);
|
||||
}
|
||||
}
|
||||
'(' => {
|
||||
output.push(ch);
|
||||
in_tuple += 1;
|
||||
}
|
||||
')' => {
|
||||
output.push(ch);
|
||||
in_tuple -= 1;
|
||||
}
|
||||
_ => {
|
||||
token_is_bracket_or_comma = false;
|
||||
output.push(ch);
|
||||
}
|
||||
}
|
||||
if token_is_bracket_or_comma {
|
||||
tokens_since_last_bracket_or_comma = 0;
|
||||
} else {
|
||||
tokens_since_last_bracket_or_comma += 1;
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use scale_info::{
|
||||
form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefArray, TypeDefPrimitive,
|
||||
TypeDefTuple, TypeDefVariant,
|
||||
};
|
||||
use scale_value::{Value, ValueDef};
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
|
||||
pub fn print_type_examples<T>(
|
||||
ty: &T,
|
||||
registry: &PortableRegistry,
|
||||
type_placeholder: &str,
|
||||
) -> color_eyre::Result<String>
|
||||
where
|
||||
T: TypeExample,
|
||||
{
|
||||
let type_examples = ty.type_example(registry)?;
|
||||
let mut output = String::new();
|
||||
match type_examples.len() {
|
||||
0 => {
|
||||
write!(
|
||||
output,
|
||||
"There are no examples available for a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
1 => {
|
||||
write!(
|
||||
output,
|
||||
"Here is an example of a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
i => {
|
||||
write!(
|
||||
output,
|
||||
"Here are {i} examples of a {type_placeholder} matching this shape:"
|
||||
)?;
|
||||
}
|
||||
};
|
||||
for self_value in type_examples {
|
||||
let value = <T as TypeExample>::upcast(self_value);
|
||||
let example_str = scale_value::stringify::to_string(&value);
|
||||
write!(output, "\n{}", example_str)?;
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// a trait for producing scale value examples for a type.
|
||||
pub trait TypeExample {
|
||||
type Value;
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>>;
|
||||
fn upcast(self_value: Self::Value) -> scale_value::Value;
|
||||
}
|
||||
|
||||
impl TypeExample for u32 {
|
||||
type Value = scale_value::Value;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let ty = registry
|
||||
.resolve(*self)
|
||||
.ok_or(eyre!("Type with id {} not found in registry", *self))?;
|
||||
|
||||
let examples = match &ty.type_def {
|
||||
TypeDef::Composite(composite) => composite
|
||||
.fields
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Variant(variant) => variant
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Variant(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Array(array) => array
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Tuple(tuple) => tuple
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect(),
|
||||
TypeDef::Primitive(primitive) => primitive
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(scale_value::Value::primitive)
|
||||
.collect(),
|
||||
TypeDef::Compact(compact) => compact.type_param.id.type_example(registry)?,
|
||||
TypeDef::BitSequence(_) => {
|
||||
return Err(eyre!("no examples for BitSequence available"));
|
||||
}
|
||||
TypeDef::Sequence(sequence) => {
|
||||
// for sequences we just give an example of an array with 3 elements:
|
||||
TypeDefArray {
|
||||
len: 3,
|
||||
type_param: sequence.type_param,
|
||||
}
|
||||
.type_example(registry)?
|
||||
.into_iter()
|
||||
.map(|e| scale_value::Value {
|
||||
value: scale_value::ValueDef::Composite(e),
|
||||
context: (),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
Ok(examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
self_value
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefVariant<PortableForm> {
|
||||
type Value = scale_value::Variant<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let mut examples: Vec<scale_value::Variant<()>> = Vec::new();
|
||||
|
||||
// returns one example for each variant
|
||||
for variant in &self.variants {
|
||||
// get the first example for the variant's data and use it
|
||||
let mut variant_value_examples = variant.fields.type_example(registry)?;
|
||||
let Some(values) = variant_value_examples.pop() else {
|
||||
return Err(eyre!("no example element for variant {}", variant.name));
|
||||
};
|
||||
|
||||
examples.push(scale_value::Variant {
|
||||
name: variant.name.clone(),
|
||||
values,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Variant(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefArray<PortableForm> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
// take the first example value and set it to each element of the array
|
||||
let mut value_examples = self.type_param.id.type_example(registry)?;
|
||||
let Some(first_value_example) = value_examples.pop() else {
|
||||
return Err(eyre!("no example element for array"));
|
||||
};
|
||||
|
||||
let one_example = {
|
||||
let mut values = Vec::with_capacity(self.len as usize);
|
||||
for _ in 0..self.len as usize {
|
||||
values.push(first_value_example.clone());
|
||||
}
|
||||
scale_value::Composite::<()>::Unnamed(values)
|
||||
};
|
||||
Ok(vec![one_example])
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for TypeDefTuple<PortableForm> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
// create unnamed fields to use the same logic already used for struct example generation
|
||||
let fields_vector: Vec<Field<PortableForm>> = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|ty| Field {
|
||||
name: None,
|
||||
ty: *ty,
|
||||
type_name: None,
|
||||
docs: Vec::new(),
|
||||
})
|
||||
.collect();
|
||||
fields_vector.type_example(registry)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeExample for Vec<Field<PortableForm>> {
|
||||
type Value = scale_value::Composite<()>;
|
||||
|
||||
fn type_example(&self, registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let all_fields_named = self.iter().all(|f| f.name.is_some());
|
||||
let all_fields_unnamed = self.iter().all(|f| f.name.is_none());
|
||||
// composite apparently has no fields:
|
||||
if all_fields_named && all_fields_unnamed {
|
||||
let one_empty_example = scale_value::Composite::Unnamed(Vec::new());
|
||||
return Ok(vec![one_empty_example]);
|
||||
}
|
||||
|
||||
// composite apparently has mix of named and unnamed fields:
|
||||
if !all_fields_named && !all_fields_unnamed {
|
||||
return Err(eyre!(
|
||||
"combination of named and unnamed fields in compound type"
|
||||
));
|
||||
}
|
||||
|
||||
// for each field get all the examples the type of that field can offer:
|
||||
let mut field_examples: Vec<(&Field<PortableForm>, Vec<scale_value::Value>)> = Vec::new();
|
||||
for field in self.iter() {
|
||||
let examples = field.ty.id.type_example(registry)?;
|
||||
field_examples.push((field, examples));
|
||||
}
|
||||
|
||||
// Let N be the mininum number of examples any field has.
|
||||
// Return N examples for the Compound type, by choosing the ith example for each of the 0..N examples for that field.
|
||||
let n = field_examples
|
||||
.iter()
|
||||
.map(|(_, examples)| examples.len())
|
||||
.min()
|
||||
.expect("Iterator is not non-empty checked above; qed");
|
||||
let mut composite_examples: Vec<Vec<(&Field<PortableForm>, scale_value::Value)>> =
|
||||
Vec::new();
|
||||
for _ in 0..n {
|
||||
let composite_example: Vec<(&Field<PortableForm>, scale_value::Value)> = field_examples
|
||||
.iter_mut()
|
||||
.map(|(field, examples)| (*field, examples.pop().unwrap()))
|
||||
.collect(); // the pop() is safe to unwrap because of the minimum we checked before
|
||||
composite_examples.push(composite_example);
|
||||
}
|
||||
|
||||
// create the vector of composite scale values. Distingiush between named and unnamed here.
|
||||
let composite_examples = composite_examples
|
||||
.into_iter()
|
||||
.map(|composite_example| {
|
||||
if all_fields_named {
|
||||
let composite_example = composite_example
|
||||
.into_iter()
|
||||
.map(|(field, value)| (field.name.as_ref().unwrap().clone(), value))
|
||||
.collect();
|
||||
scale_value::Composite::Named(composite_example)
|
||||
} else {
|
||||
let composite_example = composite_example
|
||||
.into_iter()
|
||||
.map(|(_, value)| (value))
|
||||
.collect();
|
||||
scale_value::Composite::Unnamed(composite_example)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(composite_examples)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Composite(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 3-4 example values for each primitive
|
||||
impl TypeExample for TypeDefPrimitive {
|
||||
type Value = scale_value::Primitive;
|
||||
|
||||
fn type_example(&self, _registry: &PortableRegistry) -> color_eyre::Result<Vec<Self::Value>> {
|
||||
let value = match &self {
|
||||
TypeDefPrimitive::Bool => vec![
|
||||
scale_value::Primitive::Bool(true),
|
||||
scale_value::Primitive::Bool(false),
|
||||
],
|
||||
TypeDefPrimitive::Char => vec![
|
||||
scale_value::Primitive::Char('r'),
|
||||
scale_value::Primitive::Char('u'),
|
||||
scale_value::Primitive::Char('s'),
|
||||
scale_value::Primitive::Char('t'),
|
||||
],
|
||||
TypeDefPrimitive::Str => vec![
|
||||
scale_value::Primitive::String("Alice".into()),
|
||||
scale_value::Primitive::String("Bob".into()),
|
||||
scale_value::Primitive::String("Foo".into()),
|
||||
scale_value::Primitive::String("Bar".into()),
|
||||
],
|
||||
TypeDefPrimitive::U8 => vec![
|
||||
scale_value::Primitive::U128(u8::MIN as u128),
|
||||
scale_value::Primitive::U128(69),
|
||||
scale_value::Primitive::U128(u8::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U16 => vec![
|
||||
scale_value::Primitive::U128(u16::MIN as u128),
|
||||
scale_value::Primitive::U128(420),
|
||||
scale_value::Primitive::U128(u16::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U32 => vec![
|
||||
scale_value::Primitive::U128(u32::MIN as u128),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u32::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U64 => vec![
|
||||
scale_value::Primitive::U128(u64::MIN as u128),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u64::MAX as u128),
|
||||
],
|
||||
TypeDefPrimitive::U128 => vec![
|
||||
scale_value::Primitive::U128(u128::MIN),
|
||||
scale_value::Primitive::U128(99000),
|
||||
scale_value::Primitive::U128(u128::MAX),
|
||||
],
|
||||
TypeDefPrimitive::U256 => vec![
|
||||
scale_value::Primitive::U256([u8::MIN; 32]),
|
||||
scale_value::Primitive::U256([3; 32]),
|
||||
scale_value::Primitive::U256([u8::MAX; 32]),
|
||||
],
|
||||
TypeDefPrimitive::I8 => vec![
|
||||
scale_value::Primitive::I128(i8::MIN as i128),
|
||||
scale_value::Primitive::I128(69),
|
||||
scale_value::Primitive::I128(i8::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I16 => vec![
|
||||
scale_value::Primitive::I128(i16::MIN as i128),
|
||||
scale_value::Primitive::I128(420),
|
||||
scale_value::Primitive::I128(i16::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I32 => vec![
|
||||
scale_value::Primitive::I128(i32::MIN as i128),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i32::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I64 => vec![
|
||||
scale_value::Primitive::I128(i64::MIN as i128),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i64::MAX as i128),
|
||||
],
|
||||
TypeDefPrimitive::I128 => vec![
|
||||
scale_value::Primitive::I128(i128::MIN),
|
||||
scale_value::Primitive::I128(99000),
|
||||
scale_value::Primitive::I128(i128::MAX),
|
||||
],
|
||||
TypeDefPrimitive::I256 => vec![
|
||||
scale_value::Primitive::I256([u8::MIN; 32]),
|
||||
scale_value::Primitive::I256([3; 32]),
|
||||
scale_value::Primitive::I256([u8::MAX; 32]),
|
||||
],
|
||||
};
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn upcast(self_value: Self::Value) -> Value {
|
||||
Value {
|
||||
value: ValueDef::Primitive(self_value),
|
||||
context: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-17
@@ -7,11 +7,10 @@ use crate::{
|
||||
types::{CompositeDefFields, TypeGenerator},
|
||||
CratePath,
|
||||
};
|
||||
use frame_metadata::v15::{PalletMetadata, RuntimeMetadataV15};
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
/// Generate calls from the provided pallet's metadata. Each call returns a `StaticTxPayload`
|
||||
/// that can be passed to the subxt client to submit/sign/encode.
|
||||
@@ -23,21 +22,20 @@ use scale_info::form::PortableForm;
|
||||
/// - `pallet` - Pallet metadata from which the calls are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
pub fn generate_calls(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no calls.
|
||||
let Some(call) = &pallet.calls else {
|
||||
let Some(call_ty) = pallet.call_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let mut struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
call.ty.id,
|
||||
call_ty,
|
||||
|name| name.to_upper_camel_case().into(),
|
||||
"Call",
|
||||
crate_path,
|
||||
@@ -61,20 +59,19 @@ pub fn generate_calls(
|
||||
.unzip(),
|
||||
CompositeDefFields::NoFields => Default::default(),
|
||||
CompositeDefFields::Unnamed(_) => {
|
||||
return Err(CodegenError::InvalidCallVariant(call.ty.id))
|
||||
return Err(CodegenError::InvalidCallVariant(call_ty))
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = &pallet.name;
|
||||
let pallet_name = pallet.name();
|
||||
let call_name = &variant_name;
|
||||
let struct_name = &struct_def.name;
|
||||
let Ok(call_hash) =
|
||||
subxt_metadata::get_call_hash(metadata, pallet_name, call_name) else {
|
||||
return Err(CodegenError::MissingCallMetadata(
|
||||
pallet_name.into(),
|
||||
call_name.to_string(),
|
||||
))
|
||||
};
|
||||
let Some(call_hash) = pallet.call_hash(call_name) else {
|
||||
return Err(CodegenError::MissingCallMetadata(
|
||||
pallet_name.into(),
|
||||
call_name.to_string(),
|
||||
))
|
||||
};
|
||||
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.
|
||||
@@ -111,8 +108,8 @@ pub fn generate_calls(
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
let call_type = type_gen.resolve_type_path(call.ty.id);
|
||||
let call_ty = type_gen.resolve_type(call.ty.id);
|
||||
let call_type = type_gen.resolve_type_path(call_ty);
|
||||
let call_ty = type_gen.resolve_type(call_ty);
|
||||
let docs = &call_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::{PalletMetadata, RuntimeMetadataV15};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -35,29 +34,27 @@ use super::CodegenError;
|
||||
/// - `pallet` - Pallet metadata from which the calls are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
pub fn generate_constants(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no constants.
|
||||
if pallet.constants.is_empty() {
|
||||
if pallet.constants().len() == 0 {
|
||||
return Ok(quote!());
|
||||
}
|
||||
let constants = &pallet.constants;
|
||||
|
||||
let constant_fns = constants.iter().map(|constant| {
|
||||
let fn_name = format_ident!("{}", constant.name.to_snake_case());
|
||||
let pallet_name = &pallet.name;
|
||||
let constant_name = &constant.name;
|
||||
let Ok(constant_hash) = subxt_metadata::get_constant_hash(metadata, pallet_name, constant_name) else {
|
||||
let constant_fns = pallet.constants().map(|constant| {
|
||||
let fn_name = format_ident!("{}", constant.name().to_snake_case());
|
||||
let pallet_name = pallet.name();
|
||||
let constant_name = constant.name();
|
||||
let Some(constant_hash) = pallet.constant_hash(constant_name) else {
|
||||
return Err(CodegenError::MissingConstantMetadata(constant_name.into(), pallet_name.into()));
|
||||
};
|
||||
|
||||
let return_ty = type_gen.resolve_type_path(constant.ty.id);
|
||||
let docs = &constant.docs;
|
||||
let return_ty = type_gen.resolve_type_path(constant.ty());
|
||||
let docs = constant.docs();
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
|
||||
@@ -14,15 +13,15 @@ use super::CodegenError;
|
||||
/// Generate error type alias from the provided pallet metadata.
|
||||
pub fn generate_error_type_alias(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(error) = &pallet.error else {
|
||||
let Some(error_ty) = pallet.error_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let error_type = type_gen.resolve_type_path(error.ty.id);
|
||||
let error_ty = type_gen.resolve_type(error.ty.id);
|
||||
let error_type = type_gen.resolve_type_path(error_ty);
|
||||
let error_ty = type_gen.resolve_type(error_ty);
|
||||
let docs = &error_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -41,19 +40,19 @@ use super::CodegenError;
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
pub fn generate_events(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no events.
|
||||
let Some(event) = &pallet.event else {
|
||||
let Some(event_ty) = pallet.event_ty_id() else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
event.ty.id,
|
||||
event_ty,
|
||||
|name| name.into(),
|
||||
"Event",
|
||||
crate_path,
|
||||
@@ -61,7 +60,7 @@ pub fn generate_events(
|
||||
)?;
|
||||
|
||||
let event_structs = struct_defs.iter().map(|(variant_name, struct_def)| {
|
||||
let pallet_name = &pallet.name;
|
||||
let pallet_name = pallet.name();
|
||||
let event_struct = &struct_def.name;
|
||||
let event_name = variant_name;
|
||||
|
||||
@@ -74,8 +73,8 @@ pub fn generate_events(
|
||||
}
|
||||
}
|
||||
});
|
||||
let event_type = type_gen.resolve_type_path(event.ty.id);
|
||||
let event_ty = type_gen.resolve_type(event.ty.id);
|
||||
let event_type = type_gen.resolve_type_path(event_ty);
|
||||
let event_ty = type_gen.resolve_type(event_ty);
|
||||
let docs = &event_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
|
||||
+92
-62
@@ -11,8 +11,7 @@ mod events;
|
||||
mod runtime_apis;
|
||||
mod storage;
|
||||
|
||||
use frame_metadata::v15::RuntimeMetadataV15;
|
||||
use subxt_metadata::{metadata_v14_to_latest, MetadataHasher};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
use super::DerivesRegistry;
|
||||
use crate::error::CodegenError;
|
||||
@@ -23,11 +22,10 @@ use crate::{
|
||||
CratePath,
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use std::{fs, io::Read, path, string::ToString};
|
||||
use std::{collections::HashMap, fs, io::Read, path, string::ToString};
|
||||
use syn::parse_quote;
|
||||
|
||||
/// Generates the API for interacting with a Substrate runtime.
|
||||
@@ -135,7 +133,7 @@ pub fn generate_runtime_api_from_bytes(
|
||||
should_gen_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
if runtime_types_only {
|
||||
@@ -159,7 +157,7 @@ pub fn generate_runtime_api_from_bytes(
|
||||
|
||||
/// Create the API for interacting with a Substrate runtime.
|
||||
pub struct RuntimeGenerator {
|
||||
metadata: RuntimeMetadataV15,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
impl RuntimeGenerator {
|
||||
@@ -174,16 +172,50 @@ impl RuntimeGenerator {
|
||||
/// Panics if the runtime metadata version is not supported.
|
||||
///
|
||||
/// Supported versions: v14 and v15.
|
||||
pub fn new(metadata: RuntimeMetadataPrefixed) -> Self {
|
||||
let metadata = match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => metadata_v14_to_latest(v14),
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
_ => panic!("Unsupported metadata version {:?}", metadata.1),
|
||||
};
|
||||
|
||||
pub fn new(mut metadata: Metadata) -> Self {
|
||||
Self::ensure_unique_type_paths(&mut metadata);
|
||||
RuntimeGenerator { metadata }
|
||||
}
|
||||
|
||||
/// Ensure that every unique type we'll be generating or referring to also has a
|
||||
/// unique path, so that types with matching paths don't end up overwriting each other
|
||||
/// in the codegen. We ignore any types with generics; Subxt actually endeavours to
|
||||
/// de-duplicate those into single types with a generic.
|
||||
fn ensure_unique_type_paths(metadata: &mut Metadata) {
|
||||
let mut visited_path_counts = HashMap::<Vec<String>, usize>::new();
|
||||
for ty in metadata.types_mut().types.iter_mut() {
|
||||
// Ignore types without a path (ie prelude types).
|
||||
if ty.ty.path.namespace().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let has_valid_type_params = ty.ty.type_params.iter().any(|tp| tp.ty.is_some());
|
||||
|
||||
// Ignore types which have generic params that the type generation will use.
|
||||
// Ordinarily we'd expect that any two types with identical paths must be parameterized
|
||||
// in order to share the path. However scale-info doesn't understand all forms of generics
|
||||
// properly I think (eg generics that have associated types that can differ), and so in
|
||||
// those cases we need to fix the paths for Subxt to generate correct code.
|
||||
if has_valid_type_params {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count how many times we've seen the same path already.
|
||||
let visited_count = visited_path_counts
|
||||
.entry(ty.ty.path.segments.clone())
|
||||
.or_default();
|
||||
*visited_count += 1;
|
||||
|
||||
// alter the type so that if it's been seen more than once, we append a number to
|
||||
// its name to ensure that every unique type has a unique path, too.
|
||||
if *visited_count > 1 {
|
||||
if let Some(name) = ty.ty.path.segments.last_mut() {
|
||||
*name = format!("{name}{visited_count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the API for interacting with a Substrate runtime.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -208,7 +240,7 @@ impl RuntimeGenerator {
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
&self.metadata.types,
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives,
|
||||
@@ -259,7 +291,7 @@ impl RuntimeGenerator {
|
||||
let default_derives = derives.default_derives();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
&self.metadata.types,
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives.clone(),
|
||||
@@ -270,12 +302,11 @@ impl RuntimeGenerator {
|
||||
let types_mod_ident = types_mod.ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.pallets()
|
||||
.map(|pallet| {
|
||||
(
|
||||
pallet,
|
||||
format_ident!("{}", pallet.name.to_string().to_snake_case()),
|
||||
format_ident!("{}", pallet.name().to_string().to_snake_case()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -285,21 +316,21 @@ impl RuntimeGenerator {
|
||||
// validation of just those pallets.
|
||||
let pallet_names: Vec<_> = self
|
||||
.metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.map(|pallet| &pallet.name)
|
||||
.pallets()
|
||||
.map(|pallet| pallet.name())
|
||||
.collect();
|
||||
let pallet_names_len = pallet_names.len();
|
||||
|
||||
let metadata_hash = MetadataHasher::new()
|
||||
let metadata_hash = self
|
||||
.metadata
|
||||
.hasher()
|
||||
.only_these_pallets(&pallet_names)
|
||||
.hash(&self.metadata);
|
||||
.hash();
|
||||
|
||||
let modules = pallets_with_mod_names
|
||||
.iter()
|
||||
.map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -316,7 +347,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -325,7 +355,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -349,12 +378,12 @@ impl RuntimeGenerator {
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let outer_event_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_event_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.event.as_ref().map(|_| {
|
||||
p.event_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Event),
|
||||
@@ -369,12 +398,12 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let outer_extrinsic_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_extrinsic_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.calls.as_ref().map(|_| {
|
||||
p.call_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Call),
|
||||
@@ -389,11 +418,12 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let root_event_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_event_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.event.as_ref().map(|_| {
|
||||
|
||||
p.event_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootEvent impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
@@ -407,11 +437,11 @@ impl RuntimeGenerator {
|
||||
})
|
||||
});
|
||||
|
||||
let root_extrinsic_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_extrinsic_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.calls.as_ref().map(|_| {
|
||||
p.call_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootExtrinsic impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
@@ -425,12 +455,12 @@ impl RuntimeGenerator {
|
||||
})
|
||||
});
|
||||
|
||||
let outer_error_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_error_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.error.as_ref().map(|_| {
|
||||
p.error_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Error),
|
||||
@@ -445,41 +475,41 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let root_error_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_error_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.error.as_ref().map(|err|
|
||||
{
|
||||
let type_id = err.ty.id;
|
||||
quote! {
|
||||
|
||||
p.error_ty_id().map(|type_id| {
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
let variant_error = #mod_name::Error::decode_with_metadata(cursor, #type_id, metadata)?;
|
||||
return Ok(Error::#variant_name(variant_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
let mod_ident = &item_mod_ir.ident;
|
||||
let pallets_with_constants: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| {
|
||||
(!pallet.constants.is_empty()).then_some(pallet_mod_name)
|
||||
pallet
|
||||
.constants()
|
||||
.next()
|
||||
.is_some()
|
||||
.then_some(pallet_mod_name)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pallets_with_storage: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| {
|
||||
pallet.storage.as_ref().map(|_| pallet_mod_name)
|
||||
})
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.storage().map(|_| pallet_mod_name))
|
||||
.collect();
|
||||
|
||||
let pallets_with_calls: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.calls.as_ref().map(|_| pallet_mod_name))
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.call_ty_id().map(|_| pallet_mod_name))
|
||||
.collect();
|
||||
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
@@ -591,9 +621,9 @@ impl RuntimeGenerator {
|
||||
|
||||
/// check whether the Client you are using is aligned with the statically generated codegen.
|
||||
pub fn validate_codegen<T: #crate_path::Config, C: #crate_path::client::OfflineClientT<T>>(client: &C) -> Result<(), #crate_path::error::MetadataError> {
|
||||
let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS);
|
||||
let runtime_metadata_hash = client.metadata().hasher().only_these_pallets(&PALLETS).hash();
|
||||
if runtime_metadata_hash != [ #(#metadata_hash,)* ] {
|
||||
Err(#crate_path::error::MetadataError::IncompatibleMetadata)
|
||||
Err(#crate_path::error::MetadataError::IncompatibleCodegen)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,45 +3,42 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CodegenError, CratePath};
|
||||
use frame_metadata::v15::{RuntimeApiMetadata, RuntimeMetadataV15};
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
use subxt_metadata::{Metadata, RuntimeApiMetadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
/// Generates runtime functions for the given API metadata.
|
||||
fn generate_runtime_api(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
api: &RuntimeApiMetadata<PortableForm>,
|
||||
api: RuntimeApiMetadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
// Trait name must remain as is (upper case) to identity the runtime call.
|
||||
let trait_name = &api.name;
|
||||
let trait_name_str = api.name();
|
||||
// The snake case for the trait name.
|
||||
let trait_name_snake = format_ident!("{}", api.name.to_snake_case());
|
||||
let docs = &api.docs;
|
||||
let trait_name_snake = format_ident!("{}", api.name().to_snake_case());
|
||||
let docs = api.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let structs_and_methods: Vec<_> = api.methods.iter().map(|method| {
|
||||
let method_name = format_ident!("{}", method.name);
|
||||
let structs_and_methods: Vec<_> = api.methods().map(|method| {
|
||||
let method_name = format_ident!("{}", method.name());
|
||||
let method_name_str = method.name();
|
||||
|
||||
// Runtime function name is `TraitName_MethodName`.
|
||||
let runtime_fn_name = format!("{}_{}", trait_name, method_name);
|
||||
let docs = &method.docs;
|
||||
let docs = method.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let inputs: Vec<_> = method.inputs.iter().map(|input| {
|
||||
let inputs: Vec<_> = method.inputs().map(|input| {
|
||||
let name = format_ident!("{}", &input.name);
|
||||
let ty = type_gen.resolve_type_path(input.ty.id);
|
||||
let ty = type_gen.resolve_type_path(input.ty);
|
||||
|
||||
let param = quote!(#name: #ty);
|
||||
(param, name)
|
||||
@@ -54,7 +51,7 @@ fn generate_runtime_api(
|
||||
// all parameter types. This structure is used with metadata
|
||||
// to encode parameters to the call via `encode_as_fields_to`.
|
||||
let derives = type_gen.default_derives();
|
||||
let struct_name = format_ident!("{}", method.name.to_upper_camel_case());
|
||||
let struct_name = format_ident!("{}", method.name().to_upper_camel_case());
|
||||
let struct_params = params.clone();
|
||||
let struct_input = quote!(
|
||||
#derives
|
||||
@@ -63,21 +60,21 @@ fn generate_runtime_api(
|
||||
}
|
||||
);
|
||||
|
||||
let output = type_gen.resolve_type_path(method.output.id);
|
||||
let output = type_gen.resolve_type_path(method.output_ty());
|
||||
|
||||
let Ok(call_hash) =
|
||||
subxt_metadata::get_runtime_api_hash(metadata, trait_name, &method.name) else {
|
||||
return Err(CodegenError::MissingRuntimeApiMetadata(
|
||||
trait_name.into(),
|
||||
method.name.clone(),
|
||||
))
|
||||
};
|
||||
let Some(call_hash) = api.method_hash(method.name()) else {
|
||||
return Err(CodegenError::MissingRuntimeApiMetadata(
|
||||
trait_name_str.to_owned(),
|
||||
method_name_str.to_owned(),
|
||||
))
|
||||
};
|
||||
|
||||
let method = quote!(
|
||||
#docs
|
||||
pub fn #method_name(&self, #( #params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, #output> {
|
||||
#crate_path::runtime_api::Payload::new_static(
|
||||
#runtime_fn_name,
|
||||
#trait_name_str,
|
||||
#method_name_str,
|
||||
types::#struct_name { #( #param_names, )* },
|
||||
[#(#call_hash,)*],
|
||||
)
|
||||
@@ -87,7 +84,7 @@ fn generate_runtime_api(
|
||||
Ok((struct_input, method))
|
||||
}).collect::<Result<_, _>>()?;
|
||||
|
||||
let trait_name = format_ident!("{}", trait_name);
|
||||
let trait_name = format_ident!("{}", trait_name_str);
|
||||
|
||||
let structs = structs_and_methods.iter().map(|(struct_, _)| struct_);
|
||||
let methods = structs_and_methods.iter().map(|(_, method)| method);
|
||||
@@ -124,25 +121,16 @@ fn generate_runtime_api(
|
||||
|
||||
/// Generate the runtime APIs.
|
||||
pub fn generate_runtime_apis(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
metadata: &Metadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let apis = &metadata.apis;
|
||||
|
||||
let runtime_fns: Vec<_> = apis
|
||||
.iter()
|
||||
let runtime_fns: Vec<_> = metadata
|
||||
.runtime_api_traits()
|
||||
.map(|api| {
|
||||
generate_runtime_api(
|
||||
metadata,
|
||||
api,
|
||||
type_gen,
|
||||
types_mod_ident,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
generate_runtime_api(api, type_gen, types_mod_ident, crate_path, should_gen_docs)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
|
||||
+28
-41
@@ -3,14 +3,13 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::{
|
||||
PalletMetadata, RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier,
|
||||
StorageEntryType,
|
||||
};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::{form::PortableForm, TypeDef};
|
||||
use scale_info::TypeDef;
|
||||
use subxt_metadata::{
|
||||
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
};
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -24,29 +23,20 @@ use super::CodegenError;
|
||||
/// - `pallet` - Pallet metadata from which the storages are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
pub fn generate_storage(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(storage) = &pallet.storage else {
|
||||
let Some(storage) = pallet.storage() else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let storage_fns = storage
|
||||
.entries
|
||||
.iter()
|
||||
.entries()
|
||||
.map(|entry| {
|
||||
generate_storage_entry_fns(
|
||||
metadata,
|
||||
type_gen,
|
||||
pallet,
|
||||
entry,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
generate_storage_entry_fns(type_gen, pallet, entry, crate_path, should_gen_docs)
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
@@ -64,18 +54,16 @@ pub fn generate_storage(
|
||||
}
|
||||
|
||||
fn generate_storage_entry_fns(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
storage_entry: &StorageEntryMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
storage_entry: &StorageEntryMetadata,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let (fields, key_impl) = match &storage_entry.ty {
|
||||
let (fields, key_impl) = match storage_entry.entry_type() {
|
||||
StorageEntryType::Plain(_) => (vec![], quote!(vec![])),
|
||||
StorageEntryType::Map { key, .. } => {
|
||||
let key_ty = type_gen.resolve_type(key.id);
|
||||
match &key_ty.type_def {
|
||||
StorageEntryType::Map { key_ty, .. } => {
|
||||
match &type_gen.resolve_type(*key_ty).type_def {
|
||||
// An N-map; return each of the keys separately.
|
||||
TypeDef::Tuple(tuple) => {
|
||||
let fields = tuple
|
||||
@@ -102,7 +90,7 @@ fn generate_storage_entry_fns(
|
||||
}
|
||||
// A map with a single key; return the single key.
|
||||
_ => {
|
||||
let ty_path = type_gen.resolve_type_path(key.id);
|
||||
let ty_path = type_gen.resolve_type_path(*key_ty);
|
||||
let fields = vec![(format_ident!("_0"), ty_path)];
|
||||
let key_impl = quote! {
|
||||
vec![ #crate_path::storage::address::make_static_storage_map_key(_0.borrow()) ]
|
||||
@@ -113,21 +101,20 @@ fn generate_storage_entry_fns(
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = &pallet.name;
|
||||
let storage_name = &storage_entry.name;
|
||||
let storage_hash = subxt_metadata::get_storage_hash(metadata, pallet_name, storage_name)
|
||||
.map_err(|_| {
|
||||
CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into())
|
||||
})?;
|
||||
|
||||
let fn_name = format_ident!("{}", storage_entry.name.to_snake_case());
|
||||
let storage_entry_ty = match storage_entry.ty {
|
||||
StorageEntryType::Plain(ref ty) => ty,
|
||||
StorageEntryType::Map { ref value, .. } => value,
|
||||
let pallet_name = pallet.name();
|
||||
let storage_name = storage_entry.name();
|
||||
let Some(storage_hash) = pallet.storage_hash(storage_name) else {
|
||||
return Err(CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into()))
|
||||
};
|
||||
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty.id);
|
||||
|
||||
let docs = &storage_entry.docs;
|
||||
let fn_name = format_ident!("{}", storage_entry.name().to_snake_case());
|
||||
let storage_entry_ty = match storage_entry.entry_type() {
|
||||
StorageEntryType::Plain(ty) => *ty,
|
||||
StorageEntryType::Map { value_ty, .. } => *value_ty,
|
||||
};
|
||||
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty);
|
||||
|
||||
let docs = storage_entry.docs();
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
@@ -145,7 +132,7 @@ fn generate_storage_entry_fns(
|
||||
quote!( #field_name: impl ::std::borrow::Borrow<#field_ty> )
|
||||
});
|
||||
|
||||
let is_map_type = matches!(storage_entry.ty, StorageEntryType::Map { .. });
|
||||
let is_map_type = matches!(storage_entry.entry_type(), StorageEntryType::Map { .. });
|
||||
|
||||
// Is the entry iterable?
|
||||
let is_iterable_type = if is_map_type {
|
||||
@@ -154,7 +141,7 @@ fn generate_storage_entry_fns(
|
||||
quote!(())
|
||||
};
|
||||
|
||||
let has_default_value = match storage_entry.modifier {
|
||||
let has_default_value = match storage_entry.modifier() {
|
||||
StorageEntryModifier::Default => true,
|
||||
StorageEntryModifier::Optional => false,
|
||||
};
|
||||
|
||||
+2
-2
@@ -20,13 +20,13 @@
|
||||
//! ```no_run
|
||||
//! use std::fs;
|
||||
//! use codec::Decode;
|
||||
//! use frame_metadata::RuntimeMetadataPrefixed;
|
||||
//! use subxt_metadata::Metadata;
|
||||
//! use subxt_codegen::{CratePath, DerivesRegistry, TypeSubstitutes};
|
||||
//!
|
||||
//! let encoded = fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
|
||||
//!
|
||||
//! // Runtime metadata obtained from a node.
|
||||
//! let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &*encoded).unwrap();
|
||||
//! let metadata = Metadata::decode(&mut &*encoded).unwrap();
|
||||
//! // Module under which the API is generated.
|
||||
//! let item_mod = syn::parse_quote!(
|
||||
//! pub mod api {}
|
||||
|
||||
@@ -14,7 +14,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// `AccountNonceApi_account_nonce` function.
|
||||
let account = AccountKeyring::Alice.to_account_id();
|
||||
let runtime_api_call = subxt::dynamic::runtime_api_call(
|
||||
"AccountNonceApi_account_nonce",
|
||||
"AccountNonceApi",
|
||||
"account_nonce",
|
||||
vec![Value::from_bytes(account)],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use subxt::ext::codec::Decode;
|
||||
use subxt::ext::frame_metadata::RuntimeMetadataPrefixed;
|
||||
use subxt::metadata::Metadata;
|
||||
use subxt::utils::H256;
|
||||
use subxt::{config::PolkadotConfig, OfflineClient};
|
||||
@@ -26,8 +25,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// `subxt metadata > file.scale` to download it):
|
||||
let metadata = {
|
||||
let bytes = std::fs::read("./artifacts/polkadot_metadata_small.scale").unwrap();
|
||||
let metadata = RuntimeMetadataPrefixed::decode(&mut &*bytes).unwrap();
|
||||
Metadata::try_from(metadata).unwrap()
|
||||
Metadata::decode(&mut &*bytes).unwrap()
|
||||
};
|
||||
|
||||
// Create an offline client using the details obtained above:
|
||||
|
||||
+5
-4
@@ -14,10 +14,11 @@ homepage.workspace = true
|
||||
description = "Command line utilities for checking metadata compatibility between nodes."
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
|
||||
frame-metadata = { version = "15.1.0", features = ["v14", "v15-unstable", "std"] }
|
||||
scale-info = "2.6.0"
|
||||
sp-core-hashing = "8.0.0"
|
||||
codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] }
|
||||
frame-metadata = { workspace = true }
|
||||
scale-info = { workspace = true }
|
||||
sp-core-hashing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bitvec = { workspace = true, features = ["alloc"] }
|
||||
|
||||
+22
-58
@@ -4,73 +4,39 @@
|
||||
|
||||
use codec::Decode;
|
||||
use criterion::*;
|
||||
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use scale_info::{form::PortableForm, TypeDef, TypeDefVariant};
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use std::{fs, path::Path};
|
||||
use subxt_metadata::{
|
||||
get_call_hash, get_constant_hash, get_pallet_hash, get_storage_hash, metadata_v14_to_latest,
|
||||
MetadataHasher,
|
||||
};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
fn load_metadata() -> RuntimeMetadataV15 {
|
||||
fn load_metadata() -> Metadata {
|
||||
let bytes = fs::read(Path::new("../artifacts/polkadot_metadata_full.scale"))
|
||||
.expect("Cannot read metadata blob");
|
||||
let meta: RuntimeMetadataPrefixed =
|
||||
Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata");
|
||||
|
||||
match meta.1 {
|
||||
RuntimeMetadata::V14(v14) => metadata_v14_to_latest(v14),
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
RuntimeMetadata::V14(v14) => v14.try_into().unwrap(),
|
||||
RuntimeMetadata::V15(v15) => v15.try_into().unwrap(),
|
||||
_ => panic!("Unsupported metadata version {:?}", meta.1),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_variant(def: &TypeDef<PortableForm>) -> &TypeDefVariant<PortableForm> {
|
||||
match def {
|
||||
TypeDef::Variant(variant) => variant,
|
||||
_ => panic!("Expected a variant type, got {def:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_get_metadata_hash(c: &mut Criterion) {
|
||||
let metadata = load_metadata();
|
||||
|
||||
c.bench_function("get_metadata_hash", |b| {
|
||||
b.iter(|| MetadataHasher::new().hash(&metadata))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_get_pallet_hash(c: &mut Criterion) {
|
||||
let metadata = load_metadata();
|
||||
let mut group = c.benchmark_group("get_pallet_hash");
|
||||
|
||||
for pallet in metadata.pallets.iter() {
|
||||
let pallet_name = &pallet.name;
|
||||
group.bench_function(pallet_name, |b| {
|
||||
b.iter(|| get_pallet_hash(&metadata.types, pallet))
|
||||
});
|
||||
}
|
||||
c.bench_function("get_metadata_hash", |b| b.iter(|| metadata.hasher().hash()));
|
||||
}
|
||||
|
||||
fn bench_get_call_hash(c: &mut Criterion) {
|
||||
let metadata = load_metadata();
|
||||
let mut group = c.benchmark_group("get_call_hash");
|
||||
|
||||
for pallet in metadata.pallets.iter() {
|
||||
let pallet_name = &pallet.name;
|
||||
let call_type_id = match &pallet.calls {
|
||||
Some(calls) => calls.ty.id,
|
||||
None => continue,
|
||||
};
|
||||
let call_type = metadata.types.resolve(call_type_id).unwrap();
|
||||
let variants = expect_variant(&call_type.type_def);
|
||||
|
||||
for variant in &variants.variants {
|
||||
for pallet in metadata.pallets() {
|
||||
let pallet_name = pallet.name();
|
||||
for variant in pallet.call_variants().unwrap() {
|
||||
let call_name = &variant.name;
|
||||
let bench_name = format!("{pallet_name}/{call_name}");
|
||||
group.bench_function(&bench_name, |b| {
|
||||
b.iter(|| get_call_hash(&metadata, &pallet.name, call_name))
|
||||
});
|
||||
group.bench_function(&bench_name, |b| b.iter(|| pallet.call_hash(call_name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,13 +45,13 @@ fn bench_get_constant_hash(c: &mut Criterion) {
|
||||
let metadata = load_metadata();
|
||||
let mut group = c.benchmark_group("get_constant_hash");
|
||||
|
||||
for pallet in metadata.pallets.iter() {
|
||||
let pallet_name = &pallet.name;
|
||||
for constant in &pallet.constants {
|
||||
let constant_name = &constant.name;
|
||||
for pallet in metadata.pallets() {
|
||||
let pallet_name = pallet.name();
|
||||
for constant in pallet.constants() {
|
||||
let constant_name = constant.name();
|
||||
let bench_name = format!("{pallet_name}/{constant_name}");
|
||||
group.bench_function(&bench_name, |b| {
|
||||
b.iter(|| get_constant_hash(&metadata, &pallet.name, constant_name))
|
||||
b.iter(|| pallet.constant_hash(constant_name))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -95,18 +61,17 @@ fn bench_get_storage_hash(c: &mut Criterion) {
|
||||
let metadata = load_metadata();
|
||||
let mut group = c.benchmark_group("get_storage_hash");
|
||||
|
||||
for pallet in metadata.pallets.iter() {
|
||||
let pallet_name = &pallet.name;
|
||||
let storage_entries = match &pallet.storage {
|
||||
Some(storage) => &storage.entries,
|
||||
None => continue,
|
||||
for pallet in metadata.pallets() {
|
||||
let pallet_name = pallet.name();
|
||||
let Some(storage_entries) = pallet.storage() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for storage in storage_entries {
|
||||
let storage_name = &storage.name;
|
||||
for storage in storage_entries.entries() {
|
||||
let storage_name = storage.name();
|
||||
let bench_name = format!("{pallet_name}/{storage_name}");
|
||||
group.bench_function(&bench_name, |b| {
|
||||
b.iter(|| get_storage_hash(&metadata, &pallet.name, storage_name))
|
||||
b.iter(|| pallet.storage_hash(storage_name))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -117,7 +82,6 @@ criterion_group!(
|
||||
config = Criterion::default();
|
||||
targets =
|
||||
bench_get_metadata_hash,
|
||||
bench_get_pallet_hash,
|
||||
bench_get_call_hash,
|
||||
bench_get_constant_hash,
|
||||
bench_get_storage_hash,
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
|
||||
mod v14;
|
||||
mod v15;
|
||||
|
||||
/// An error emitted if something goes wrong converting [`frame_metadata`]
|
||||
/// types into [`crate::Metadata`].
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TryFromError {
|
||||
/// Type missing from type registry
|
||||
#[error("Type {0} is expected but not found in the type registry")]
|
||||
TypeNotFound(u32),
|
||||
/// Type was not a variant/enum type
|
||||
#[error("Type {0} was not a variant/enum type, but is expected to be one")]
|
||||
VariantExpected(u32),
|
||||
/// An unsupported metadata version was provided.
|
||||
#[error("Cannot convert v{0} metadata into Metadata type")]
|
||||
UnsupportedMetadataVersion(u32),
|
||||
}
|
||||
|
||||
impl From<crate::Metadata> for frame_metadata::RuntimeMetadataPrefixed {
|
||||
fn from(value: crate::Metadata) -> Self {
|
||||
let m: frame_metadata::v15::RuntimeMetadataV15 = value.into();
|
||||
m.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<frame_metadata::RuntimeMetadataPrefixed> for crate::Metadata {
|
||||
type Error = TryFromError;
|
||||
|
||||
fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result<Self, Self::Error> {
|
||||
match value.1 {
|
||||
frame_metadata::RuntimeMetadata::V0(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(0))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V1(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(1))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V2(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(2))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V3(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(3))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V4(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(4))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V5(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(5))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V6(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(6))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V7(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(7))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V8(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(8))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V9(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(9))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V10(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(10))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V11(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(11))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V12(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(12))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V13(_) => {
|
||||
Err(TryFromError::UnsupportedMetadataVersion(13))
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V14(m) => m.try_into(),
|
||||
frame_metadata::RuntimeMetadata::V15(m) => m.try_into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// 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 super::TryFromError;
|
||||
use crate::Metadata;
|
||||
use frame_metadata::{v14, v15};
|
||||
|
||||
impl TryFrom<v14::RuntimeMetadataV14> for Metadata {
|
||||
type Error = TryFromError;
|
||||
fn try_from(value: v14::RuntimeMetadataV14) -> Result<Self, Self::Error> {
|
||||
// Convert to v15 and then convert that into Metadata.
|
||||
v14_to_v15(value).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Metadata> for v14::RuntimeMetadataV14 {
|
||||
fn from(val: Metadata) -> Self {
|
||||
let v15 = val.into();
|
||||
v15_to_v14(v15)
|
||||
}
|
||||
}
|
||||
|
||||
fn v15_to_v14(metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 {
|
||||
v14::RuntimeMetadataV14 {
|
||||
types: metadata.types,
|
||||
pallets: metadata
|
||||
.pallets
|
||||
.into_iter()
|
||||
.map(|pallet| frame_metadata::v14::PalletMetadata {
|
||||
name: pallet.name,
|
||||
storage: pallet
|
||||
.storage
|
||||
.map(|storage| frame_metadata::v14::PalletStorageMetadata {
|
||||
prefix: storage.prefix,
|
||||
entries: storage
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let modifier = match entry.modifier {
|
||||
frame_metadata::v15::StorageEntryModifier::Optional => {
|
||||
frame_metadata::v14::StorageEntryModifier::Optional
|
||||
}
|
||||
frame_metadata::v15::StorageEntryModifier::Default => {
|
||||
frame_metadata::v14::StorageEntryModifier::Default
|
||||
}
|
||||
};
|
||||
|
||||
let ty = match entry.ty {
|
||||
frame_metadata::v15::StorageEntryType::Plain(ty) => {
|
||||
frame_metadata::v14::StorageEntryType::Plain(ty)
|
||||
},
|
||||
frame_metadata::v15::StorageEntryType::Map {
|
||||
hashers,
|
||||
key,
|
||||
value,
|
||||
} => frame_metadata::v14::StorageEntryType::Map {
|
||||
hashers: hashers.into_iter().map(|hasher| match hasher {
|
||||
frame_metadata::v15::StorageHasher::Blake2_128 => frame_metadata::v14::StorageHasher::Blake2_128,
|
||||
frame_metadata::v15::StorageHasher::Blake2_256 => frame_metadata::v14::StorageHasher::Blake2_256,
|
||||
frame_metadata::v15::StorageHasher::Blake2_128Concat => frame_metadata::v14::StorageHasher::Blake2_128Concat ,
|
||||
frame_metadata::v15::StorageHasher::Twox128 => frame_metadata::v14::StorageHasher::Twox128,
|
||||
frame_metadata::v15::StorageHasher::Twox256 => frame_metadata::v14::StorageHasher::Twox256,
|
||||
frame_metadata::v15::StorageHasher::Twox64Concat => frame_metadata::v14::StorageHasher::Twox64Concat,
|
||||
frame_metadata::v15::StorageHasher::Identity=> frame_metadata::v14::StorageHasher::Identity,
|
||||
}).collect(),
|
||||
key,
|
||||
value,
|
||||
},
|
||||
};
|
||||
|
||||
frame_metadata::v14::StorageEntryMetadata {
|
||||
name: entry.name,
|
||||
modifier,
|
||||
ty,
|
||||
default: entry.default,
|
||||
docs: entry.docs,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
calls: pallet.calls.map(|calls| frame_metadata::v14::PalletCallMetadata { ty: calls.ty } ),
|
||||
event: pallet.event.map(|event| frame_metadata::v14::PalletEventMetadata { ty: event.ty } ),
|
||||
constants: pallet.constants.into_iter().map(|constant| frame_metadata::v14::PalletConstantMetadata {
|
||||
name: constant.name,
|
||||
ty: constant.ty,
|
||||
value: constant.value,
|
||||
docs: constant.docs,
|
||||
} ).collect(),
|
||||
error: pallet.error.map(|error| frame_metadata::v14::PalletErrorMetadata { ty: error.ty } ),
|
||||
index: pallet.index,
|
||||
})
|
||||
.collect(),
|
||||
extrinsic: frame_metadata::v14::ExtrinsicMetadata {
|
||||
ty: metadata.extrinsic.ty,
|
||||
version: metadata.extrinsic.version,
|
||||
signed_extensions: metadata.extrinsic.signed_extensions.into_iter().map(|ext| {
|
||||
frame_metadata::v14::SignedExtensionMetadata {
|
||||
identifier: ext.identifier,
|
||||
ty: ext.ty,
|
||||
additional_signed: ext.additional_signed,
|
||||
}
|
||||
}).collect()
|
||||
},
|
||||
ty: metadata.ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn v14_to_v15(metadata: v14::RuntimeMetadataV14) -> v15::RuntimeMetadataV15 {
|
||||
v15::RuntimeMetadataV15 {
|
||||
types: metadata.types,
|
||||
pallets: metadata
|
||||
.pallets
|
||||
.into_iter()
|
||||
.map(|pallet| frame_metadata::v15::PalletMetadata {
|
||||
name: pallet.name,
|
||||
storage: pallet
|
||||
.storage
|
||||
.map(|storage| frame_metadata::v15::PalletStorageMetadata {
|
||||
prefix: storage.prefix,
|
||||
entries: storage
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let modifier = match entry.modifier {
|
||||
frame_metadata::v14::StorageEntryModifier::Optional => {
|
||||
frame_metadata::v15::StorageEntryModifier::Optional
|
||||
}
|
||||
frame_metadata::v14::StorageEntryModifier::Default => {
|
||||
frame_metadata::v15::StorageEntryModifier::Default
|
||||
}
|
||||
};
|
||||
|
||||
let ty = match entry.ty {
|
||||
frame_metadata::v14::StorageEntryType::Plain(ty) => {
|
||||
frame_metadata::v15::StorageEntryType::Plain(ty)
|
||||
},
|
||||
frame_metadata::v14::StorageEntryType::Map {
|
||||
hashers,
|
||||
key,
|
||||
value,
|
||||
} => frame_metadata::v15::StorageEntryType::Map {
|
||||
hashers: hashers.into_iter().map(|hasher| match hasher {
|
||||
frame_metadata::v14::StorageHasher::Blake2_128 => frame_metadata::v15::StorageHasher::Blake2_128,
|
||||
frame_metadata::v14::StorageHasher::Blake2_256 => frame_metadata::v15::StorageHasher::Blake2_256,
|
||||
frame_metadata::v14::StorageHasher::Blake2_128Concat => frame_metadata::v15::StorageHasher::Blake2_128Concat ,
|
||||
frame_metadata::v14::StorageHasher::Twox128 => frame_metadata::v15::StorageHasher::Twox128,
|
||||
frame_metadata::v14::StorageHasher::Twox256 => frame_metadata::v15::StorageHasher::Twox256,
|
||||
frame_metadata::v14::StorageHasher::Twox64Concat => frame_metadata::v15::StorageHasher::Twox64Concat,
|
||||
frame_metadata::v14::StorageHasher::Identity=> frame_metadata::v15::StorageHasher::Identity,
|
||||
}).collect(),
|
||||
key,
|
||||
value,
|
||||
},
|
||||
};
|
||||
|
||||
frame_metadata::v15::StorageEntryMetadata {
|
||||
name: entry.name,
|
||||
modifier,
|
||||
ty,
|
||||
default: entry.default,
|
||||
docs: entry.docs,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
calls: pallet.calls.map(|calls| frame_metadata::v15::PalletCallMetadata { ty: calls.ty } ),
|
||||
event: pallet.event.map(|event| frame_metadata::v15::PalletEventMetadata { ty: event.ty } ),
|
||||
constants: pallet.constants.into_iter().map(|constant| frame_metadata::v15::PalletConstantMetadata {
|
||||
name: constant.name,
|
||||
ty: constant.ty,
|
||||
value: constant.value,
|
||||
docs: constant.docs,
|
||||
} ).collect(),
|
||||
error: pallet.error.map(|error| frame_metadata::v15::PalletErrorMetadata { ty: error.ty } ),
|
||||
index: pallet.index,
|
||||
docs: Default::default(),
|
||||
})
|
||||
.collect(),
|
||||
extrinsic: frame_metadata::v15::ExtrinsicMetadata {
|
||||
ty: metadata.extrinsic.ty,
|
||||
version: metadata.extrinsic.version,
|
||||
signed_extensions: metadata.extrinsic.signed_extensions.into_iter().map(|ext| {
|
||||
frame_metadata::v15::SignedExtensionMetadata {
|
||||
identifier: ext.identifier,
|
||||
ty: ext.ty,
|
||||
additional_signed: ext.additional_signed,
|
||||
}
|
||||
}).collect()
|
||||
},
|
||||
ty: metadata.ty,
|
||||
apis: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
// 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 super::TryFromError;
|
||||
use crate::utils::variant_index::VariantIndex;
|
||||
use crate::{
|
||||
utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata,
|
||||
PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadata,
|
||||
RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata,
|
||||
StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
|
||||
};
|
||||
use frame_metadata::v15;
|
||||
use scale_info::form::PortableForm;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Converting from V15 metadata into our Subxt repr.
|
||||
mod from_v15 {
|
||||
use super::*;
|
||||
|
||||
impl TryFrom<v15::RuntimeMetadataV15> for Metadata {
|
||||
type Error = TryFromError;
|
||||
fn try_from(m: v15::RuntimeMetadataV15) -> Result<Self, TryFromError> {
|
||||
let mut pallets = OrderedMap::new();
|
||||
let mut pallets_by_index = HashMap::new();
|
||||
for (pos, p) in m.pallets.into_iter().enumerate() {
|
||||
let name: ArcStr = p.name.into();
|
||||
|
||||
let storage = p.storage.map(|s| StorageMetadata {
|
||||
prefix: s.prefix,
|
||||
entries: s
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let name: ArcStr = s.name.clone().into();
|
||||
(name.clone(), from_storage_entry_metadata(name, s))
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
let constants = p.constants.into_iter().map(|c| {
|
||||
let name: ArcStr = c.name.clone().into();
|
||||
(name.clone(), from_constant_metadata(name, c))
|
||||
});
|
||||
|
||||
let call_variant_index =
|
||||
VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), &m.types);
|
||||
let error_variant_index =
|
||||
VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), &m.types);
|
||||
let event_variant_index =
|
||||
VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), &m.types);
|
||||
|
||||
pallets_by_index.insert(p.index, pos);
|
||||
pallets.push_insert(
|
||||
name.clone(),
|
||||
PalletMetadataInner {
|
||||
name,
|
||||
index: p.index,
|
||||
storage,
|
||||
call_ty: p.calls.map(|c| c.ty.id),
|
||||
call_variant_index,
|
||||
event_ty: p.event.map(|e| e.ty.id),
|
||||
event_variant_index,
|
||||
error_ty: p.error.map(|e| e.ty.id),
|
||||
error_variant_index,
|
||||
constants: constants.collect(),
|
||||
docs: p.docs,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let apis = m.apis.into_iter().map(|api| {
|
||||
let name: ArcStr = api.name.clone().into();
|
||||
(name.clone(), from_runtime_api_metadata(name, api))
|
||||
});
|
||||
|
||||
let dispatch_error_ty = m
|
||||
.types
|
||||
.types
|
||||
.iter()
|
||||
.find(|ty| ty.ty.path.segments == ["sp_runtime", "DispatchError"])
|
||||
.map(|ty| ty.id);
|
||||
|
||||
Ok(Metadata {
|
||||
types: m.types,
|
||||
pallets,
|
||||
pallets_by_index,
|
||||
extrinsic: from_extrinsic_metadata(m.extrinsic),
|
||||
runtime_ty: m.ty.id,
|
||||
dispatch_error_ty,
|
||||
apis: apis.collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn from_signed_extension_metadata(
|
||||
value: v15::SignedExtensionMetadata<PortableForm>,
|
||||
) -> SignedExtensionMetadata {
|
||||
SignedExtensionMetadata {
|
||||
identifier: value.identifier,
|
||||
extra_ty: value.ty.id,
|
||||
additional_ty: value.additional_signed.id,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_extrinsic_metadata(value: v15::ExtrinsicMetadata<PortableForm>) -> ExtrinsicMetadata {
|
||||
ExtrinsicMetadata {
|
||||
ty: value.ty.id,
|
||||
version: value.version,
|
||||
signed_extensions: value
|
||||
.signed_extensions
|
||||
.into_iter()
|
||||
.map(from_signed_extension_metadata)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_hasher(value: v15::StorageHasher) -> StorageHasher {
|
||||
match value {
|
||||
v15::StorageHasher::Blake2_128 => StorageHasher::Blake2_128,
|
||||
v15::StorageHasher::Blake2_256 => StorageHasher::Blake2_256,
|
||||
v15::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat,
|
||||
v15::StorageHasher::Twox128 => StorageHasher::Twox128,
|
||||
v15::StorageHasher::Twox256 => StorageHasher::Twox256,
|
||||
v15::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat,
|
||||
v15::StorageHasher::Identity => StorageHasher::Identity,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_type(value: v15::StorageEntryType<PortableForm>) -> StorageEntryType {
|
||||
match value {
|
||||
v15::StorageEntryType::Plain(ty) => StorageEntryType::Plain(ty.id),
|
||||
v15::StorageEntryType::Map {
|
||||
hashers,
|
||||
key,
|
||||
value,
|
||||
} => StorageEntryType::Map {
|
||||
hashers: hashers.into_iter().map(from_storage_hasher).collect(),
|
||||
key_ty: key.id,
|
||||
value_ty: value.id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_modifier(value: v15::StorageEntryModifier) -> StorageEntryModifier {
|
||||
match value {
|
||||
v15::StorageEntryModifier::Optional => StorageEntryModifier::Optional,
|
||||
v15::StorageEntryModifier::Default => StorageEntryModifier::Default,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_metadata(
|
||||
name: ArcStr,
|
||||
s: v15::StorageEntryMetadata<PortableForm>,
|
||||
) -> StorageEntryMetadata {
|
||||
StorageEntryMetadata {
|
||||
name,
|
||||
modifier: from_storage_entry_modifier(s.modifier),
|
||||
entry_type: from_storage_entry_type(s.ty),
|
||||
default: s.default,
|
||||
docs: s.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_constant_metadata(
|
||||
name: ArcStr,
|
||||
s: v15::PalletConstantMetadata<PortableForm>,
|
||||
) -> ConstantMetadata {
|
||||
ConstantMetadata {
|
||||
name,
|
||||
ty: s.ty.id,
|
||||
value: s.value,
|
||||
docs: s.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_metadata(
|
||||
name: ArcStr,
|
||||
s: v15::RuntimeApiMetadata<PortableForm>,
|
||||
) -> RuntimeApiMetadataInner {
|
||||
RuntimeApiMetadataInner {
|
||||
name,
|
||||
docs: s.docs,
|
||||
methods: s
|
||||
.methods
|
||||
.into_iter()
|
||||
.map(|m| {
|
||||
let name: ArcStr = m.name.clone().into();
|
||||
(name.clone(), from_runtime_api_method_metadata(name, m))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_method_metadata(
|
||||
name: ArcStr,
|
||||
s: v15::RuntimeApiMethodMetadata<PortableForm>,
|
||||
) -> RuntimeApiMethodMetadata {
|
||||
RuntimeApiMethodMetadata {
|
||||
name,
|
||||
inputs: s
|
||||
.inputs
|
||||
.into_iter()
|
||||
.map(from_runtime_api_method_param_metadata)
|
||||
.collect(),
|
||||
output_ty: s.output.id,
|
||||
docs: s.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_method_param_metadata(
|
||||
s: v15::RuntimeApiMethodParamMetadata<PortableForm>,
|
||||
) -> RuntimeApiMethodParamMetadata {
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: s.name,
|
||||
ty: s.ty.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Converting from our metadata repr to V15 metadata.
|
||||
mod into_v15 {
|
||||
use super::*;
|
||||
|
||||
impl From<Metadata> for v15::RuntimeMetadataV15 {
|
||||
fn from(m: Metadata) -> Self {
|
||||
let pallets = m.pallets.into_values().into_iter().map(|p| {
|
||||
let storage = p.storage.map(|s| v15::PalletStorageMetadata {
|
||||
prefix: s.prefix,
|
||||
entries: s
|
||||
.entries
|
||||
.into_values()
|
||||
.into_iter()
|
||||
.map(from_storage_entry_metadata)
|
||||
.collect(),
|
||||
});
|
||||
|
||||
v15::PalletMetadata {
|
||||
name: (*p.name).to_owned(),
|
||||
calls: p
|
||||
.call_ty
|
||||
.map(|id| v15::PalletCallMetadata { ty: id.into() }),
|
||||
event: p
|
||||
.event_ty
|
||||
.map(|id| v15::PalletEventMetadata { ty: id.into() }),
|
||||
error: p
|
||||
.error_ty
|
||||
.map(|id| v15::PalletErrorMetadata { ty: id.into() }),
|
||||
storage,
|
||||
constants: p
|
||||
.constants
|
||||
.into_values()
|
||||
.into_iter()
|
||||
.map(from_constant_metadata)
|
||||
.collect(),
|
||||
index: p.index,
|
||||
docs: p.docs,
|
||||
}
|
||||
});
|
||||
|
||||
v15::RuntimeMetadataV15 {
|
||||
types: m.types,
|
||||
pallets: pallets.collect(),
|
||||
ty: m.runtime_ty.into(),
|
||||
extrinsic: from_extrinsic_metadata(m.extrinsic),
|
||||
apis: m
|
||||
.apis
|
||||
.into_values()
|
||||
.into_iter()
|
||||
.map(from_runtime_api_metadata)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_metadata(
|
||||
r: RuntimeApiMetadataInner,
|
||||
) -> v15::RuntimeApiMetadata<PortableForm> {
|
||||
v15::RuntimeApiMetadata {
|
||||
name: (*r.name).to_owned(),
|
||||
methods: r
|
||||
.methods
|
||||
.into_values()
|
||||
.into_iter()
|
||||
.map(from_runtime_api_method_metadata)
|
||||
.collect(),
|
||||
docs: r.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_method_metadata(
|
||||
m: RuntimeApiMethodMetadata,
|
||||
) -> v15::RuntimeApiMethodMetadata<PortableForm> {
|
||||
v15::RuntimeApiMethodMetadata {
|
||||
name: (*m.name).to_owned(),
|
||||
inputs: m
|
||||
.inputs
|
||||
.into_iter()
|
||||
.map(from_runtime_api_method_param_metadata)
|
||||
.collect(),
|
||||
output: m.output_ty.into(),
|
||||
docs: m.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_runtime_api_method_param_metadata(
|
||||
p: RuntimeApiMethodParamMetadata,
|
||||
) -> v15::RuntimeApiMethodParamMetadata<PortableForm> {
|
||||
v15::RuntimeApiMethodParamMetadata {
|
||||
name: p.name,
|
||||
ty: p.ty.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_extrinsic_metadata(e: ExtrinsicMetadata) -> v15::ExtrinsicMetadata<PortableForm> {
|
||||
v15::ExtrinsicMetadata {
|
||||
ty: e.ty.into(),
|
||||
version: e.version,
|
||||
signed_extensions: e
|
||||
.signed_extensions
|
||||
.into_iter()
|
||||
.map(from_signed_extension_metadata)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_signed_extension_metadata(
|
||||
s: SignedExtensionMetadata,
|
||||
) -> v15::SignedExtensionMetadata<PortableForm> {
|
||||
v15::SignedExtensionMetadata {
|
||||
identifier: s.identifier,
|
||||
ty: s.extra_ty.into(),
|
||||
additional_signed: s.additional_ty.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_constant_metadata(c: ConstantMetadata) -> v15::PalletConstantMetadata<PortableForm> {
|
||||
v15::PalletConstantMetadata {
|
||||
name: (*c.name).to_owned(),
|
||||
ty: c.ty.into(),
|
||||
value: c.value,
|
||||
docs: c.docs,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_metadata(
|
||||
s: StorageEntryMetadata,
|
||||
) -> v15::StorageEntryMetadata<PortableForm> {
|
||||
v15::StorageEntryMetadata {
|
||||
docs: s.docs,
|
||||
default: s.default,
|
||||
name: (*s.name).to_owned(),
|
||||
ty: from_storage_entry_type(s.entry_type),
|
||||
modifier: from_storage_entry_modifier(s.modifier),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_modifier(s: StorageEntryModifier) -> v15::StorageEntryModifier {
|
||||
match s {
|
||||
StorageEntryModifier::Default => v15::StorageEntryModifier::Default,
|
||||
StorageEntryModifier::Optional => v15::StorageEntryModifier::Optional,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_entry_type(s: StorageEntryType) -> v15::StorageEntryType<PortableForm> {
|
||||
match s {
|
||||
StorageEntryType::Plain(ty) => v15::StorageEntryType::Plain(ty.into()),
|
||||
StorageEntryType::Map {
|
||||
hashers,
|
||||
key_ty,
|
||||
value_ty,
|
||||
} => v15::StorageEntryType::Map {
|
||||
hashers: hashers.into_iter().map(from_storage_hasher).collect(),
|
||||
key: key_ty.into(),
|
||||
value: value_ty.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_storage_hasher(s: StorageHasher) -> v15::StorageHasher {
|
||||
match s {
|
||||
StorageHasher::Blake2_128 => v15::StorageHasher::Blake2_128,
|
||||
StorageHasher::Blake2_256 => v15::StorageHasher::Blake2_256,
|
||||
StorageHasher::Blake2_128Concat => v15::StorageHasher::Blake2_128Concat,
|
||||
StorageHasher::Twox128 => v15::StorageHasher::Twox128,
|
||||
StorageHasher::Twox256 => v15::StorageHasher::Twox256,
|
||||
StorageHasher::Twox64Concat => v15::StorageHasher::Twox64Concat,
|
||||
StorageHasher::Identity => v15::StorageHasher::Identity,
|
||||
}
|
||||
}
|
||||
}
|
||||
+609
-91
@@ -2,101 +2,619 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
mod retain;
|
||||
mod validation;
|
||||
//! A representation of the metadata provided by a substrate based node.
|
||||
//! This representation is optimized to be used by Subxt and related crates,
|
||||
//! and is independent of the different versions of metadata that can be
|
||||
//! provided from a node.
|
||||
//!
|
||||
//! Typically, this will be constructed by either:
|
||||
//!
|
||||
//! 1. Calling `Metadata::decode()` given some metadata bytes obtained
|
||||
//! from a node (this uses [`codec::Decode`]).
|
||||
//! 2. Obtaining [`frame_metadata::RuntimeMetadataPrefixed`], and then
|
||||
//! using `.try_into()` to convert it into [`Metadata`].
|
||||
|
||||
use frame_metadata::{v14::RuntimeMetadataV14, v15::RuntimeMetadataV15};
|
||||
#![deny(missing_docs)]
|
||||
|
||||
pub use retain::retain_metadata_pallets;
|
||||
pub use validation::{
|
||||
get_call_hash, get_constant_hash, get_pallet_hash, get_runtime_api_hash, get_storage_hash,
|
||||
MetadataHasher, NotFound,
|
||||
};
|
||||
mod from_into;
|
||||
mod utils;
|
||||
|
||||
/// Convert the metadata V14 to the latest metadata version.
|
||||
pub fn metadata_v14_to_latest(metadata: RuntimeMetadataV14) -> RuntimeMetadataV15 {
|
||||
RuntimeMetadataV15 {
|
||||
types: metadata.types,
|
||||
pallets: metadata
|
||||
.pallets
|
||||
.into_iter()
|
||||
.map(|pallet| frame_metadata::v15::PalletMetadata {
|
||||
name: pallet.name,
|
||||
storage: pallet
|
||||
.storage
|
||||
.map(|storage| frame_metadata::v15::PalletStorageMetadata {
|
||||
prefix: storage.prefix,
|
||||
entries: storage
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
let modifier = match entry.modifier {
|
||||
frame_metadata::v14::StorageEntryModifier::Optional => {
|
||||
frame_metadata::v15::StorageEntryModifier::Optional
|
||||
}
|
||||
frame_metadata::v14::StorageEntryModifier::Default => {
|
||||
frame_metadata::v15::StorageEntryModifier::Default
|
||||
}
|
||||
};
|
||||
use scale_info::{form::PortableForm, PortableRegistry, Variant};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use utils::ordered_map::OrderedMap;
|
||||
use utils::variant_index::VariantIndex;
|
||||
|
||||
let ty = match entry.ty {
|
||||
frame_metadata::v14::StorageEntryType::Plain(ty) => {
|
||||
frame_metadata::v15::StorageEntryType::Plain(ty)
|
||||
},
|
||||
frame_metadata::v14::StorageEntryType::Map {
|
||||
hashers,
|
||||
key,
|
||||
value,
|
||||
} => frame_metadata::v15::StorageEntryType::Map {
|
||||
hashers: hashers.into_iter().map(|hasher| match hasher {
|
||||
frame_metadata::v14::StorageHasher::Blake2_128 => frame_metadata::v15::StorageHasher::Blake2_128,
|
||||
frame_metadata::v14::StorageHasher::Blake2_256 => frame_metadata::v15::StorageHasher::Blake2_256,
|
||||
frame_metadata::v14::StorageHasher::Blake2_128Concat => frame_metadata::v15::StorageHasher::Blake2_128Concat ,
|
||||
frame_metadata::v14::StorageHasher::Twox128 => frame_metadata::v15::StorageHasher::Twox128,
|
||||
frame_metadata::v14::StorageHasher::Twox256 => frame_metadata::v15::StorageHasher::Twox256,
|
||||
frame_metadata::v14::StorageHasher::Twox64Concat => frame_metadata::v15::StorageHasher::Twox64Concat,
|
||||
frame_metadata::v14::StorageHasher::Identity=> frame_metadata::v15::StorageHasher::Identity,
|
||||
}).collect(),
|
||||
key,
|
||||
value,
|
||||
},
|
||||
};
|
||||
type ArcStr = Arc<str>;
|
||||
|
||||
frame_metadata::v15::StorageEntryMetadata {
|
||||
name: entry.name,
|
||||
modifier,
|
||||
ty,
|
||||
default: entry.default,
|
||||
docs: entry.docs,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
calls: pallet.calls.map(|calls| frame_metadata::v15::PalletCallMetadata { ty: calls.ty } ),
|
||||
event: pallet.event.map(|event| frame_metadata::v15::PalletEventMetadata { ty: event.ty } ),
|
||||
constants: pallet.constants.into_iter().map(|constant| frame_metadata::v15::PalletConstantMetadata {
|
||||
name: constant.name,
|
||||
ty: constant.ty,
|
||||
value: constant.value,
|
||||
docs: constant.docs,
|
||||
} ).collect(),
|
||||
error: pallet.error.map(|error| frame_metadata::v15::PalletErrorMetadata { ty: error.ty } ),
|
||||
index: pallet.index,
|
||||
docs: Default::default(),
|
||||
})
|
||||
.collect(),
|
||||
extrinsic: frame_metadata::v15::ExtrinsicMetadata {
|
||||
ty: metadata.extrinsic.ty,
|
||||
version: metadata.extrinsic.version,
|
||||
signed_extensions: metadata.extrinsic.signed_extensions.into_iter().map(|ext| {
|
||||
frame_metadata::v15::SignedExtensionMetadata {
|
||||
identifier: ext.identifier,
|
||||
ty: ext.ty,
|
||||
additional_signed: ext.additional_signed,
|
||||
}
|
||||
}).collect()
|
||||
},
|
||||
ty: metadata.ty,
|
||||
apis: Default::default(),
|
||||
pub use from_into::TryFromError;
|
||||
pub use utils::validation::MetadataHasher;
|
||||
|
||||
/// Node metadata. This can be constructed by providing some compatible [`frame_metadata`]
|
||||
/// which is then decoded into this. We aim to preserve all of the existing information in
|
||||
/// the incoming metadata while optimizing the format a little for Subxt's use cases.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Metadata {
|
||||
/// Type registry containing all types used in the metadata.
|
||||
types: PortableRegistry,
|
||||
/// Metadata of all the pallets.
|
||||
pallets: OrderedMap<ArcStr, PalletMetadataInner>,
|
||||
/// Find the location in the pallet Vec by pallet index.
|
||||
pallets_by_index: HashMap<u8, usize>,
|
||||
/// Metadata of the extrinsic.
|
||||
extrinsic: ExtrinsicMetadata,
|
||||
/// The type ID of the `Runtime` type.
|
||||
runtime_ty: u32,
|
||||
/// The type Id of the `DispatchError` type, which Subxt makes use of.
|
||||
dispatch_error_ty: Option<u32>,
|
||||
/// Details about each of the runtime API traits.
|
||||
apis: OrderedMap<ArcStr, RuntimeApiMetadataInner>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Access the underlying type registry.
|
||||
pub fn types(&self) -> &PortableRegistry {
|
||||
&self.types
|
||||
}
|
||||
|
||||
/// Mutable access to the underlying type registry.
|
||||
pub fn types_mut(&mut self) -> &mut PortableRegistry {
|
||||
&mut self.types
|
||||
}
|
||||
|
||||
/// The type ID of the `Runtime` type.
|
||||
pub fn runtime_ty(&self) -> u32 {
|
||||
self.runtime_ty
|
||||
}
|
||||
|
||||
/// The type ID of the `DispatchError` type, if it exists.
|
||||
pub fn dispatch_error_ty(&self) -> Option<u32> {
|
||||
self.dispatch_error_ty
|
||||
}
|
||||
|
||||
/// Return details about the extrinsic format.
|
||||
pub fn extrinsic(&self) -> &ExtrinsicMetadata {
|
||||
&self.extrinsic
|
||||
}
|
||||
|
||||
/// An iterator over all of the available pallets.
|
||||
pub fn pallets(&self) -> impl ExactSizeIterator<Item = PalletMetadata<'_>> {
|
||||
self.pallets.values().iter().map(|inner| PalletMetadata {
|
||||
inner,
|
||||
types: self.types(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Access a pallet given its encoded variant index.
|
||||
pub fn pallet_by_index(&self, variant_index: u8) -> Option<PalletMetadata<'_>> {
|
||||
let inner = self
|
||||
.pallets_by_index
|
||||
.get(&variant_index)
|
||||
.and_then(|i| self.pallets.get_by_index(*i))?;
|
||||
|
||||
Some(PalletMetadata {
|
||||
inner,
|
||||
types: self.types(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Access a pallet given its name.
|
||||
pub fn pallet_by_name(&self, pallet_name: &str) -> Option<PalletMetadata<'_>> {
|
||||
let inner = self.pallets.get_by_key(pallet_name)?;
|
||||
|
||||
Some(PalletMetadata {
|
||||
inner,
|
||||
types: self.types(),
|
||||
})
|
||||
}
|
||||
|
||||
/// An iterator over all of the runtime APIs.
|
||||
pub fn runtime_api_traits(&self) -> impl ExactSizeIterator<Item = RuntimeApiMetadata<'_>> {
|
||||
self.apis.values().iter().map(|inner| RuntimeApiMetadata {
|
||||
inner,
|
||||
types: self.types(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Access a runtime API trait given its name.
|
||||
pub fn runtime_api_trait_by_name(&'_ self, name: &str) -> Option<RuntimeApiMetadata<'_>> {
|
||||
let inner = self.apis.get_by_key(name)?;
|
||||
Some(RuntimeApiMetadata {
|
||||
inner,
|
||||
types: self.types(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Obtain a unique hash representing this metadata or specific parts of it.
|
||||
pub fn hasher(&self) -> MetadataHasher {
|
||||
MetadataHasher::new(self)
|
||||
}
|
||||
|
||||
/// Filter out any pallets that we don't want to keep, retaining only those that we do.
|
||||
pub fn retain<F, G>(&mut self, pallet_filter: F, api_filter: G)
|
||||
where
|
||||
F: FnMut(&str) -> bool,
|
||||
G: FnMut(&str) -> bool,
|
||||
{
|
||||
utils::retain::retain_metadata(self, pallet_filter, api_filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a specific pallet.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PalletMetadata<'a> {
|
||||
inner: &'a PalletMetadataInner,
|
||||
types: &'a PortableRegistry,
|
||||
}
|
||||
|
||||
impl<'a> PalletMetadata<'a> {
|
||||
/// The pallet name.
|
||||
pub fn name(&self) -> &'a str {
|
||||
&self.inner.name
|
||||
}
|
||||
|
||||
/// The pallet index.
|
||||
pub fn index(&self) -> u8 {
|
||||
self.inner.index
|
||||
}
|
||||
|
||||
/// The pallet docs.
|
||||
pub fn docs(&self) -> &'a [String] {
|
||||
&self.inner.docs
|
||||
}
|
||||
|
||||
/// Type ID for the pallet's Call type, if it exists.
|
||||
pub fn call_ty_id(&self) -> Option<u32> {
|
||||
self.inner.call_ty
|
||||
}
|
||||
|
||||
/// Type ID for the pallet's Event type, if it exists.
|
||||
pub fn event_ty_id(&self) -> Option<u32> {
|
||||
self.inner.event_ty
|
||||
}
|
||||
|
||||
/// Type ID for the pallet's Error type, if it exists.
|
||||
pub fn error_ty_id(&self) -> Option<u32> {
|
||||
self.inner.error_ty
|
||||
}
|
||||
|
||||
/// Return metadata about the pallet's storage entries.
|
||||
pub fn storage(&self) -> Option<&'a StorageMetadata> {
|
||||
self.inner.storage.as_ref()
|
||||
}
|
||||
|
||||
/// Return all of the event variants, if an event type exists.
|
||||
pub fn event_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
|
||||
VariantIndex::get(self.inner.event_ty, self.types)
|
||||
}
|
||||
|
||||
/// Return an event variant given it's encoded variant index.
|
||||
pub fn event_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
|
||||
self.inner.event_variant_index.lookup_by_index(
|
||||
variant_index,
|
||||
self.inner.event_ty,
|
||||
self.types,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return all of the call variants, if a call type exists.
|
||||
pub fn call_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
|
||||
VariantIndex::get(self.inner.call_ty, self.types)
|
||||
}
|
||||
|
||||
/// Return a call variant given it's encoded variant index.
|
||||
pub fn call_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
|
||||
self.inner
|
||||
.call_variant_index
|
||||
.lookup_by_index(variant_index, self.inner.call_ty, self.types)
|
||||
}
|
||||
|
||||
/// Return a call variant given it's name.
|
||||
pub fn call_variant_by_name(&self, call_name: &str) -> Option<&'a Variant<PortableForm>> {
|
||||
self.inner
|
||||
.call_variant_index
|
||||
.lookup_by_name(call_name, self.inner.call_ty, self.types)
|
||||
}
|
||||
|
||||
/// Return all of the error variants, if an error type exists.
|
||||
pub fn error_variants(&self) -> Option<&'a [Variant<PortableForm>]> {
|
||||
VariantIndex::get(self.inner.error_ty, self.types)
|
||||
}
|
||||
|
||||
/// Return an error variant given it's encoded variant index.
|
||||
pub fn error_variant_by_index(&self, variant_index: u8) -> Option<&'a Variant<PortableForm>> {
|
||||
self.inner.error_variant_index.lookup_by_index(
|
||||
variant_index,
|
||||
self.inner.error_ty,
|
||||
self.types,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return constant details given the constant name.
|
||||
pub fn constant_by_name(&self, name: &str) -> Option<&'a ConstantMetadata> {
|
||||
self.inner.constants.get_by_key(name)
|
||||
}
|
||||
|
||||
/// An iterator over the constants in this pallet.
|
||||
pub fn constants(&self) -> impl ExactSizeIterator<Item = &'a ConstantMetadata> {
|
||||
self.inner.constants.values().iter()
|
||||
}
|
||||
|
||||
/// Return a hash for the storage entry, or None if it was not found.
|
||||
pub fn storage_hash(&self, entry_name: &str) -> Option<[u8; 32]> {
|
||||
crate::utils::validation::get_storage_hash(self, entry_name)
|
||||
}
|
||||
|
||||
/// Return a hash for the constant, or None if it was not found.
|
||||
pub fn constant_hash(&self, constant_name: &str) -> Option<[u8; 32]> {
|
||||
crate::utils::validation::get_constant_hash(self, constant_name)
|
||||
}
|
||||
|
||||
/// Return a hash for the call, or None if it was not found.
|
||||
pub fn call_hash(&self, call_name: &str) -> Option<[u8; 32]> {
|
||||
crate::utils::validation::get_call_hash(self, call_name)
|
||||
}
|
||||
|
||||
/// Return a hash for the entire pallet.
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
crate::utils::validation::get_pallet_hash(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PalletMetadataInner {
|
||||
/// Pallet name.
|
||||
name: ArcStr,
|
||||
/// Pallet index.
|
||||
index: u8,
|
||||
/// Pallet storage metadata.
|
||||
storage: Option<StorageMetadata>,
|
||||
/// Type ID for the pallet Call enum.
|
||||
call_ty: Option<u32>,
|
||||
/// Call variants by name/u8.
|
||||
call_variant_index: VariantIndex,
|
||||
/// Type ID for the pallet Event enum.
|
||||
event_ty: Option<u32>,
|
||||
/// Event variants by name/u8.
|
||||
event_variant_index: VariantIndex,
|
||||
/// Type ID for the pallet Error enum.
|
||||
error_ty: Option<u32>,
|
||||
/// Error variants by name/u8.
|
||||
error_variant_index: VariantIndex,
|
||||
/// Map from constant name to constant details.
|
||||
constants: OrderedMap<ArcStr, ConstantMetadata>,
|
||||
/// Pallet documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
/// Metadata for the storage entries in a pallet.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StorageMetadata {
|
||||
/// The common prefix used by all storage entries.
|
||||
prefix: String,
|
||||
/// Map from storage entry name to details.
|
||||
entries: OrderedMap<ArcStr, StorageEntryMetadata>,
|
||||
}
|
||||
|
||||
impl StorageMetadata {
|
||||
/// The common prefix used by all storage entries.
|
||||
pub fn prefix(&self) -> &str {
|
||||
&self.prefix
|
||||
}
|
||||
|
||||
/// An iterator over the storage entries.
|
||||
pub fn entries(&self) -> impl ExactSizeIterator<Item = &StorageEntryMetadata> {
|
||||
self.entries.values().iter()
|
||||
}
|
||||
|
||||
/// Return a specific storage entry given its name.
|
||||
pub fn entry_by_name(&self, name: &str) -> Option<&StorageEntryMetadata> {
|
||||
self.entries.get_by_key(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a single storage entry.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StorageEntryMetadata {
|
||||
/// Variable name of the storage entry.
|
||||
name: ArcStr,
|
||||
/// An `Option` modifier of that storage entry.
|
||||
modifier: StorageEntryModifier,
|
||||
/// Type of the value stored in the entry.
|
||||
entry_type: StorageEntryType,
|
||||
/// Default value (SCALE encoded).
|
||||
default: Vec<u8>,
|
||||
/// Storage entry documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl StorageEntryMetadata {
|
||||
/// Name of this entry.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
/// Is the entry value optional or does it have a default value.
|
||||
pub fn modifier(&self) -> StorageEntryModifier {
|
||||
self.modifier
|
||||
}
|
||||
/// Type of the storage entry.
|
||||
pub fn entry_type(&self) -> &StorageEntryType {
|
||||
&self.entry_type
|
||||
}
|
||||
/// The SCALE encoded default value for this entry.
|
||||
pub fn default_bytes(&self) -> &[u8] {
|
||||
&self.default
|
||||
}
|
||||
/// Storage entry documentation.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of a storage entry.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StorageEntryType {
|
||||
/// Plain storage entry (just the value).
|
||||
Plain(u32),
|
||||
/// A storage map.
|
||||
Map {
|
||||
/// One or more hashers, should be one hasher per key element.
|
||||
hashers: Vec<StorageHasher>,
|
||||
/// The type of the key, can be a tuple with elements for each of the hashers.
|
||||
key_ty: u32,
|
||||
/// The type of the value.
|
||||
value_ty: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Hasher used by storage maps.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum StorageHasher {
|
||||
/// 128-bit Blake2 hash.
|
||||
Blake2_128,
|
||||
/// 256-bit Blake2 hash.
|
||||
Blake2_256,
|
||||
/// Multiple 128-bit Blake2 hashes concatenated.
|
||||
Blake2_128Concat,
|
||||
/// 128-bit XX hash.
|
||||
Twox128,
|
||||
/// 256-bit XX hash.
|
||||
Twox256,
|
||||
/// Multiple 64-bit XX hashes concatenated.
|
||||
Twox64Concat,
|
||||
/// Identity hashing (no hashing).
|
||||
Identity,
|
||||
}
|
||||
|
||||
/// Is the storage entry optional, or does it have a default value.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum StorageEntryModifier {
|
||||
/// The storage entry returns an `Option<T>`, with `None` if the key is not present.
|
||||
Optional,
|
||||
/// The storage entry returns `T::Default` if the key is not present.
|
||||
Default,
|
||||
}
|
||||
|
||||
/// Metadata for a single constant.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstantMetadata {
|
||||
/// Name of the pallet constant.
|
||||
name: ArcStr,
|
||||
/// Type of the pallet constant.
|
||||
ty: u32,
|
||||
/// Value stored in the constant (SCALE encoded).
|
||||
value: Vec<u8>,
|
||||
/// Constant documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl ConstantMetadata {
|
||||
/// Name of the pallet constant.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
/// Type of the pallet constant.
|
||||
pub fn ty(&self) -> u32 {
|
||||
self.ty
|
||||
}
|
||||
/// Value stored in the constant (SCALE encoded).
|
||||
pub fn value(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
/// Constant documentation.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for the extrinsic type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtrinsicMetadata {
|
||||
/// The type of the extrinsic.
|
||||
ty: u32,
|
||||
/// Extrinsic version.
|
||||
version: u8,
|
||||
/// The signed extensions in the order they appear in the extrinsic.
|
||||
signed_extensions: Vec<SignedExtensionMetadata>,
|
||||
}
|
||||
|
||||
impl ExtrinsicMetadata {
|
||||
/// Type of the extrinsic.
|
||||
pub fn ty(&self) -> u32 {
|
||||
self.ty
|
||||
}
|
||||
|
||||
/// Extrinsic version.
|
||||
pub fn version(&self) -> u8 {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// The extra/additional information associated with the extrinsic.
|
||||
pub fn signed_extensions(&self) -> &[SignedExtensionMetadata] {
|
||||
&self.signed_extensions
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for the signed extensions used by extrinsics.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SignedExtensionMetadata {
|
||||
/// The unique signed extension identifier, which may be different from the type name.
|
||||
identifier: String,
|
||||
/// The type of the signed extension, with the data to be included in the extrinsic.
|
||||
extra_ty: u32,
|
||||
/// The type of the additional signed data, with the data to be included in the signed payload
|
||||
additional_ty: u32,
|
||||
}
|
||||
|
||||
impl SignedExtensionMetadata {
|
||||
/// The unique signed extension identifier, which may be different from the type name.
|
||||
pub fn identifier(&self) -> &str {
|
||||
&self.identifier
|
||||
}
|
||||
/// The type of the signed extension, with the data to be included in the extrinsic.
|
||||
pub fn extra_ty(&self) -> u32 {
|
||||
self.extra_ty
|
||||
}
|
||||
/// The type of the additional signed data, with the data to be included in the signed payload
|
||||
pub fn additional_ty(&self) -> u32 {
|
||||
self.additional_ty
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for the available runtime APIs.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RuntimeApiMetadata<'a> {
|
||||
inner: &'a RuntimeApiMetadataInner,
|
||||
types: &'a PortableRegistry,
|
||||
}
|
||||
|
||||
impl<'a> RuntimeApiMetadata<'a> {
|
||||
/// Trait name.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.inner.name
|
||||
}
|
||||
/// Trait documentation.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.inner.docs
|
||||
}
|
||||
/// An iterator over the trait methods.
|
||||
pub fn methods(&self) -> impl ExactSizeIterator<Item = &'a RuntimeApiMethodMetadata> {
|
||||
self.inner.methods.values().iter()
|
||||
}
|
||||
/// Get a specific trait method given its name.
|
||||
pub fn method_by_name(&self, name: &str) -> Option<&'a RuntimeApiMethodMetadata> {
|
||||
self.inner.methods.get_by_key(name)
|
||||
}
|
||||
/// Return a hash for the constant, or None if it was not found.
|
||||
pub fn method_hash(&self, method_name: &str) -> Option<[u8; 32]> {
|
||||
crate::utils::validation::get_runtime_api_hash(self, method_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct RuntimeApiMetadataInner {
|
||||
/// Trait name.
|
||||
name: ArcStr,
|
||||
/// Trait methods.
|
||||
methods: OrderedMap<ArcStr, RuntimeApiMethodMetadata>,
|
||||
/// Trait documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
/// Metadata for a single runtime API method.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RuntimeApiMethodMetadata {
|
||||
/// Method name.
|
||||
name: ArcStr,
|
||||
/// Method parameters.
|
||||
inputs: Vec<RuntimeApiMethodParamMetadata>,
|
||||
/// Method output type.
|
||||
output_ty: u32,
|
||||
/// Method documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl RuntimeApiMethodMetadata {
|
||||
/// Method name.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
/// Method documentation.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
/// Method inputs.
|
||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = &RuntimeApiMethodParamMetadata> {
|
||||
self.inputs.iter()
|
||||
}
|
||||
/// Method return type.
|
||||
pub fn output_ty(&self) -> u32 {
|
||||
self.output_ty
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a single input parameter to a runtime API method.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RuntimeApiMethodParamMetadata {
|
||||
/// Parameter name.
|
||||
pub name: String,
|
||||
/// Parameter type.
|
||||
pub ty: u32,
|
||||
}
|
||||
|
||||
// Support decoding metadata from the "wire" format directly into this.
|
||||
// Errors may be lost in the case that the metadata content is somehow invalid.
|
||||
impl codec::Decode for Metadata {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(input)?;
|
||||
let metadata = match metadata.1 {
|
||||
frame_metadata::RuntimeMetadata::V14(md) => md.try_into(),
|
||||
frame_metadata::RuntimeMetadata::V15(md) => md.try_into(),
|
||||
_ => return Err("Cannot try_into() to Metadata: unsupported metadata version".into()),
|
||||
};
|
||||
|
||||
metadata.map_err(|_e| "Cannot try_into() to Metadata.".into())
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata can be encoded, too. It will encode into a format that's compatible with what
|
||||
// Subxt requires, and that it can be decoded back from. The actual specifics of the format
|
||||
// can change over time.
|
||||
impl codec::Encode for Metadata {
|
||||
fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
|
||||
let m: frame_metadata::v15::RuntimeMetadataV15 = self.clone().into();
|
||||
let m: frame_metadata::RuntimeMetadataPrefixed = m.into();
|
||||
m.encode_to(dest)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
fn load_metadata() -> Vec<u8> {
|
||||
std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap()
|
||||
}
|
||||
|
||||
// We don't expect to lose any information converting back and forth between
|
||||
// our own representation and the latest version emitted from a node that we can
|
||||
// work with.
|
||||
#[test]
|
||||
fn is_isomorphic_to_v15() {
|
||||
let bytes = load_metadata();
|
||||
|
||||
// Decode into our metadata struct:
|
||||
let metadata = Metadata::decode(&mut &*bytes).unwrap();
|
||||
|
||||
// Convert into v15 metadata:
|
||||
let v15: frame_metadata::v15::RuntimeMetadataV15 = metadata.into();
|
||||
let prefixed = frame_metadata::RuntimeMetadataPrefixed::from(v15);
|
||||
|
||||
// Re-encode that:
|
||||
let new_bytes = prefixed.encode();
|
||||
|
||||
// The bytes should be identical:
|
||||
assert_eq!(bytes, new_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
|
||||
pub mod ordered_map;
|
||||
pub mod retain;
|
||||
pub mod validation;
|
||||
pub mod variant_index;
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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 std::collections::HashMap;
|
||||
|
||||
/// A minimal ordered map to let one search for
|
||||
/// things by key or get the values in insert order.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OrderedMap<K, V> {
|
||||
values: Vec<V>,
|
||||
map: HashMap<K, usize>,
|
||||
}
|
||||
|
||||
impl<K, V> Default for OrderedMap<K, V> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
values: Default::default(),
|
||||
map: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> OrderedMap<K, V>
|
||||
where
|
||||
K: PartialEq + Eq + std::hash::Hash,
|
||||
{
|
||||
/// Create a new, empty [`OrderedMap`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Number of entries in the map.
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Is the map empty.
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
|
||||
/// Retain specific entries.
|
||||
pub fn retain<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(&V) -> bool,
|
||||
{
|
||||
let values = std::mem::take(&mut self.values);
|
||||
let map = std::mem::take(&mut self.map);
|
||||
|
||||
// Filter the values, storing a map from old to new positions:
|
||||
let mut new_values = Vec::new();
|
||||
let mut old_pos_to_new_pos = HashMap::new();
|
||||
for (pos, value) in values.into_iter().enumerate().filter(|(_, v)| f(v)) {
|
||||
old_pos_to_new_pos.insert(pos, new_values.len());
|
||||
new_values.push(value);
|
||||
}
|
||||
|
||||
// Update the values now we've filtered them:
|
||||
self.values = new_values;
|
||||
|
||||
// Rebuild the map using the new positions:
|
||||
self.map = map
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| old_pos_to_new_pos.get(&v).map(|v2| (k, *v2)))
|
||||
.collect();
|
||||
}
|
||||
|
||||
/// Push/insert an item to the end of the map.
|
||||
pub fn push_insert(&mut self, key: K, value: V) {
|
||||
let idx = self.values.len();
|
||||
self.values.push(value);
|
||||
self.map.insert(key, idx);
|
||||
}
|
||||
|
||||
/// Get an item by its key.
|
||||
pub fn get_by_key<Q>(&self, key: &Q) -> Option<&V>
|
||||
where
|
||||
K: std::borrow::Borrow<Q>,
|
||||
Q: std::hash::Hash + Eq + ?Sized,
|
||||
{
|
||||
self.map.get(key).and_then(|&v| self.values.get(v))
|
||||
}
|
||||
|
||||
/// Get an item by its index.
|
||||
pub fn get_by_index(&self, i: usize) -> Option<&V> {
|
||||
self.values.get(i)
|
||||
}
|
||||
|
||||
/// Access the underlying values.
|
||||
pub fn values(&self) -> &[V] {
|
||||
&self.values
|
||||
}
|
||||
|
||||
/// Mutable access to the underlying values.
|
||||
pub fn values_mut(&mut self) -> &mut [V] {
|
||||
&mut self.values
|
||||
}
|
||||
|
||||
/// Return the underlying values.
|
||||
pub fn into_values(self) -> Vec<V> {
|
||||
self.values
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> FromIterator<(K, V)> for OrderedMap<K, V>
|
||||
where
|
||||
K: PartialEq + Eq + std::hash::Hash,
|
||||
{
|
||||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
|
||||
let mut map = OrderedMap::new();
|
||||
for (k, v) in iter {
|
||||
map.push_insert(k, v)
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn retain() {
|
||||
let mut m = OrderedMap::from_iter([(1, 'a'), (2, 'b'), (3, 'c')]);
|
||||
|
||||
m.retain(|v| *v != 'b');
|
||||
|
||||
assert_eq!(m.get_by_key(&1), Some(&'a'));
|
||||
assert_eq!(m.get_by_key(&2), None);
|
||||
assert_eq!(m.get_by_key(&3), Some(&'c'));
|
||||
|
||||
assert_eq!(m.values(), &['a', 'c'])
|
||||
}
|
||||
}
|
||||
@@ -4,133 +4,120 @@
|
||||
|
||||
//! Utility functions to generate a subset of the metadata.
|
||||
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletMetadata, RuntimeApiMetadata, RuntimeMetadataV15, StorageEntryType,
|
||||
};
|
||||
use scale_info::{form::PortableForm, interner::UntrackedSymbol, TypeDef};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
collections::{BTreeMap, HashSet},
|
||||
use crate::{
|
||||
ExtrinsicMetadata, Metadata, PalletMetadataInner, RuntimeApiMetadataInner, StorageEntryType,
|
||||
};
|
||||
use scale_info::TypeDef;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
/// Collect all type IDs needed to represent the provided pallet.
|
||||
fn collect_pallet_types(pallet: &PalletMetadata<PortableForm>, type_ids: &mut HashSet<u32>) {
|
||||
fn collect_pallet_types(pallet: &PalletMetadataInner, type_ids: &mut HashSet<u32>) {
|
||||
if let Some(storage) = &pallet.storage {
|
||||
for entry in &storage.entries {
|
||||
match entry.ty {
|
||||
for entry in storage.entries() {
|
||||
match entry.entry_type {
|
||||
StorageEntryType::Plain(ty) => {
|
||||
type_ids.insert(ty.id);
|
||||
type_ids.insert(ty);
|
||||
}
|
||||
StorageEntryType::Map { key, value, .. } => {
|
||||
type_ids.insert(key.id);
|
||||
type_ids.insert(value.id);
|
||||
StorageEntryType::Map {
|
||||
key_ty, value_ty, ..
|
||||
} => {
|
||||
type_ids.insert(key_ty);
|
||||
type_ids.insert(value_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(calls) = &pallet.calls {
|
||||
type_ids.insert(calls.ty.id);
|
||||
if let Some(ty) = pallet.call_ty {
|
||||
type_ids.insert(ty);
|
||||
}
|
||||
|
||||
if let Some(event) = &pallet.event {
|
||||
type_ids.insert(event.ty.id);
|
||||
if let Some(ty) = pallet.event_ty {
|
||||
type_ids.insert(ty);
|
||||
}
|
||||
|
||||
for constant in &pallet.constants {
|
||||
type_ids.insert(constant.ty.id);
|
||||
for constant in pallet.constants.values() {
|
||||
type_ids.insert(constant.ty);
|
||||
}
|
||||
|
||||
if let Some(error) = &pallet.error {
|
||||
type_ids.insert(error.ty.id);
|
||||
if let Some(ty) = pallet.error_ty {
|
||||
type_ids.insert(ty);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update all type IDs of the provided pallet using the new type IDs from the portable registry.
|
||||
fn update_pallet_types(pallet: &mut PalletMetadata<PortableForm>, map_ids: &BTreeMap<u32, u32>) {
|
||||
fn update_pallet_types(pallet: &mut PalletMetadataInner, map_ids: &BTreeMap<u32, u32>) {
|
||||
if let Some(storage) = &mut pallet.storage {
|
||||
for entry in &mut storage.entries {
|
||||
match &mut entry.ty {
|
||||
for entry in storage.entries.values_mut() {
|
||||
match &mut entry.entry_type {
|
||||
StorageEntryType::Plain(ty) => {
|
||||
update_type(ty, map_ids);
|
||||
}
|
||||
StorageEntryType::Map { key, value, .. } => {
|
||||
update_type(key, map_ids);
|
||||
update_type(value, map_ids);
|
||||
StorageEntryType::Map {
|
||||
key_ty, value_ty, ..
|
||||
} => {
|
||||
update_type(key_ty, map_ids);
|
||||
update_type(value_ty, map_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(calls) = &mut pallet.calls {
|
||||
update_type(&mut calls.ty, map_ids);
|
||||
if let Some(ty) = &mut pallet.call_ty {
|
||||
update_type(ty, map_ids);
|
||||
}
|
||||
|
||||
if let Some(event) = &mut pallet.event {
|
||||
update_type(&mut event.ty, map_ids);
|
||||
if let Some(ty) = &mut pallet.event_ty {
|
||||
update_type(ty, map_ids);
|
||||
}
|
||||
|
||||
for constant in &mut pallet.constants {
|
||||
if let Some(ty) = &mut pallet.error_ty {
|
||||
update_type(ty, map_ids);
|
||||
}
|
||||
|
||||
for constant in pallet.constants.values_mut() {
|
||||
update_type(&mut constant.ty, map_ids);
|
||||
}
|
||||
|
||||
if let Some(error) = &mut pallet.error {
|
||||
update_type(&mut error.ty, map_ids);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all type IDs needed to represent the extrinsic metadata.
|
||||
fn collect_extrinsic_types(
|
||||
extrinsic: &ExtrinsicMetadata<PortableForm>,
|
||||
type_ids: &mut HashSet<u32>,
|
||||
) {
|
||||
type_ids.insert(extrinsic.ty.id);
|
||||
fn collect_extrinsic_types(extrinsic: &ExtrinsicMetadata, type_ids: &mut HashSet<u32>) {
|
||||
type_ids.insert(extrinsic.ty);
|
||||
|
||||
for signed in &extrinsic.signed_extensions {
|
||||
type_ids.insert(signed.ty.id);
|
||||
type_ids.insert(signed.additional_signed.id);
|
||||
type_ids.insert(signed.extra_ty);
|
||||
type_ids.insert(signed.additional_ty);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update all type IDs of the provided extrinsic metadata using the new type IDs from the portable registry.
|
||||
fn update_extrinsic_types(
|
||||
extrinsic: &mut ExtrinsicMetadata<PortableForm>,
|
||||
map_ids: &BTreeMap<u32, u32>,
|
||||
) {
|
||||
fn update_extrinsic_types(extrinsic: &mut ExtrinsicMetadata, map_ids: &BTreeMap<u32, u32>) {
|
||||
update_type(&mut extrinsic.ty, map_ids);
|
||||
|
||||
for signed in &mut extrinsic.signed_extensions {
|
||||
update_type(&mut signed.ty, map_ids);
|
||||
update_type(&mut signed.additional_signed, map_ids);
|
||||
update_type(&mut signed.extra_ty, map_ids);
|
||||
update_type(&mut signed.additional_ty, map_ids);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all type IDs needed to represent the runtime APIs.
|
||||
fn collect_runtime_api_types(
|
||||
apis: &[RuntimeApiMetadata<PortableForm>],
|
||||
type_ids: &mut HashSet<u32>,
|
||||
) {
|
||||
for api in apis {
|
||||
for method in &api.methods {
|
||||
for input in &method.inputs {
|
||||
type_ids.insert(input.ty.id);
|
||||
}
|
||||
type_ids.insert(method.output.id);
|
||||
fn collect_runtime_api_types(api: &RuntimeApiMetadataInner, type_ids: &mut HashSet<u32>) {
|
||||
for method in api.methods.values() {
|
||||
for input in &method.inputs {
|
||||
type_ids.insert(input.ty);
|
||||
}
|
||||
type_ids.insert(method.output_ty);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update all type IDs of the provided runtime APIs metadata using the new type IDs from the portable registry.
|
||||
fn update_runtime_api_types(
|
||||
apis: &mut [RuntimeApiMetadata<PortableForm>],
|
||||
map_ids: &BTreeMap<u32, u32>,
|
||||
) {
|
||||
fn update_runtime_api_types(apis: &mut [RuntimeApiMetadataInner], map_ids: &BTreeMap<u32, u32>) {
|
||||
for api in apis {
|
||||
for method in &mut api.methods {
|
||||
for method in api.methods.values_mut() {
|
||||
for input in &mut method.inputs {
|
||||
update_type(&mut input.ty, map_ids);
|
||||
}
|
||||
update_type(&mut method.output, map_ids);
|
||||
update_type(&mut method.output_ty, map_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,32 +127,32 @@ fn update_runtime_api_types(
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`scale_info::PortableRegistry`] did not retain all needed types.
|
||||
fn update_type(ty: &mut UntrackedSymbol<TypeId>, map_ids: &BTreeMap<u32, u32>) {
|
||||
let old_id = ty.id;
|
||||
fn update_type(ty: &mut u32, map_ids: &BTreeMap<u32, u32>) {
|
||||
let old_id = *ty;
|
||||
let new_id = map_ids
|
||||
.get(&old_id)
|
||||
.copied()
|
||||
.unwrap_or_else(|| panic!("PortableRegistry did not retain type id {old_id}. This is a bug. Please open an issue."));
|
||||
*ty = new_id.into();
|
||||
*ty = new_id;
|
||||
}
|
||||
|
||||
/// Strip any pallets out of the RuntimeCall type that aren't the ones we want to keep.
|
||||
/// The RuntimeCall type is referenced in a bunch of places, so doing this prevents us from
|
||||
/// holding on to stuff in pallets we've asked not to keep.
|
||||
fn retain_pallets_in_runtime_call_type<F>(metadata: &mut RuntimeMetadataV15, mut filter: F)
|
||||
fn retain_pallets_in_runtime_call_type<F>(metadata: &mut Metadata, mut filter: F)
|
||||
where
|
||||
F: FnMut(&str) -> bool,
|
||||
{
|
||||
let extrinsic_ty = metadata
|
||||
.types
|
||||
.types
|
||||
.get_mut(metadata.extrinsic.ty.id as usize)
|
||||
.get_mut(metadata.extrinsic.ty as usize)
|
||||
.expect("Metadata should contain extrinsic type in registry");
|
||||
|
||||
let Some(call_ty) = extrinsic_ty.ty.type_params
|
||||
.iter_mut()
|
||||
.find(|ty| ty.name == "Call")
|
||||
.and_then(|ty| ty.ty) else { return };
|
||||
.and_then(|ty| ty.ty) else { return; };
|
||||
|
||||
let call_ty = metadata
|
||||
.types
|
||||
@@ -182,7 +169,7 @@ where
|
||||
}
|
||||
|
||||
/// Generate a subset of the metadata that contains only the
|
||||
/// types needed to represent the provided pallets.
|
||||
/// types needed to represent the provided pallets and runtime APIs.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -193,36 +180,55 @@ where
|
||||
///
|
||||
/// Panics if the [`scale_info::PortableRegistry`] did not retain all needed types,
|
||||
/// or the metadata does not contain the "sp_runtime::DispatchError" type.
|
||||
pub fn retain_metadata_pallets<F>(metadata: &mut RuntimeMetadataV15, mut filter: F)
|
||||
where
|
||||
pub fn retain_metadata<F, G>(
|
||||
metadata: &mut Metadata,
|
||||
mut pallets_filter: F,
|
||||
mut runtime_apis_filter: G,
|
||||
) where
|
||||
F: FnMut(&str) -> bool,
|
||||
G: FnMut(&str) -> bool,
|
||||
{
|
||||
let mut type_ids = HashSet::new();
|
||||
|
||||
// There is a special RuntimeCall type which points to all pallets and call types by default.
|
||||
// This brings in a significant chunk of types. We trim this down to only include variants
|
||||
// for the pallets we're retaining, to avoid this.
|
||||
retain_pallets_in_runtime_call_type(metadata, &mut filter);
|
||||
retain_pallets_in_runtime_call_type(metadata, &mut pallets_filter);
|
||||
|
||||
// Filter our pallet list to only those pallets we want to keep. Keep hold of all
|
||||
//type IDs in the pallets we're keeping.
|
||||
// type IDs in the pallets we're keeping. Retain all, if no filter specified.
|
||||
metadata.pallets.retain(|pallet| {
|
||||
if filter(&pallet.name) {
|
||||
let should_retain = pallets_filter(&pallet.name);
|
||||
if should_retain {
|
||||
collect_pallet_types(pallet, &mut type_ids);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
should_retain
|
||||
});
|
||||
|
||||
// We index pallets by their u8 index for easy access. Rebuild this index.
|
||||
metadata.pallets_by_index = metadata
|
||||
.pallets
|
||||
.values()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(pos, p)| (p.index, pos))
|
||||
.collect();
|
||||
|
||||
// Keep the extrinsic stuff referenced in our metadata.
|
||||
collect_extrinsic_types(&metadata.extrinsic, &mut type_ids);
|
||||
|
||||
// Keep the "runtime" type ID, since it's referenced in our metadata.
|
||||
type_ids.insert(metadata.ty.id);
|
||||
type_ids.insert(metadata.runtime_ty);
|
||||
|
||||
// Keep the runtime APIs types.
|
||||
collect_runtime_api_types(&metadata.apis, &mut type_ids);
|
||||
// Keep only the runtime API types that the filter allows for. Keep hold of all
|
||||
// type IDs in the runtime apis we're keeping. Retain all, if no filter specified.
|
||||
metadata.apis.retain(|api| {
|
||||
let should_retain = runtime_apis_filter(&api.name);
|
||||
if should_retain {
|
||||
collect_runtime_api_types(api, &mut type_ids);
|
||||
}
|
||||
should_retain
|
||||
});
|
||||
|
||||
// Additionally, subxt depends on the `DispatchError` type existing; we use the same
|
||||
// logic here that is used when building our `Metadata`.
|
||||
@@ -239,31 +245,31 @@ where
|
||||
let map_ids = metadata.types.retain(|id| type_ids.contains(&id));
|
||||
|
||||
// And finally, we can go and update all of our type IDs in the metadata as a result of this:
|
||||
for pallets in &mut metadata.pallets {
|
||||
for pallets in metadata.pallets.values_mut() {
|
||||
update_pallet_types(pallets, &map_ids);
|
||||
}
|
||||
update_extrinsic_types(&mut metadata.extrinsic, &map_ids);
|
||||
update_type(&mut metadata.ty, &map_ids);
|
||||
update_runtime_api_types(&mut metadata.apis, &map_ids);
|
||||
update_type(&mut metadata.runtime_ty, &map_ids);
|
||||
update_runtime_api_types(metadata.apis.values_mut(), &map_ids);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::metadata_v14_to_latest;
|
||||
use crate::Metadata;
|
||||
use codec::Decode;
|
||||
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn load_metadata() -> RuntimeMetadataV15 {
|
||||
fn load_metadata() -> Metadata {
|
||||
let bytes = fs::read(Path::new("../artifacts/polkadot_metadata_full.scale"))
|
||||
.expect("Cannot read metadata blob");
|
||||
let meta: RuntimeMetadataPrefixed =
|
||||
Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata");
|
||||
|
||||
match meta.1 {
|
||||
RuntimeMetadata::V14(v14) => metadata_v14_to_latest(v14),
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
RuntimeMetadata::V14(v14) => v14.try_into().unwrap(),
|
||||
RuntimeMetadata::V15(v15) => v15.try_into().unwrap(),
|
||||
_ => panic!("Unsupported metadata version {:?}", meta.1),
|
||||
}
|
||||
}
|
||||
@@ -273,12 +279,40 @@ mod tests {
|
||||
let metadata_cache = load_metadata();
|
||||
|
||||
// Retain one pallet at a time ensuring the test does not panic.
|
||||
for pallet in &metadata_cache.pallets {
|
||||
for pallet in metadata_cache.pallets() {
|
||||
let mut metadata = metadata_cache.clone();
|
||||
retain_metadata_pallets(&mut metadata, |pallet_name| pallet_name == pallet.name);
|
||||
retain_metadata(
|
||||
&mut metadata,
|
||||
|pallet_name| pallet_name == pallet.name(),
|
||||
|_| true,
|
||||
);
|
||||
|
||||
assert_eq!(metadata.pallets.len(), 1);
|
||||
assert_eq!(metadata.pallets.get(0).unwrap().name, pallet.name);
|
||||
assert_eq!(
|
||||
&*metadata.pallets.get_by_index(0).unwrap().name,
|
||||
pallet.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retain_one_runtime_api() {
|
||||
let metadata_cache = load_metadata();
|
||||
|
||||
// Retain one runtime API at a time ensuring the test does not panic.
|
||||
for runtime_api in metadata_cache.runtime_api_traits() {
|
||||
let mut metadata = metadata_cache.clone();
|
||||
retain_metadata(
|
||||
&mut metadata,
|
||||
|_| true,
|
||||
|runtime_api_name| runtime_api_name == runtime_api.name(),
|
||||
);
|
||||
|
||||
assert_eq!(metadata.apis.len(), 1);
|
||||
assert_eq!(
|
||||
&*metadata.apis.get_by_index(0).unwrap().name,
|
||||
runtime_api.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
//! Utility functions for metadata validation.
|
||||
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata,
|
||||
RuntimeMetadataV15, StorageEntryMetadata, StorageEntryType,
|
||||
use crate::{
|
||||
ExtrinsicMetadata, Metadata, PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata,
|
||||
StorageEntryMetadata, StorageEntryType,
|
||||
};
|
||||
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, Variant};
|
||||
use std::collections::HashSet;
|
||||
@@ -194,12 +194,12 @@ fn get_type_hash(
|
||||
/// Obtain the hash representation of a `frame_metadata::v15::ExtrinsicMetadata`.
|
||||
fn get_extrinsic_hash(
|
||||
registry: &PortableRegistry,
|
||||
extrinsic: &ExtrinsicMetadata<PortableForm>,
|
||||
extrinsic: &ExtrinsicMetadata,
|
||||
) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::<u32>::new();
|
||||
|
||||
let mut bytes = concat_and_hash2(
|
||||
&get_type_hash(registry, extrinsic.ty.id, &mut visited_ids),
|
||||
&get_type_hash(registry, extrinsic.ty, &mut visited_ids),
|
||||
&[extrinsic.version; 32],
|
||||
);
|
||||
|
||||
@@ -207,12 +207,8 @@ fn get_extrinsic_hash(
|
||||
bytes = concat_and_hash4(
|
||||
&bytes,
|
||||
&hash(signed_extension.identifier.as_bytes()),
|
||||
&get_type_hash(registry, signed_extension.ty.id, &mut visited_ids),
|
||||
&get_type_hash(
|
||||
registry,
|
||||
signed_extension.additional_signed.id,
|
||||
&mut visited_ids,
|
||||
),
|
||||
&get_type_hash(registry, signed_extension.extra_ty, &mut visited_ids),
|
||||
&get_type_hash(registry, signed_extension.additional_ty, &mut visited_ids),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -222,33 +218,33 @@ fn get_extrinsic_hash(
|
||||
/// Get the hash corresponding to a single storage entry.
|
||||
fn get_storage_entry_hash(
|
||||
registry: &PortableRegistry,
|
||||
entry: &StorageEntryMetadata<PortableForm>,
|
||||
entry: &StorageEntryMetadata,
|
||||
visited_ids: &mut HashSet<u32>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
let mut bytes = concat_and_hash3(
|
||||
&hash(entry.name.as_bytes()),
|
||||
// Cloning 'entry.modifier' should essentially be a copy.
|
||||
&[entry.modifier.clone() as u8; HASH_LEN],
|
||||
&[entry.modifier as u8; HASH_LEN],
|
||||
&hash(&entry.default),
|
||||
);
|
||||
|
||||
match &entry.ty {
|
||||
match &entry.entry_type {
|
||||
StorageEntryType::Plain(ty) => {
|
||||
concat_and_hash2(&bytes, &get_type_hash(registry, ty.id, visited_ids))
|
||||
concat_and_hash2(&bytes, &get_type_hash(registry, *ty, visited_ids))
|
||||
}
|
||||
StorageEntryType::Map {
|
||||
hashers,
|
||||
key,
|
||||
value,
|
||||
key_ty,
|
||||
value_ty,
|
||||
} => {
|
||||
for hasher in hashers {
|
||||
// Cloning the hasher should essentially be a copy.
|
||||
bytes = concat_and_hash2(&bytes, &[hasher.clone() as u8; HASH_LEN]);
|
||||
bytes = concat_and_hash2(&bytes, &[*hasher as u8; HASH_LEN]);
|
||||
}
|
||||
concat_and_hash3(
|
||||
&bytes,
|
||||
&get_type_hash(registry, key.id, visited_ids),
|
||||
&get_type_hash(registry, value.id, visited_ids),
|
||||
&get_type_hash(registry, *key_ty, visited_ids),
|
||||
&get_type_hash(registry, *value_ty, visited_ids),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -256,9 +252,9 @@ fn get_storage_entry_hash(
|
||||
|
||||
/// Get the hash corresponding to a single runtime API method.
|
||||
fn get_runtime_method_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
trait_metadata: &RuntimeApiMetadata<PortableForm>,
|
||||
method_metadata: &RuntimeApiMethodMetadata<PortableForm>,
|
||||
registry: &PortableRegistry,
|
||||
trait_name: &str,
|
||||
method_metadata: &RuntimeApiMethodMetadata,
|
||||
visited_ids: &mut HashSet<u32>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
// The trait name is part of the runtime API call that is being
|
||||
@@ -266,7 +262,7 @@ fn get_runtime_method_hash(
|
||||
// connected to the method in the same way as a parameter is
|
||||
// to the method.
|
||||
let mut bytes = concat_and_hash2(
|
||||
&hash(trait_metadata.name.as_bytes()),
|
||||
&hash(trait_name.as_bytes()),
|
||||
&hash(method_metadata.name.as_bytes()),
|
||||
);
|
||||
|
||||
@@ -274,198 +270,123 @@ fn get_runtime_method_hash(
|
||||
bytes = concat_and_hash3(
|
||||
&bytes,
|
||||
&hash(input.name.as_bytes()),
|
||||
&get_type_hash(&metadata.types, input.ty.id, visited_ids),
|
||||
&get_type_hash(registry, input.ty, visited_ids),
|
||||
);
|
||||
}
|
||||
|
||||
bytes = concat_and_hash2(
|
||||
&bytes,
|
||||
&get_type_hash(&metadata.types, method_metadata.output.id, visited_ids),
|
||||
&get_type_hash(registry, method_metadata.output_ty, visited_ids),
|
||||
);
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Obtain the hash of all of a runtime API trait, including all of its methods.
|
||||
fn get_runtime_trait_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
trait_metadata: &RuntimeApiMetadata<PortableForm>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::new();
|
||||
let method_name = hash(trait_metadata.name.as_bytes());
|
||||
let trait_name = &*trait_metadata.inner.name;
|
||||
let method_bytes = trait_metadata
|
||||
.methods()
|
||||
.fold([0u8; HASH_LEN], |bytes, method_metadata| {
|
||||
// We don't care what order the trait methods exist in, and want the hash to
|
||||
// be identical regardless. For this, we can just XOR the hashes for each method
|
||||
// together; we'll get the same output whichever order they are XOR'd together in,
|
||||
// so long as each individual method is the same.
|
||||
xor(
|
||||
bytes,
|
||||
get_runtime_method_hash(
|
||||
trait_metadata.types,
|
||||
trait_name,
|
||||
method_metadata,
|
||||
&mut visited_ids,
|
||||
),
|
||||
)
|
||||
});
|
||||
|
||||
let method_bytes =
|
||||
trait_metadata
|
||||
.methods
|
||||
.iter()
|
||||
.fold([0u8; HASH_LEN], |bytes, method_metadata| {
|
||||
// We don't care what order the trait methods exist in, and want the hash to
|
||||
// be identical regardless. For this, we can just XOR the hashes for each method
|
||||
// together; we'll get the same output whichever order they are XOR'd together in,
|
||||
// so long as each individual method is the same.
|
||||
xor(
|
||||
bytes,
|
||||
get_runtime_method_hash(
|
||||
metadata,
|
||||
trait_metadata,
|
||||
method_metadata,
|
||||
&mut visited_ids,
|
||||
),
|
||||
)
|
||||
});
|
||||
|
||||
concat_and_hash2(&method_name, &method_bytes)
|
||||
concat_and_hash2(&hash(trait_name.as_bytes()), &method_bytes)
|
||||
}
|
||||
|
||||
/// Obtain the hash for a specific storage item, or an error if it's not found.
|
||||
pub fn get_storage_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
pallet_name: &str,
|
||||
storage_name: &str,
|
||||
) -> Result<[u8; HASH_LEN], NotFound> {
|
||||
let pallet = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.find(|p| p.name == pallet_name)
|
||||
.ok_or(NotFound::Root)?;
|
||||
pub fn get_storage_hash(pallet: &PalletMetadata, entry_name: &str) -> Option<[u8; HASH_LEN]> {
|
||||
let storage = pallet.storage()?;
|
||||
let entry = storage.entry_by_name(entry_name)?;
|
||||
|
||||
let storage = pallet.storage.as_ref().ok_or(NotFound::Item)?;
|
||||
|
||||
let entry = storage
|
||||
.entries
|
||||
.iter()
|
||||
.find(|s| s.name == storage_name)
|
||||
.ok_or(NotFound::Item)?;
|
||||
|
||||
let hash = get_storage_entry_hash(&metadata.types, entry, &mut HashSet::new());
|
||||
Ok(hash)
|
||||
let hash = get_storage_entry_hash(pallet.types, entry, &mut HashSet::new());
|
||||
Some(hash)
|
||||
}
|
||||
|
||||
/// Obtain the hash for a specific constant, or an error if it's not found.
|
||||
pub fn get_constant_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
pallet_name: &str,
|
||||
constant_name: &str,
|
||||
) -> Result<[u8; HASH_LEN], NotFound> {
|
||||
let pallet = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.find(|p| p.name == pallet_name)
|
||||
.ok_or(NotFound::Root)?;
|
||||
|
||||
let constant = pallet
|
||||
.constants
|
||||
.iter()
|
||||
.find(|c| c.name == constant_name)
|
||||
.ok_or(NotFound::Item)?;
|
||||
pub fn get_constant_hash(pallet: &PalletMetadata, constant_name: &str) -> Option<[u8; HASH_LEN]> {
|
||||
let constant = pallet.constant_by_name(constant_name)?;
|
||||
|
||||
// We only need to check that the type of the constant asked for matches.
|
||||
let bytes = get_type_hash(&metadata.types, constant.ty.id, &mut HashSet::new());
|
||||
Ok(bytes)
|
||||
let bytes = get_type_hash(pallet.types, constant.ty, &mut HashSet::new());
|
||||
Some(bytes)
|
||||
}
|
||||
|
||||
/// Obtain the hash for a specific call, or an error if it's not found.
|
||||
pub fn get_call_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
pallet_name: &str,
|
||||
call_name: &str,
|
||||
) -> Result<[u8; HASH_LEN], NotFound> {
|
||||
let pallet = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.find(|p| p.name == pallet_name)
|
||||
.ok_or(NotFound::Root)?;
|
||||
|
||||
let call_id = pallet.calls.as_ref().ok_or(NotFound::Item)?.ty.id;
|
||||
|
||||
let call_ty = metadata.types.resolve(call_id).ok_or(NotFound::Item)?;
|
||||
|
||||
let call_variants = match &call_ty.type_def {
|
||||
TypeDef::Variant(variant) => &variant.variants,
|
||||
_ => return Err(NotFound::Item),
|
||||
};
|
||||
|
||||
let variant = call_variants
|
||||
.iter()
|
||||
.find(|v| v.name == call_name)
|
||||
.ok_or(NotFound::Item)?;
|
||||
pub fn get_call_hash(pallet: &PalletMetadata, call_name: &str) -> Option<[u8; HASH_LEN]> {
|
||||
let call_variant = pallet.call_variant_by_name(call_name)?;
|
||||
|
||||
// hash the specific variant representing the call we are interested in.
|
||||
let hash = get_variant_hash(&metadata.types, variant, &mut HashSet::new());
|
||||
Ok(hash)
|
||||
let hash = get_variant_hash(pallet.types, call_variant, &mut HashSet::new());
|
||||
Some(hash)
|
||||
}
|
||||
|
||||
/// Obtain the hash of a specific runtime API function, or an error if it's not found.
|
||||
pub fn get_runtime_api_hash(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
trait_name: &str,
|
||||
runtime_apis: &RuntimeApiMetadata,
|
||||
method_name: &str,
|
||||
) -> Result<[u8; HASH_LEN], NotFound> {
|
||||
let trait_metadata = metadata
|
||||
.apis
|
||||
.iter()
|
||||
.find(|m| m.name == trait_name)
|
||||
.ok_or(NotFound::Root)?;
|
||||
) -> Option<[u8; HASH_LEN]> {
|
||||
let trait_name = &*runtime_apis.inner.name;
|
||||
let method_metadata = runtime_apis.method_by_name(method_name)?;
|
||||
|
||||
let method_metadata = trait_metadata
|
||||
.methods
|
||||
.iter()
|
||||
.find(|m| m.name == method_name)
|
||||
.ok_or(NotFound::Item)?;
|
||||
|
||||
Ok(get_runtime_method_hash(
|
||||
metadata,
|
||||
trait_metadata,
|
||||
Some(get_runtime_method_hash(
|
||||
runtime_apis.types,
|
||||
trait_name,
|
||||
method_metadata,
|
||||
&mut HashSet::new(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Obtain the hash representation of a `frame_metadata::v15::PalletMetadata`.
|
||||
pub fn get_pallet_hash(
|
||||
registry: &PortableRegistry,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::<u32>::new();
|
||||
let registry = pallet.types;
|
||||
|
||||
let call_bytes = match &pallet.calls {
|
||||
Some(calls) => get_type_hash(registry, calls.ty.id, &mut visited_ids),
|
||||
let call_bytes = match pallet.call_ty_id() {
|
||||
Some(calls) => get_type_hash(registry, calls, &mut visited_ids),
|
||||
None => [0u8; HASH_LEN],
|
||||
};
|
||||
let event_bytes = match &pallet.event {
|
||||
Some(event) => get_type_hash(registry, event.ty.id, &mut visited_ids),
|
||||
let event_bytes = match pallet.event_ty_id() {
|
||||
Some(event) => get_type_hash(registry, event, &mut visited_ids),
|
||||
None => [0u8; HASH_LEN],
|
||||
};
|
||||
let error_bytes = match &pallet.error {
|
||||
Some(error) => get_type_hash(registry, error.ty.id, &mut visited_ids),
|
||||
let error_bytes = match pallet.error_ty_id() {
|
||||
Some(error) => get_type_hash(registry, error, &mut visited_ids),
|
||||
None => [0u8; HASH_LEN],
|
||||
};
|
||||
let constant_bytes = pallet
|
||||
.constants
|
||||
.iter()
|
||||
.fold([0u8; HASH_LEN], |bytes, constant| {
|
||||
// We don't care what order the constants occur in, so XOR together the combinations
|
||||
// of (constantName, constantType) to make the order we see them irrelevant.
|
||||
let constant_hash = concat_and_hash2(
|
||||
&hash(constant.name.as_bytes()),
|
||||
&get_type_hash(registry, constant.ty.id, &mut visited_ids),
|
||||
);
|
||||
xor(bytes, constant_hash)
|
||||
});
|
||||
let storage_bytes = match &pallet.storage {
|
||||
let constant_bytes = pallet.constants().fold([0u8; HASH_LEN], |bytes, constant| {
|
||||
// We don't care what order the constants occur in, so XOR together the combinations
|
||||
// of (constantName, constantType) to make the order we see them irrelevant.
|
||||
let constant_hash = concat_and_hash2(
|
||||
&hash(constant.name.as_bytes()),
|
||||
&get_type_hash(registry, constant.ty(), &mut visited_ids),
|
||||
);
|
||||
xor(bytes, constant_hash)
|
||||
});
|
||||
let storage_bytes = match pallet.storage() {
|
||||
Some(storage) => {
|
||||
let prefix_hash = hash(storage.prefix.as_bytes());
|
||||
let entries_hash = storage
|
||||
.entries
|
||||
.iter()
|
||||
.fold([0u8; HASH_LEN], |bytes, entry| {
|
||||
// We don't care what order the storage entries occur in, so XOR them together
|
||||
// to make the order irrelevant.
|
||||
xor(
|
||||
bytes,
|
||||
get_storage_entry_hash(registry, entry, &mut visited_ids),
|
||||
)
|
||||
});
|
||||
let prefix_hash = hash(storage.prefix().as_bytes());
|
||||
let entries_hash = storage.entries().fold([0u8; HASH_LEN], |bytes, entry| {
|
||||
// We don't care what order the storage entries occur in, so XOR them together
|
||||
// to make the order irrelevant.
|
||||
xor(
|
||||
bytes,
|
||||
get_storage_entry_hash(registry, entry, &mut visited_ids),
|
||||
)
|
||||
});
|
||||
concat_and_hash2(&prefix_hash, &entries_hash)
|
||||
}
|
||||
None => [0u8; HASH_LEN],
|
||||
@@ -481,21 +402,18 @@ pub fn get_pallet_hash(
|
||||
)
|
||||
}
|
||||
|
||||
/// Obtain the hash representation of a `frame_metadata::v15::RuntimeMetadataV15`.
|
||||
/// Obtain a hash representation of our metadata or some part of it.
|
||||
/// This is obtained by calling [`crate::Metadata::hasher()`].
|
||||
pub struct MetadataHasher<'a> {
|
||||
metadata: &'a Metadata,
|
||||
specific_pallets: Option<Vec<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'a> Default for MetadataHasher<'a> {
|
||||
fn default() -> Self {
|
||||
MetadataHasher::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetadataHasher<'a> {
|
||||
/// Create a new [`MetadataHasher`]
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new(metadata: &'a Metadata) -> Self {
|
||||
Self {
|
||||
metadata,
|
||||
specific_pallets: None,
|
||||
}
|
||||
}
|
||||
@@ -507,67 +425,43 @@ impl<'a> MetadataHasher<'a> {
|
||||
}
|
||||
|
||||
/// Hash the given metadata.
|
||||
pub fn hash(&self, metadata: &RuntimeMetadataV15) -> [u8; HASH_LEN] {
|
||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::<u32>::new();
|
||||
|
||||
let pallet_hash = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.fold([0u8; HASH_LEN], |bytes, pallet| {
|
||||
// If specific pallets are given, only include this pallet if it's
|
||||
// in the list.
|
||||
if let Some(specific_pallets) = &self.specific_pallets {
|
||||
if specific_pallets.iter().all(|&p| p != pallet.name) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
// We don't care what order the pallets are seen in, so XOR their
|
||||
// hashes together to be order independent.
|
||||
xor(bytes, get_pallet_hash(&metadata.types, pallet))
|
||||
});
|
||||
let metadata = self.metadata;
|
||||
|
||||
let apis_hash = metadata.apis.iter().fold([0u8; HASH_LEN], |bytes, api| {
|
||||
// We don't care what order the runtime APIs are seen in, so XOR
|
||||
xor(bytes, get_runtime_trait_hash(metadata, api))
|
||||
let pallet_hash = metadata.pallets().fold([0u8; HASH_LEN], |bytes, pallet| {
|
||||
// If specific pallets are given, only include this pallet if it's
|
||||
// in the list.
|
||||
if let Some(specific_pallets) = &self.specific_pallets {
|
||||
if specific_pallets.iter().all(|&p| p != pallet.name()) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
// We don't care what order the pallets are seen in, so XOR their
|
||||
// hashes together to be order independent.
|
||||
xor(bytes, get_pallet_hash(pallet))
|
||||
});
|
||||
|
||||
let apis_hash = metadata
|
||||
.runtime_api_traits()
|
||||
.fold([0u8; HASH_LEN], |bytes, api| {
|
||||
// We don't care what order the runtime APIs are seen in, so XOR
|
||||
xor(bytes, get_runtime_trait_hash(api))
|
||||
});
|
||||
|
||||
let extrinsic_hash = get_extrinsic_hash(&metadata.types, &metadata.extrinsic);
|
||||
let runtime_hash = get_type_hash(&metadata.types, metadata.ty.id, &mut visited_ids);
|
||||
let runtime_hash = get_type_hash(&metadata.types, metadata.runtime_ty(), &mut visited_ids);
|
||||
|
||||
concat_and_hash4(&pallet_hash, &apis_hash, &extrinsic_hash, &runtime_hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned if we attempt to get the hash for a specific call, constant,
|
||||
/// storage or runtime API function does not exist.
|
||||
///
|
||||
/// The location of the specific item (call, constant, storage or runtime API function)
|
||||
/// is stored with two indirections:
|
||||
/// - Root
|
||||
/// The root location of the item. For calls, constants, storage this represents the
|
||||
/// pallet name. While for runtime API function this represents the trait name.
|
||||
/// - Item
|
||||
/// The actual item. For calls, constants, storage this represents the actual name.
|
||||
/// While for runtime API functions this represents the method name.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NotFound {
|
||||
/// The root location of the item cannot be found.
|
||||
/// - pallet name: for calls, constants, storage
|
||||
/// - trait name: for runtime API functions
|
||||
Root,
|
||||
/// The actual item name cannot be found.
|
||||
Item,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bitvec::{order::Lsb0, vec::BitVec};
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletCallMetadata, PalletConstantMetadata, PalletErrorMetadata,
|
||||
PalletEventMetadata, PalletMetadata, PalletStorageMetadata, RuntimeMetadataV15,
|
||||
StorageEntryMetadata, StorageEntryModifier,
|
||||
};
|
||||
use frame_metadata::v15;
|
||||
use scale_info::meta_type;
|
||||
|
||||
// Define recursive types.
|
||||
@@ -623,16 +517,16 @@ mod tests {
|
||||
Remark { remark: DigestItem },
|
||||
}
|
||||
|
||||
fn build_default_extrinsic() -> ExtrinsicMetadata {
|
||||
ExtrinsicMetadata {
|
||||
fn build_default_extrinsic() -> v15::ExtrinsicMetadata {
|
||||
v15::ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn default_pallet() -> PalletMetadata {
|
||||
PalletMetadata {
|
||||
fn default_pallet() -> v15::PalletMetadata {
|
||||
v15::PalletMetadata {
|
||||
name: "Test",
|
||||
storage: None,
|
||||
calls: None,
|
||||
@@ -644,19 +538,19 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_default_pallets() -> Vec<PalletMetadata> {
|
||||
fn build_default_pallets() -> Vec<v15::PalletMetadata> {
|
||||
vec![
|
||||
PalletMetadata {
|
||||
v15::PalletMetadata {
|
||||
name: "First",
|
||||
calls: Some(PalletCallMetadata {
|
||||
calls: Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<MetadataTestType>(),
|
||||
}),
|
||||
..default_pallet()
|
||||
},
|
||||
PalletMetadata {
|
||||
v15::PalletMetadata {
|
||||
name: "Second",
|
||||
index: 1,
|
||||
calls: Some(PalletCallMetadata {
|
||||
calls: Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<(DigestItem, AccountId32, A)>(),
|
||||
}),
|
||||
..default_pallet()
|
||||
@@ -664,13 +558,15 @@ mod tests {
|
||||
]
|
||||
}
|
||||
|
||||
fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> RuntimeMetadataV15 {
|
||||
RuntimeMetadataV15::new(
|
||||
fn pallets_to_metadata(pallets: Vec<v15::PalletMetadata>) -> Metadata {
|
||||
v15::RuntimeMetadataV15::new(
|
||||
pallets,
|
||||
build_default_extrinsic(),
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
)
|
||||
.try_into()
|
||||
.expect("can build valid metadata")
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -686,8 +582,8 @@ mod tests {
|
||||
pallets_swap[1].index = 1;
|
||||
let metadata_swap = pallets_to_metadata(pallets_swap);
|
||||
|
||||
let hash = MetadataHasher::new().hash(&metadata);
|
||||
let hash_swap = MetadataHasher::new().hash(&metadata_swap);
|
||||
let hash = MetadataHasher::new(&metadata).hash();
|
||||
let hash_swap = MetadataHasher::new(&metadata_swap).hash();
|
||||
|
||||
// Changing pallet order must still result in a deterministic unique hash.
|
||||
assert_eq!(hash, hash_swap);
|
||||
@@ -696,13 +592,13 @@ mod tests {
|
||||
#[test]
|
||||
fn recursive_type() {
|
||||
let mut pallet = default_pallet();
|
||||
pallet.calls = Some(PalletCallMetadata {
|
||||
pallet.calls = Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<A>(),
|
||||
});
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
|
||||
// Check hashing algorithm finishes on a recursive type.
|
||||
MetadataHasher::new().hash(&metadata);
|
||||
MetadataHasher::new(&metadata).hash();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -713,10 +609,10 @@ mod tests {
|
||||
/// must produce the same deterministic hashing value.
|
||||
fn recursive_types_different_order() {
|
||||
let mut pallets = build_default_pallets();
|
||||
pallets[0].calls = Some(PalletCallMetadata {
|
||||
pallets[0].calls = Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<A>(),
|
||||
});
|
||||
pallets[1].calls = Some(PalletCallMetadata {
|
||||
pallets[1].calls = Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<B>(),
|
||||
});
|
||||
pallets[1].index = 1;
|
||||
@@ -728,8 +624,8 @@ mod tests {
|
||||
pallets_swap[1].index = 1;
|
||||
let metadata_swap = pallets_to_metadata(pallets_swap);
|
||||
|
||||
let hash = MetadataHasher::new().hash(&metadata);
|
||||
let hash_swap = MetadataHasher::new().hash(&metadata_swap);
|
||||
let hash = MetadataHasher::new(&metadata).hash();
|
||||
let hash_swap = MetadataHasher::new(&metadata_swap).hash();
|
||||
|
||||
// Changing pallet order must still result in a deterministic unique hash.
|
||||
assert_eq!(hash, hash_swap);
|
||||
@@ -737,12 +633,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pallet_hash_correctness() {
|
||||
let compare_pallets_hash = |lhs: &PalletMetadata, rhs: &PalletMetadata| {
|
||||
let compare_pallets_hash = |lhs: &v15::PalletMetadata, rhs: &v15::PalletMetadata| {
|
||||
let metadata = pallets_to_metadata(vec![lhs.clone()]);
|
||||
let hash = MetadataHasher::new().hash(&metadata);
|
||||
let hash = MetadataHasher::new(&metadata).hash();
|
||||
|
||||
let metadata = pallets_to_metadata(vec![rhs.clone()]);
|
||||
let new_hash = MetadataHasher::new().hash(&metadata);
|
||||
let new_hash = MetadataHasher::new(&metadata).hash();
|
||||
|
||||
assert_ne!(hash, new_hash);
|
||||
};
|
||||
@@ -750,12 +646,12 @@ mod tests {
|
||||
// Build metadata progressively from an empty pallet to a fully populated pallet.
|
||||
let mut pallet = default_pallet();
|
||||
let pallet_lhs = pallet.clone();
|
||||
pallet.storage = Some(PalletStorageMetadata {
|
||||
pallet.storage = Some(v15::PalletStorageMetadata {
|
||||
prefix: "Storage",
|
||||
entries: vec![StorageEntryMetadata {
|
||||
entries: vec![v15::StorageEntryMetadata {
|
||||
name: "BlockWeight",
|
||||
modifier: StorageEntryModifier::Default,
|
||||
ty: StorageEntryType::Plain(meta_type::<u8>()),
|
||||
modifier: v15::StorageEntryModifier::Default,
|
||||
ty: v15::StorageEntryType::Plain(meta_type::<u8>()),
|
||||
default: vec![],
|
||||
docs: vec![],
|
||||
}],
|
||||
@@ -771,20 +667,20 @@ mod tests {
|
||||
// call_name_02 { arg01: type, arg02: type }
|
||||
// }
|
||||
// ```
|
||||
pallet.calls = Some(PalletCallMetadata {
|
||||
pallet.calls = Some(v15::PalletCallMetadata {
|
||||
ty: meta_type::<Call>(),
|
||||
});
|
||||
compare_pallets_hash(&pallet_lhs, &pallet);
|
||||
|
||||
let pallet_lhs = pallet.clone();
|
||||
// Events are similar to Calls.
|
||||
pallet.event = Some(PalletEventMetadata {
|
||||
pallet.event = Some(v15::PalletEventMetadata {
|
||||
ty: meta_type::<Call>(),
|
||||
});
|
||||
compare_pallets_hash(&pallet_lhs, &pallet);
|
||||
|
||||
let pallet_lhs = pallet.clone();
|
||||
pallet.constants = vec![PalletConstantMetadata {
|
||||
pallet.constants = vec![v15::PalletConstantMetadata {
|
||||
name: "BlockHashCount",
|
||||
ty: meta_type::<u64>(),
|
||||
value: vec![96u8, 0, 0, 0],
|
||||
@@ -793,7 +689,7 @@ mod tests {
|
||||
compare_pallets_hash(&pallet_lhs, &pallet);
|
||||
|
||||
let pallet_lhs = pallet.clone();
|
||||
pallet.error = Some(PalletErrorMetadata {
|
||||
pallet.error = Some(v15::PalletErrorMetadata {
|
||||
ty: meta_type::<MetadataTestType>(),
|
||||
});
|
||||
compare_pallets_hash(&pallet_lhs, &pallet);
|
||||
@@ -809,27 +705,27 @@ mod tests {
|
||||
let metadata_both = pallets_to_metadata(pallets);
|
||||
|
||||
// Hashing will ignore any non-existant pallet and return the same result.
|
||||
let hash = MetadataHasher::new()
|
||||
let hash = MetadataHasher::new(&metadata_one)
|
||||
.only_these_pallets(&["First", "Second"])
|
||||
.hash(&metadata_one);
|
||||
let hash_rhs = MetadataHasher::new()
|
||||
.hash();
|
||||
let hash_rhs = MetadataHasher::new(&metadata_one)
|
||||
.only_these_pallets(&["First"])
|
||||
.hash(&metadata_one);
|
||||
.hash();
|
||||
assert_eq!(hash, hash_rhs, "hashing should ignore non-existant pallets");
|
||||
|
||||
// Hashing one pallet from metadata with 2 pallets inserted will ignore the second pallet.
|
||||
let hash_second = MetadataHasher::new()
|
||||
let hash_second = MetadataHasher::new(&metadata_both)
|
||||
.only_these_pallets(&["First"])
|
||||
.hash(&metadata_both);
|
||||
.hash();
|
||||
assert_eq!(
|
||||
hash_second, hash,
|
||||
"hashing one pallet should ignore the others"
|
||||
);
|
||||
|
||||
// Check hashing with all pallets.
|
||||
let hash_second = MetadataHasher::new()
|
||||
let hash_second = MetadataHasher::new(&metadata_both)
|
||||
.only_these_pallets(&["First", "Second"])
|
||||
.hash(&metadata_both);
|
||||
.hash();
|
||||
assert_ne!(
|
||||
hash_second, hash,
|
||||
"hashing both pallets should produce a different result from hashing just one pallet"
|
||||
@@ -841,12 +737,12 @@ mod tests {
|
||||
// Get a hash representation of the provided meta type,
|
||||
// inserted in the context of pallet metadata call.
|
||||
let to_hash = |meta_ty| {
|
||||
let pallet = PalletMetadata {
|
||||
calls: Some(PalletCallMetadata { ty: meta_ty }),
|
||||
let pallet = v15::PalletMetadata {
|
||||
calls: Some(v15::PalletCallMetadata { ty: meta_ty }),
|
||||
..default_pallet()
|
||||
};
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
MetadataHasher::new().hash(&metadata)
|
||||
MetadataHasher::new(&metadata).hash()
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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::{form::PortableForm, PortableRegistry, TypeDef, Variant};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Given some type ID and type registry, build a couple of
|
||||
/// indexes to look up variants by index or name. If the ID provided
|
||||
/// is not a variant, the index will be empty.
|
||||
///
|
||||
/// API optimized for dealing with the `Option<u32>` variant type IDs
|
||||
/// that we get in metadata pallets.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VariantIndex {
|
||||
by_name: HashMap<String, usize>,
|
||||
by_index: HashMap<u8, usize>,
|
||||
}
|
||||
|
||||
impl VariantIndex {
|
||||
/// Build indexes from the optional variant ID.
|
||||
pub fn build(variant_id: Option<u32>, types: &PortableRegistry) -> Self {
|
||||
let Some(variants) = Self::get(variant_id, types) else {
|
||||
return Self::empty()
|
||||
};
|
||||
|
||||
let mut by_name = HashMap::new();
|
||||
let mut by_index = HashMap::new();
|
||||
for (pos, variant) in variants.iter().enumerate() {
|
||||
by_name.insert(variant.name.to_owned(), pos);
|
||||
by_index.insert(variant.index, pos);
|
||||
}
|
||||
|
||||
Self { by_name, by_index }
|
||||
}
|
||||
|
||||
/// Build an empty index.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
by_name: Default::default(),
|
||||
by_index: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the variants we're pointing at; None if this isn't possible.
|
||||
pub fn get(
|
||||
variant_id: Option<u32>,
|
||||
types: &PortableRegistry,
|
||||
) -> Option<&[Variant<PortableForm>]> {
|
||||
let Some(variant_id) = variant_id else {
|
||||
return None
|
||||
};
|
||||
let TypeDef::Variant(v) = &types.resolve(variant_id)?.type_def else {
|
||||
return None
|
||||
};
|
||||
Some(&v.variants)
|
||||
}
|
||||
|
||||
/// Lookup a variant by name; `None` if the type is not a variant or name isn't found.
|
||||
pub fn lookup_by_name<'a, K>(
|
||||
&self,
|
||||
name: &K,
|
||||
variant_id: Option<u32>,
|
||||
types: &'a PortableRegistry,
|
||||
) -> Option<&'a Variant<PortableForm>>
|
||||
where
|
||||
String: std::borrow::Borrow<K>,
|
||||
K: std::hash::Hash + Eq + ?Sized,
|
||||
{
|
||||
let pos = *self.by_name.get(name)?;
|
||||
let variants = Self::get(variant_id, types)?;
|
||||
variants.get(pos)
|
||||
}
|
||||
|
||||
/// Lookup a variant by index; `None` if the type is not a variant or index isn't found.
|
||||
pub fn lookup_by_index<'a>(
|
||||
&self,
|
||||
index: u8,
|
||||
variant_id: Option<u32>,
|
||||
types: &'a PortableRegistry,
|
||||
) -> Option<&'a Variant<PortableForm>> {
|
||||
let pos = *self.by_index.get(&index)?;
|
||||
let variants = Self::get(variant_id, types)?;
|
||||
variants.get(pos)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,6 @@ serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, features = ["raw_value"] }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
frame-metadata = { workspace = true }
|
||||
derivative = { workspace = true }
|
||||
either = { workspace = true }
|
||||
|
||||
@@ -70,7 +70,7 @@ where
|
||||
|
||||
/// Fetch and return the block body.
|
||||
pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
|
||||
let ids = ExtrinsicPartTypeIds::new(self.client.metadata().runtime_metadata())?;
|
||||
let ids = ExtrinsicPartTypeIds::new(&self.client.metadata())?;
|
||||
let block_hash = self.header.hash();
|
||||
let Some(block_details) = self.client.rpc().block(Some(block_hash)).await? else {
|
||||
return Err(BlockError::not_found(block_hash).into());
|
||||
|
||||
@@ -6,16 +6,15 @@ use crate::{
|
||||
blocks::block_types::{get_events, CachedEvents},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, Hasher},
|
||||
error::{BlockError, Error},
|
||||
error::{BlockError, Error, MetadataError},
|
||||
events,
|
||||
metadata::ExtrinsicMetadata,
|
||||
metadata::types::PalletMetadata,
|
||||
rpc::types::ChainBlockExtrinsic,
|
||||
Metadata,
|
||||
};
|
||||
|
||||
use codec::Decode;
|
||||
use derivative::Derivative;
|
||||
use frame_metadata::v15::RuntimeMetadataV15;
|
||||
use scale_decode::DecodeAsFields;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
@@ -242,7 +241,7 @@ where
|
||||
scale_decode::visitor::decode_with_visitor(
|
||||
cursor,
|
||||
ids.address,
|
||||
&metadata.runtime_metadata().types,
|
||||
metadata.types(),
|
||||
scale_decode::visitor::IgnoreVisitor,
|
||||
)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
@@ -251,7 +250,7 @@ where
|
||||
scale_decode::visitor::decode_with_visitor(
|
||||
cursor,
|
||||
ids.signature,
|
||||
&metadata.runtime_metadata().types,
|
||||
metadata.types(),
|
||||
scale_decode::visitor::IgnoreVisitor,
|
||||
)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
@@ -259,7 +258,7 @@ where
|
||||
scale_decode::visitor::decode_with_visitor(
|
||||
cursor,
|
||||
ids.extra,
|
||||
&metadata.runtime_metadata().types,
|
||||
metadata.types(),
|
||||
scale_decode::visitor::IgnoreVisitor,
|
||||
)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
@@ -357,19 +356,25 @@ where
|
||||
|
||||
/// The name of the pallet from whence the extrinsic originated.
|
||||
pub fn pallet_name(&self) -> Result<&str, Error> {
|
||||
Ok(self.extrinsic_metadata()?.pallet())
|
||||
Ok(self.extrinsic_metadata()?.pallet.name())
|
||||
}
|
||||
|
||||
/// The name of the call (ie the name of the variant that it corresponds to).
|
||||
pub fn variant_name(&self) -> Result<&str, Error> {
|
||||
Ok(self.extrinsic_metadata()?.call())
|
||||
Ok(&self.extrinsic_metadata()?.variant.name)
|
||||
}
|
||||
|
||||
/// Fetch the metadata for this extrinsic.
|
||||
pub fn extrinsic_metadata(&self) -> Result<&ExtrinsicMetadata, Error> {
|
||||
Ok(self
|
||||
pub fn extrinsic_metadata(&self) -> Result<ExtrinsicMetadataDetails, Error> {
|
||||
let pallet = self
|
||||
.metadata
|
||||
.extrinsic(self.pallet_index(), self.variant_index())?)
|
||||
.pallet_by_index(self.pallet_index())
|
||||
.ok_or_else(|| MetadataError::PalletIndexNotFound(self.pallet_index()))?;
|
||||
let variant = pallet
|
||||
.call_variant_by_index(self.variant_index())
|
||||
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.variant_index()))?;
|
||||
|
||||
Ok(ExtrinsicMetadataDetails { pallet, variant })
|
||||
}
|
||||
|
||||
/// Decode and provide the extrinsic fields back in the form of a [`scale_value::Composite`]
|
||||
@@ -382,8 +387,8 @@ where
|
||||
|
||||
let decoded = <scale_value::Composite<scale_value::scale::TypeId>>::decode_as_fields(
|
||||
bytes,
|
||||
extrinsic_metadata.fields(),
|
||||
&self.metadata.runtime_metadata().types,
|
||||
&extrinsic_metadata.variant.fields,
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
|
||||
Ok(decoded)
|
||||
@@ -393,10 +398,12 @@ where
|
||||
/// Such types are exposed in the codegen as `pallet_name::calls::types::CallName` types.
|
||||
pub fn as_extrinsic<E: StaticExtrinsic>(&self) -> Result<Option<E>, Error> {
|
||||
let extrinsic_metadata = self.extrinsic_metadata()?;
|
||||
if extrinsic_metadata.pallet() == E::PALLET && extrinsic_metadata.call() == E::CALL {
|
||||
if extrinsic_metadata.pallet.name() == E::PALLET
|
||||
&& extrinsic_metadata.variant.name == E::CALL
|
||||
{
|
||||
let decoded = E::decode_as_fields(
|
||||
&mut self.field_bytes(),
|
||||
extrinsic_metadata.fields(),
|
||||
&extrinsic_metadata.variant.fields,
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
Ok(Some(decoded))
|
||||
@@ -409,18 +416,15 @@ where
|
||||
/// the pallet and extrinsic enum variants as well as the extrinsic fields). A compatible
|
||||
/// type for this is exposed via static codegen as a root level `Call` type.
|
||||
pub fn as_root_extrinsic<E: RootExtrinsic>(&self) -> Result<E, Error> {
|
||||
let pallet = self.metadata.pallet(self.pallet_name()?)?;
|
||||
let pallet_extrinsic_ty = pallet.call_ty_id().ok_or_else(|| {
|
||||
Error::Metadata(crate::metadata::MetadataError::ExtrinsicNotFound(
|
||||
pallet.index(),
|
||||
self.variant_index(),
|
||||
))
|
||||
let md = self.extrinsic_metadata()?;
|
||||
let pallet_extrinsic_ty = md.pallet.call_ty_id().ok_or_else(|| {
|
||||
Error::Metadata(MetadataError::CallTypeNotFoundInPallet(md.pallet.index()))
|
||||
})?;
|
||||
|
||||
// Ignore root enum index.
|
||||
E::root_extrinsic(
|
||||
&self.call_bytes()[1..],
|
||||
self.pallet_name()?,
|
||||
md.pallet.name(),
|
||||
pallet_extrinsic_ty,
|
||||
&self.metadata,
|
||||
)
|
||||
@@ -440,6 +444,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Details for the given extrinsic plucked from the metadata.
|
||||
pub struct ExtrinsicMetadataDetails<'a> {
|
||||
pub pallet: PalletMetadata<'a>,
|
||||
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
||||
}
|
||||
|
||||
/// The type IDs extracted from the metadata that represent the
|
||||
/// generic type parameters passed to the `UncheckedExtrinsic` from
|
||||
/// the substrate-based chain.
|
||||
@@ -459,15 +469,15 @@ pub(crate) struct ExtrinsicPartTypeIds {
|
||||
|
||||
impl ExtrinsicPartTypeIds {
|
||||
/// Extract the generic type parameters IDs from the extrinsic type.
|
||||
pub(crate) fn new(metadata: &RuntimeMetadataV15) -> Result<Self, BlockError> {
|
||||
pub(crate) fn new(metadata: &Metadata) -> Result<Self, BlockError> {
|
||||
const ADDRESS: &str = "Address";
|
||||
const CALL: &str = "Call";
|
||||
const SIGNATURE: &str = "Signature";
|
||||
const EXTRA: &str = "Extra";
|
||||
|
||||
let id = metadata.extrinsic.ty.id;
|
||||
let id = metadata.extrinsic().ty();
|
||||
|
||||
let Some(ty) = metadata.types.resolve(id) else {
|
||||
let Some(ty) = metadata.types().resolve(id) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
|
||||
@@ -732,7 +742,7 @@ mod tests {
|
||||
let meta = RuntimeMetadataV15::new(pallets, extrinsic, meta_type::<()>(), vec![]);
|
||||
let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
|
||||
|
||||
Metadata::try_from(runtime_metadata).unwrap()
|
||||
Metadata::new(runtime_metadata.try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Build an offline client to work with the test metadata.
|
||||
@@ -752,18 +762,20 @@ mod tests {
|
||||
let metadata = metadata();
|
||||
|
||||
// Except our metadata to contain the registered types.
|
||||
let extrinsic = metadata
|
||||
.extrinsic(0, 2)
|
||||
let pallet = metadata.pallet_by_index(0).expect("pallet exists");
|
||||
let extrinsic = pallet
|
||||
.call_variant_by_index(2)
|
||||
.expect("metadata contains the RuntimeCall enum with this pallet");
|
||||
assert_eq!(extrinsic.pallet(), "Test");
|
||||
assert_eq!(extrinsic.call(), "TestCall");
|
||||
|
||||
assert_eq!(pallet.name(), "Test");
|
||||
assert_eq!(&extrinsic.name, "TestCall");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insufficient_extrinsic_bytes() {
|
||||
let metadata = metadata();
|
||||
let client = client(metadata.clone());
|
||||
let ids = ExtrinsicPartTypeIds::new(metadata.runtime_metadata()).unwrap();
|
||||
let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap();
|
||||
|
||||
// Decode with empty bytes.
|
||||
let result = ExtrinsicDetails::decode_from(
|
||||
@@ -781,7 +793,7 @@ mod tests {
|
||||
fn unsupported_version_extrinsic() {
|
||||
let metadata = metadata();
|
||||
let client = client(metadata.clone());
|
||||
let ids = ExtrinsicPartTypeIds::new(metadata.runtime_metadata()).unwrap();
|
||||
let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap();
|
||||
|
||||
// Decode with invalid version.
|
||||
let result = ExtrinsicDetails::decode_from(
|
||||
@@ -805,7 +817,7 @@ mod tests {
|
||||
fn statically_decode_extrinsic() {
|
||||
let metadata = metadata();
|
||||
let client = client(metadata.clone());
|
||||
let ids = ExtrinsicPartTypeIds::new(metadata.runtime_metadata()).unwrap();
|
||||
let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap();
|
||||
|
||||
let tx = crate::tx::dynamic(
|
||||
"Test",
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let runtime_call = subxt::dynamic::runtime_api_call(
|
||||
//! "Metadata_metadata_versions",
|
||||
//! "Metadata",
|
||||
//! "metadata_versions",
|
||||
//! Vec::<Value<()>>::new()
|
||||
//! );
|
||||
//! ```
|
||||
|
||||
@@ -73,13 +73,13 @@ impl<T: Config> OfflineClient<T> {
|
||||
pub fn new(
|
||||
genesis_hash: T::Hash,
|
||||
runtime_version: RuntimeVersion,
|
||||
metadata: Metadata,
|
||||
metadata: impl Into<Metadata>,
|
||||
) -> OfflineClient<T> {
|
||||
OfflineClient {
|
||||
inner: Arc::new(Inner {
|
||||
genesis_hash,
|
||||
runtime_version,
|
||||
metadata,
|
||||
metadata: metadata.into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ use crate::{
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use futures::future;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// A trait representing a client that can perform
|
||||
/// online actions.
|
||||
@@ -119,14 +118,14 @@ impl<T: Config> OnlineClient<T> {
|
||||
pub fn from_rpc_client_with<R: RpcClientT>(
|
||||
genesis_hash: T::Hash,
|
||||
runtime_version: RuntimeVersion,
|
||||
metadata: Metadata,
|
||||
metadata: impl Into<Metadata>,
|
||||
rpc_client: Arc<R>,
|
||||
) -> Result<OnlineClient<T>, Error> {
|
||||
Ok(OnlineClient {
|
||||
inner: Arc::new(RwLock::new(Inner {
|
||||
genesis_hash,
|
||||
runtime_version,
|
||||
metadata,
|
||||
metadata: metadata.into(),
|
||||
})),
|
||||
rpc: Rpc::new(rpc_client),
|
||||
})
|
||||
@@ -196,7 +195,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
|
||||
/// Return the [`Metadata`] used in this client.
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
let inner = self.inner.read();
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
inner.metadata.clone()
|
||||
}
|
||||
|
||||
@@ -206,14 +205,14 @@ impl<T: Config> OnlineClient<T> {
|
||||
///
|
||||
/// Setting custom metadata may leave Subxt unable to work with certain blocks,
|
||||
/// subscribe to latest blocks or submit valid transactions.
|
||||
pub fn set_metadata(&self, metadata: Metadata) {
|
||||
let mut inner = self.inner.write();
|
||||
inner.metadata = metadata;
|
||||
pub fn set_metadata(&self, metadata: impl Into<Metadata>) {
|
||||
let mut inner = self.inner.write().expect("shouldn't be poisoned");
|
||||
inner.metadata = metadata.into();
|
||||
}
|
||||
|
||||
/// Return the genesis hash.
|
||||
pub fn genesis_hash(&self) -> T::Hash {
|
||||
let inner = self.inner.read();
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
inner.genesis_hash
|
||||
}
|
||||
|
||||
@@ -224,13 +223,13 @@ impl<T: Config> OnlineClient<T> {
|
||||
/// Setting a custom genesis hash may leave Subxt unable to
|
||||
/// submit valid transactions.
|
||||
pub fn set_genesis_hash(&self, genesis_hash: T::Hash) {
|
||||
let mut inner = self.inner.write();
|
||||
let mut inner = self.inner.write().expect("shouldn't be poisoned");
|
||||
inner.genesis_hash = genesis_hash;
|
||||
}
|
||||
|
||||
/// Return the runtime version.
|
||||
pub fn runtime_version(&self) -> RuntimeVersion {
|
||||
let inner = self.inner.read();
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
inner.runtime_version.clone()
|
||||
}
|
||||
|
||||
@@ -241,7 +240,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
/// Setting a custom runtime version may leave Subxt unable to
|
||||
/// submit valid transactions.
|
||||
pub fn set_runtime_version(&self, runtime_version: RuntimeVersion) {
|
||||
let mut inner = self.inner.write();
|
||||
let mut inner = self.inner.write().expect("shouldn't be poisoned");
|
||||
inner.runtime_version = runtime_version;
|
||||
}
|
||||
|
||||
@@ -252,7 +251,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
|
||||
/// Return an offline client with the same configuration as this.
|
||||
pub fn offline(&self) -> OfflineClient<T> {
|
||||
let inner = self.inner.read();
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
OfflineClient::new(
|
||||
inner.genesis_hash,
|
||||
inner.runtime_version.clone(),
|
||||
@@ -318,12 +317,12 @@ pub struct ClientRuntimeUpdater<T: Config>(OnlineClient<T>);
|
||||
|
||||
impl<T: Config> ClientRuntimeUpdater<T> {
|
||||
fn is_runtime_version_different(&self, new: &RuntimeVersion) -> bool {
|
||||
let curr = self.0.inner.read();
|
||||
let curr = self.0.inner.read().expect("shouldn't be poisoned");
|
||||
&curr.runtime_version != new
|
||||
}
|
||||
|
||||
fn do_update(&self, update: Update) {
|
||||
let mut writable = self.0.inner.write();
|
||||
let mut writable = self.0.inner.write().expect("shouldn't be poisoned");
|
||||
writable.metadata = update.metadata;
|
||||
writable.runtime_version = update.runtime_version;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
use super::ConstantAddress;
|
||||
use crate::{
|
||||
client::OfflineClientT,
|
||||
error::Error,
|
||||
metadata::{DecodeWithMetadata, MetadataError},
|
||||
error::{Error, MetadataError},
|
||||
metadata::DecodeWithMetadata,
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
@@ -39,13 +39,14 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
|
||||
let expected_hash = self
|
||||
.client
|
||||
.metadata()
|
||||
.constant_hash(address.pallet_name(), address.constant_name())?;
|
||||
.pallet_by_name(address.pallet_name())
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?
|
||||
.constant_hash(address.constant_name())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
|
||||
})?;
|
||||
if actual_hash != expected_hash {
|
||||
return Err(MetadataError::IncompatibleConstantMetadata(
|
||||
address.pallet_name().into(),
|
||||
address.constant_name().into(),
|
||||
)
|
||||
.into());
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -64,11 +65,17 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
|
||||
self.validate(address)?;
|
||||
|
||||
// 2. Attempt to decode the constant into the type given:
|
||||
let pallet = metadata.pallet(address.pallet_name())?;
|
||||
let constant = pallet.constant(address.constant_name())?;
|
||||
let pallet = metadata
|
||||
.pallet_by_name(address.pallet_name())
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?;
|
||||
let constant = pallet
|
||||
.constant_by_name(address.constant_name())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
|
||||
})?;
|
||||
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
|
||||
&mut &*constant.value,
|
||||
constant.ty.id,
|
||||
&mut constant.value(),
|
||||
constant.ty(),
|
||||
&metadata,
|
||||
)?;
|
||||
Ok(value)
|
||||
|
||||
@@ -10,7 +10,7 @@ use core::fmt::Debug;
|
||||
use scale_decode::visitor::DecodeAsTypeResult;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::Error;
|
||||
use super::{Error, MetadataError};
|
||||
use crate::error::RootError;
|
||||
|
||||
/// An error dispatching a transaction.
|
||||
@@ -145,20 +145,27 @@ impl std::fmt::Display for ModuleError {
|
||||
return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)");
|
||||
};
|
||||
|
||||
let pallet = details.pallet();
|
||||
let error = details.error();
|
||||
let pallet = details.pallet.name();
|
||||
let error = &details.variant.name;
|
||||
write!(f, "Pallet error {pallet}::{error}")
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleError {
|
||||
/// Return more details about this error.
|
||||
pub fn details(&self) -> Result<&crate::metadata::ErrorMetadata, super::Error> {
|
||||
let error_details = self
|
||||
pub fn details(&self) -> Result<ModuleErrorDetails, MetadataError> {
|
||||
let pallet = self
|
||||
.metadata
|
||||
.error(self.raw.pallet_index, self.raw.error[0])?;
|
||||
Ok(error_details)
|
||||
.pallet_by_index(self.raw.pallet_index)
|
||||
.ok_or(MetadataError::PalletIndexNotFound(self.raw.pallet_index))?;
|
||||
|
||||
let variant = pallet
|
||||
.error_variant_by_index(self.raw.error[0])
|
||||
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.raw.error[0]))?;
|
||||
|
||||
Ok(ModuleErrorDetails { pallet, variant })
|
||||
}
|
||||
|
||||
/// Return the underlying module error data that was decoded.
|
||||
pub fn raw(&self) -> RawModuleError {
|
||||
self.raw
|
||||
@@ -167,10 +174,22 @@ impl ModuleError {
|
||||
/// Attempts to decode the ModuleError into a value implementing the trait `RootError`
|
||||
/// where the actual type of value is the generated top level enum `Error`.
|
||||
pub fn as_root_error<E: RootError>(&self) -> Result<E, Error> {
|
||||
E::root_error(&self.raw.error, self.details()?.pallet(), &self.metadata)
|
||||
E::root_error(
|
||||
&self.raw.error,
|
||||
self.details()?.pallet.name(),
|
||||
&self.metadata,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Details about the module error.
|
||||
pub struct ModuleErrorDetails<'a> {
|
||||
/// The pallet that the error came from
|
||||
pub pallet: crate::metadata::types::PalletMetadata<'a>,
|
||||
/// The variant representing the error
|
||||
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
||||
}
|
||||
|
||||
/// The error details about a module error that has occurred.
|
||||
///
|
||||
/// **Note**: Structure used to obtain the underlying bytes of a ModuleError.
|
||||
@@ -198,16 +217,9 @@ impl DispatchError {
|
||||
metadata: Metadata,
|
||||
) -> Result<Self, super::Error> {
|
||||
let bytes = bytes.into();
|
||||
|
||||
let dispatch_error_ty_id = match metadata.dispatch_error_ty() {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
tracing::warn!(
|
||||
"Can't decode error: sp_runtime::DispatchError was not found in Metadata"
|
||||
);
|
||||
return Err(super::Error::Unknown(bytes.into_owned()));
|
||||
}
|
||||
};
|
||||
let dispatch_error_ty_id = metadata
|
||||
.dispatch_error_ty()
|
||||
.ok_or(MetadataError::DispatchErrorNotFound)?;
|
||||
|
||||
// The aim is to decode our bytes into roughly this shape. This is copied from
|
||||
// `sp_runtime::DispatchError`; we need the variant names and any inner variant
|
||||
|
||||
+54
-8
@@ -17,9 +17,10 @@ pub use dispatch_error::{
|
||||
};
|
||||
|
||||
// Re-expose the errors we use from other crates here:
|
||||
pub use crate::metadata::{InvalidMetadataError, Metadata, MetadataError};
|
||||
pub use crate::metadata::Metadata;
|
||||
pub use scale_decode::Error as DecodeError;
|
||||
pub use scale_encode::Error as EncodeError;
|
||||
pub use subxt_metadata::TryFromError as MetadataTryFromError;
|
||||
|
||||
/// The underlying error enum, generic over the type held by the `Runtime`
|
||||
/// variant. Prefer to use the [`Error<E>`] and [`Error`] aliases over
|
||||
@@ -39,12 +40,12 @@ pub enum Error {
|
||||
/// Serde serialization error
|
||||
#[error("Serde json error: {0}")]
|
||||
Serialization(#[from] serde_json::error::Error),
|
||||
/// Invalid metadata error
|
||||
#[error("Invalid Metadata: {0}")]
|
||||
InvalidMetadata(#[from] InvalidMetadataError),
|
||||
/// Invalid metadata error
|
||||
/// Error working with metadata.
|
||||
#[error("Metadata: {0}")]
|
||||
Metadata(#[from] MetadataError),
|
||||
/// Error decoding metadata.
|
||||
#[error("Metadata: {0}")]
|
||||
MetadataDecoding(#[from] MetadataTryFromError),
|
||||
/// Runtime error.
|
||||
#[error("Runtime error: {0:?}")]
|
||||
Runtime(#[from] DispatchError),
|
||||
@@ -167,9 +168,6 @@ pub enum StorageAddressError {
|
||||
/// The number of keys provided in the storage address.
|
||||
expected: usize,
|
||||
},
|
||||
/// Storage lookup requires a type that wasn't found in the metadata.
|
||||
#[error("Storage lookup requires type {0} to exist in the metadata, but it was not found")]
|
||||
TypeNotFound(u32),
|
||||
/// This storage entry in the metadata does not have the correct number of hashers to fields.
|
||||
#[error("Storage entry in metadata does not have the correct number of hashers to fields")]
|
||||
WrongNumberOfHashers {
|
||||
@@ -180,6 +178,54 @@ pub enum StorageAddressError {
|
||||
},
|
||||
}
|
||||
|
||||
/// Something went wrong trying to access details in the metadata.
|
||||
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum MetadataError {
|
||||
/// The DispatchError type isn't available in the metadata
|
||||
#[error("The DispatchError type isn't available")]
|
||||
DispatchErrorNotFound,
|
||||
/// Type not found in metadata.
|
||||
#[error("Type with ID {0} not found")]
|
||||
TypeNotFound(u32),
|
||||
/// Pallet not found (index).
|
||||
#[error("Pallet with index {0} not found")]
|
||||
PalletIndexNotFound(u8),
|
||||
/// Pallet not found (name).
|
||||
#[error("Pallet with name {0} not found")]
|
||||
PalletNameNotFound(String),
|
||||
/// Variant not found.
|
||||
#[error("Variant with index {0} not found")]
|
||||
VariantIndexNotFound(u8),
|
||||
/// Constant not found.
|
||||
#[error("Constant with name {0} not found")]
|
||||
ConstantNameNotFound(String),
|
||||
/// Call not found.
|
||||
#[error("Call with name {0} not found")]
|
||||
CallNameNotFound(String),
|
||||
/// Runtime trait not found.
|
||||
#[error("Runtime trait with name {0} not found")]
|
||||
RuntimeTraitNotFound(String),
|
||||
/// Runtime method not found.
|
||||
#[error("Runtime method with name {0} not found")]
|
||||
RuntimeMethodNotFound(String),
|
||||
/// Call type not found in metadata.
|
||||
#[error("Call type not found in pallet with index {0}")]
|
||||
CallTypeNotFoundInPallet(u8),
|
||||
/// Event type not found in metadata.
|
||||
#[error("Event type not found in pallet with index {0}")]
|
||||
EventTypeNotFoundInPallet(u8),
|
||||
/// Storage details not found in metadata.
|
||||
#[error("Storage details not found in pallet with name {0}")]
|
||||
StorageNotFoundInPallet(String),
|
||||
/// Storage entry not found.
|
||||
#[error("Storage entry {0} not found")]
|
||||
StorageEntryNotFound(String),
|
||||
/// The generated interface used is not compatible with the node.
|
||||
#[error("The generated code is not compatible with the node")]
|
||||
IncompatibleCodegen,
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root ModuleError type
|
||||
#[doc(hidden)]
|
||||
pub trait RootError: Sized {
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
|
||||
use super::{Phase, StaticEvent};
|
||||
use crate::{
|
||||
client::OnlineClientT, error::Error, events::events_client::get_event_bytes,
|
||||
metadata::EventMetadata, Config, Metadata,
|
||||
client::OnlineClientT,
|
||||
error::{Error, MetadataError},
|
||||
events::events_client::get_event_bytes,
|
||||
metadata::types::PalletMetadata,
|
||||
Config, Metadata,
|
||||
};
|
||||
use codec::{Compact, Decode};
|
||||
use derivative::Derivative;
|
||||
@@ -224,20 +227,25 @@ impl EventDetails {
|
||||
let event_fields_start_idx = all_bytes.len() - input.len();
|
||||
|
||||
// Get metadata for the event:
|
||||
let event_metadata = metadata.event(pallet_index, variant_index)?;
|
||||
let event_pallet = metadata
|
||||
.pallet_by_index(pallet_index)
|
||||
.ok_or(MetadataError::PalletIndexNotFound(pallet_index))?;
|
||||
let event_variant = event_pallet
|
||||
.event_variant_by_index(variant_index)
|
||||
.ok_or(MetadataError::VariantIndexNotFound(variant_index))?;
|
||||
tracing::debug!(
|
||||
"Decoding Event '{}::{}'",
|
||||
event_metadata.pallet(),
|
||||
event_metadata.event()
|
||||
event_pallet.name(),
|
||||
&event_variant.name
|
||||
);
|
||||
|
||||
// Skip over the bytes belonging to this event.
|
||||
for field_metadata in event_metadata.fields() {
|
||||
for field_metadata in &event_variant.fields {
|
||||
// Skip over the bytes for this field:
|
||||
scale_decode::visitor::decode_with_visitor(
|
||||
input,
|
||||
field_metadata.ty.id,
|
||||
&metadata.runtime_metadata().types,
|
||||
metadata.types(),
|
||||
scale_decode::visitor::IgnoreVisitor,
|
||||
)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
@@ -292,19 +300,25 @@ impl EventDetails {
|
||||
|
||||
/// The name of the pallet from whence the Event originated.
|
||||
pub fn pallet_name(&self) -> &str {
|
||||
self.event_metadata().pallet()
|
||||
self.event_metadata().pallet.name()
|
||||
}
|
||||
|
||||
/// The name of the event (ie the name of the variant that it corresponds to).
|
||||
pub fn variant_name(&self) -> &str {
|
||||
self.event_metadata().event()
|
||||
&self.event_metadata().variant.name
|
||||
}
|
||||
|
||||
/// Fetch the metadata for this event.
|
||||
pub fn event_metadata(&self) -> &EventMetadata {
|
||||
self.metadata
|
||||
.event(self.pallet_index(), self.variant_index())
|
||||
.expect("this must exist in order to have produced the EventDetails")
|
||||
/// Fetch details from the metadata for this event.
|
||||
pub fn event_metadata(&self) -> EventMetadataDetails {
|
||||
let pallet = self
|
||||
.metadata
|
||||
.pallet_by_index(self.pallet_index())
|
||||
.expect("event pallet to be found; we did this already during decoding");
|
||||
let variant = pallet
|
||||
.event_variant_by_index(self.variant_index())
|
||||
.expect("event variant to be found; we did this already during decoding");
|
||||
|
||||
EventMetadataDetails { pallet, variant }
|
||||
}
|
||||
|
||||
/// Return _all_ of the bytes representing this event, which include, in order:
|
||||
@@ -332,8 +346,8 @@ impl EventDetails {
|
||||
use scale_decode::DecodeAsFields;
|
||||
let decoded = <scale_value::Composite<scale_value::scale::TypeId>>::decode_as_fields(
|
||||
bytes,
|
||||
event_metadata.fields(),
|
||||
&self.metadata.runtime_metadata().types,
|
||||
&event_metadata.variant.fields,
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
|
||||
Ok(decoded)
|
||||
@@ -343,10 +357,10 @@ impl EventDetails {
|
||||
/// Such types are exposed in the codegen as `pallet_name::events::EventName` types.
|
||||
pub fn as_event<E: StaticEvent>(&self) -> Result<Option<E>, Error> {
|
||||
let ev_metadata = self.event_metadata();
|
||||
if ev_metadata.pallet() == E::PALLET && ev_metadata.event() == E::EVENT {
|
||||
if ev_metadata.pallet.name() == E::PALLET && ev_metadata.variant.name == E::EVENT {
|
||||
let decoded = E::decode_as_fields(
|
||||
&mut self.field_bytes(),
|
||||
ev_metadata.fields(),
|
||||
&ev_metadata.variant.fields,
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
Ok(Some(decoded))
|
||||
@@ -359,14 +373,12 @@ impl EventDetails {
|
||||
/// the pallet and event enum variants as well as the event fields). A compatible
|
||||
/// type for this is exposed via static codegen as a root level `Event` type.
|
||||
pub fn as_root_event<E: RootEvent>(&self) -> Result<E, Error> {
|
||||
let ev_metadata = self.event_metadata();
|
||||
let pallet_bytes = &self.all_bytes[self.event_start_idx + 1..self.event_fields_end_idx];
|
||||
let pallet = self.metadata.pallet(self.pallet_name())?;
|
||||
let pallet_event_ty = pallet.event_ty_id().ok_or_else(|| {
|
||||
Error::Metadata(crate::metadata::MetadataError::EventNotFound(
|
||||
pallet.index(),
|
||||
self.variant_index(),
|
||||
))
|
||||
})?;
|
||||
let pallet_event_ty = ev_metadata
|
||||
.pallet
|
||||
.event_ty_id()
|
||||
.ok_or_else(|| MetadataError::EventTypeNotFoundInPallet(ev_metadata.pallet.index()))?;
|
||||
|
||||
E::root_event(
|
||||
pallet_bytes,
|
||||
@@ -377,6 +389,12 @@ impl EventDetails {
|
||||
}
|
||||
}
|
||||
|
||||
/// Details for the given event plucked from the metadata.
|
||||
pub struct EventMetadataDetails<'a> {
|
||||
pub pallet: PalletMetadata<'a>,
|
||||
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root event type, so that we're able
|
||||
/// to decode it properly via a pallet event that impls `DecodeAsMetadata`. This is necessary
|
||||
/// becasue the "root event" type is generated using pallet info but doesn't actually exist in the
|
||||
@@ -406,7 +424,6 @@ pub(crate) mod test_utils {
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use scale_info::{meta_type, TypeInfo};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// An "outer" events enum containing exactly one event.
|
||||
#[derive(
|
||||
@@ -511,7 +528,7 @@ pub(crate) mod test_utils {
|
||||
let meta = RuntimeMetadataV15::new(pallets, extrinsic, meta_type::<()>(), vec![]);
|
||||
let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
|
||||
|
||||
Metadata::try_from(runtime_metadata).unwrap()
|
||||
Metadata::new(runtime_metadata.try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Build an `Events` object for test purposes, based on the details provided,
|
||||
@@ -584,7 +601,7 @@ mod tests {
|
||||
actual: EventDetails,
|
||||
expected: TestRawEventDetails,
|
||||
) {
|
||||
let types = &metadata.runtime_metadata().types;
|
||||
let types = &metadata.types();
|
||||
|
||||
// Make sure that the bytes handed back line up with the fields handed back;
|
||||
// encode the fields back into bytes and they should be equal.
|
||||
|
||||
@@ -1,92 +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 parking_lot::RwLock;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
/// A cache with the simple goal of storing 32 byte hashes against root+item keys
|
||||
#[derive(Default, Debug)]
|
||||
pub struct HashCache {
|
||||
inner: RwLock<HashMap<RootItemKey<'static>, [u8; 32]>>,
|
||||
}
|
||||
|
||||
impl HashCache {
|
||||
/// get a hash out of the cache by its root and item key. If the item doesn't exist,
|
||||
/// run the function provided to obtain a hash to insert (or bail with some error on failure).
|
||||
pub fn get_or_insert<F, E>(&self, root: &str, item: &str, f: F) -> Result<[u8; 32], E>
|
||||
where
|
||||
F: FnOnce() -> Result<[u8; 32], E>,
|
||||
{
|
||||
let maybe_hash = self
|
||||
.inner
|
||||
.read()
|
||||
.get(&RootItemKey::new(root, item))
|
||||
.copied();
|
||||
|
||||
if let Some(hash) = maybe_hash {
|
||||
return Ok(hash);
|
||||
}
|
||||
|
||||
let hash = f()?;
|
||||
self.inner
|
||||
.write()
|
||||
.insert(RootItemKey::new(root.to_string(), item.to_string()), hash);
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// This exists so that we can look items up in the cache using &strs, without having to allocate
|
||||
/// Strings first (as you'd have to do to construct something like an `&(String,String)` key).
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
struct RootItemKey<'a> {
|
||||
pallet: Cow<'a, str>,
|
||||
item: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> RootItemKey<'a> {
|
||||
fn new(pallet: impl Into<Cow<'a, str>>, item: impl Into<Cow<'a, str>>) -> Self {
|
||||
RootItemKey {
|
||||
pallet: pallet.into(),
|
||||
item: item.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hash_cache_validation() {
|
||||
let cache = HashCache::default();
|
||||
|
||||
let pallet = "System";
|
||||
let item = "Account";
|
||||
let mut call_number = 0;
|
||||
let value = cache.get_or_insert(pallet, item, || -> Result<[u8; 32], ()> {
|
||||
call_number += 1;
|
||||
Ok([0; 32])
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
cache
|
||||
.inner
|
||||
.read()
|
||||
.get(&RootItemKey::new(pallet, item))
|
||||
.unwrap(),
|
||||
&value.unwrap()
|
||||
);
|
||||
assert_eq!(value.unwrap(), [0; 32]);
|
||||
assert_eq!(call_number, 1);
|
||||
|
||||
// Further calls must be hashed.
|
||||
let value = cache.get_or_insert(pallet, item, || -> Result<[u8; 32], ()> {
|
||||
call_number += 1;
|
||||
Ok([0; 32])
|
||||
});
|
||||
assert_eq!(call_number, 1);
|
||||
assert_eq!(value.unwrap(), [0; 32]);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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.
|
||||
|
||||
/// Locate an item of a known type in the metadata.
|
||||
/// We should already know that the item we're looking
|
||||
/// for is a call or event for instance, and then with this,
|
||||
/// we can dig up details for that item in the metadata.
|
||||
pub trait MetadataLocation {
|
||||
/// The pallet in which the item lives.
|
||||
fn pallet(&self) -> &str;
|
||||
/// The name of the item.
|
||||
fn item(&self) -> &str;
|
||||
}
|
||||
@@ -2,888 +2,37 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::hash_cache::HashCache;
|
||||
use codec::Error as CodecError;
|
||||
use frame_metadata::{
|
||||
v15::PalletConstantMetadata, v15::RuntimeMetadataV15, v15::StorageEntryMetadata,
|
||||
RuntimeMetadata, RuntimeMetadataPrefixed, META_RESERVED,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use scale_info::{form::PortableForm, PortableRegistry, Type};
|
||||
use std::{collections::HashMap, convert::TryFrom, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Metadata error originated from inspecting the internal representation of the runtime metadata.
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
pub enum MetadataError {
|
||||
/// Module is not in metadata.
|
||||
#[error("Pallet not found")]
|
||||
PalletNotFound,
|
||||
/// Pallet is not in metadata.
|
||||
#[error("Pallet index {0} not found")]
|
||||
PalletIndexNotFound(u8),
|
||||
/// Call is not in metadata.
|
||||
#[error("Call not found")]
|
||||
CallNotFound,
|
||||
/// Event is not in metadata.
|
||||
#[error("Pallet {0}, Event {0} not found")]
|
||||
EventNotFound(u8, u8),
|
||||
/// Extrinsic is not in metadata.
|
||||
#[error("Pallet {0}, Extrinsic {0} not found")]
|
||||
ExtrinsicNotFound(u8, u8),
|
||||
/// Event is not in metadata.
|
||||
#[error("Pallet {0}, Error {0} not found")]
|
||||
ErrorNotFound(u8, u8),
|
||||
/// Runtime function is not in metadata.
|
||||
#[error("Runtime function not found")]
|
||||
RuntimeFnNotFound,
|
||||
/// Storage is not in metadata.
|
||||
#[error("Storage not found")]
|
||||
StorageNotFound,
|
||||
/// Storage type does not match requested type.
|
||||
#[error("Storage type error")]
|
||||
StorageTypeError,
|
||||
/// Default error.
|
||||
#[error("Failed to decode default: {0}")]
|
||||
DefaultError(CodecError),
|
||||
/// Failure to decode constant value.
|
||||
#[error("Failed to decode constant value: {0}")]
|
||||
ConstantValueError(CodecError),
|
||||
/// Constant is not in metadata.
|
||||
#[error("Constant not found")]
|
||||
ConstantNotFound,
|
||||
/// Type is not in metadata.
|
||||
#[error("Type {0} missing from type registry")]
|
||||
TypeNotFound(u32),
|
||||
/// Runtime constant metadata is incompatible with the static one.
|
||||
#[error("Pallet {0} Constant {0} has incompatible metadata")]
|
||||
IncompatibleConstantMetadata(String, String),
|
||||
/// Runtime call metadata is incompatible with the static one.
|
||||
#[error("Pallet {0} Call {0} has incompatible metadata")]
|
||||
IncompatibleCallMetadata(String, String),
|
||||
/// Runtime storage metadata is incompatible with the static one.
|
||||
#[error("Pallet {0} Storage {0} has incompatible metadata")]
|
||||
IncompatibleStorageMetadata(String, String),
|
||||
/// Runtime API metadata is incompatible with the static one.
|
||||
#[error("Runtime API Trait {0} Method {0} has incompatible metadata")]
|
||||
IncompatibleRuntimeApiMetadata(String, String),
|
||||
/// Runtime metadata is not fully compatible with the static one.
|
||||
#[error("Node metadata is not fully compatible")]
|
||||
IncompatibleMetadata,
|
||||
}
|
||||
|
||||
// We hide the innards behind an Arc so that it's easy to clone and share.
|
||||
#[derive(Debug)]
|
||||
struct MetadataInner {
|
||||
metadata: RuntimeMetadataV15,
|
||||
|
||||
// Events are hashed by pallet an error index (decode oriented)
|
||||
events: HashMap<(u8, u8), EventMetadata>,
|
||||
// Extrinsics are hashed by pallet an error index (decode oriented)
|
||||
extrinsics: HashMap<(u8, u8), ExtrinsicMetadata>,
|
||||
// Errors are hashed by pallet and error index (decode oriented)
|
||||
errors: HashMap<(u8, u8), ErrorMetadata>,
|
||||
|
||||
// Other pallet details are hashed by pallet name.
|
||||
pallets: HashMap<String, PalletMetadata>,
|
||||
|
||||
// Type of the DispatchError type, which is what comes back if
|
||||
// an extrinsic fails.
|
||||
dispatch_error_ty: Option<u32>,
|
||||
|
||||
// Runtime API metadata
|
||||
runtime_apis: HashMap<String, RuntimeFnMetadata>,
|
||||
|
||||
// The hashes uniquely identify parts of the metadata; different
|
||||
// hashes mean some type difference exists between static and runtime
|
||||
// versions. We cache them here to avoid recalculating:
|
||||
cached_metadata_hash: RwLock<Option<[u8; 32]>>,
|
||||
cached_call_hashes: HashCache,
|
||||
cached_constant_hashes: HashCache,
|
||||
cached_storage_hashes: HashCache,
|
||||
cached_runtime_hashes: HashCache,
|
||||
}
|
||||
|
||||
/// A representation of the runtime metadata received from a node.
|
||||
/// A cheaply clone-able representation of the runtime metadata received from a node.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Metadata {
|
||||
inner: Arc<MetadataInner>,
|
||||
inner: Arc<subxt_metadata::Metadata>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Metadata {
|
||||
type Target = subxt_metadata::Metadata;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Returns a reference to [`RuntimeFnMetadata`].
|
||||
pub fn runtime_fn(&self, name: &str) -> Result<&RuntimeFnMetadata, MetadataError> {
|
||||
self.inner
|
||||
.runtime_apis
|
||||
.get(name)
|
||||
.ok_or(MetadataError::RuntimeFnNotFound)
|
||||
}
|
||||
|
||||
/// Returns a reference to [`PalletMetadata`].
|
||||
pub fn pallet(&self, name: &str) -> Result<&PalletMetadata, MetadataError> {
|
||||
self.inner
|
||||
.pallets
|
||||
.get(name)
|
||||
.ok_or(MetadataError::PalletNotFound)
|
||||
}
|
||||
|
||||
/// Returns the metadata for the event at the given pallet and event indices.
|
||||
pub fn event(
|
||||
&self,
|
||||
pallet_index: u8,
|
||||
event_index: u8,
|
||||
) -> Result<&EventMetadata, MetadataError> {
|
||||
let event = self
|
||||
.inner
|
||||
.events
|
||||
.get(&(pallet_index, event_index))
|
||||
.ok_or(MetadataError::EventNotFound(pallet_index, event_index))?;
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
/// Returns the metadata for the extrinsic at the given pallet and call indices.
|
||||
pub fn extrinsic(
|
||||
&self,
|
||||
pallet_index: u8,
|
||||
call_index: u8,
|
||||
) -> Result<&ExtrinsicMetadata, MetadataError> {
|
||||
let event = self
|
||||
.inner
|
||||
.extrinsics
|
||||
.get(&(pallet_index, call_index))
|
||||
.ok_or(MetadataError::ExtrinsicNotFound(pallet_index, call_index))?;
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
/// Returns the metadata for the error at the given pallet and error indices.
|
||||
pub fn error(
|
||||
&self,
|
||||
pallet_index: u8,
|
||||
error_index: u8,
|
||||
) -> Result<&ErrorMetadata, MetadataError> {
|
||||
let error = self
|
||||
.inner
|
||||
.errors
|
||||
.get(&(pallet_index, error_index))
|
||||
.ok_or(MetadataError::ErrorNotFound(pallet_index, error_index))?;
|
||||
Ok(error)
|
||||
}
|
||||
|
||||
/// Return the DispatchError type ID if it exists.
|
||||
pub fn dispatch_error_ty(&self) -> Option<u32> {
|
||||
self.inner.dispatch_error_ty
|
||||
}
|
||||
|
||||
/// Return the type registry embedded within the metadata.
|
||||
pub fn types(&self) -> &PortableRegistry {
|
||||
&self.inner.metadata.types
|
||||
}
|
||||
|
||||
/// Resolve a type definition.
|
||||
pub fn resolve_type(&self, id: u32) -> Option<&Type<PortableForm>> {
|
||||
self.inner.metadata.types.resolve(id)
|
||||
}
|
||||
|
||||
/// Return the runtime metadata.
|
||||
pub fn runtime_metadata(&self) -> &RuntimeMetadataV15 {
|
||||
&self.inner.metadata
|
||||
}
|
||||
|
||||
/// Obtain the unique hash for a specific storage entry.
|
||||
pub fn storage_hash(&self, pallet: &str, storage: &str) -> Result<[u8; 32], MetadataError> {
|
||||
self.inner
|
||||
.cached_storage_hashes
|
||||
.get_or_insert(pallet, storage, || {
|
||||
subxt_metadata::get_storage_hash(&self.inner.metadata, pallet, storage).map_err(
|
||||
|e| match e {
|
||||
subxt_metadata::NotFound::Root => MetadataError::PalletNotFound,
|
||||
subxt_metadata::NotFound::Item => MetadataError::StorageNotFound,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Obtain the unique hash for a constant.
|
||||
pub fn constant_hash(&self, pallet: &str, constant: &str) -> Result<[u8; 32], MetadataError> {
|
||||
self.inner
|
||||
.cached_constant_hashes
|
||||
.get_or_insert(pallet, constant, || {
|
||||
subxt_metadata::get_constant_hash(&self.inner.metadata, pallet, constant).map_err(
|
||||
|e| match e {
|
||||
subxt_metadata::NotFound::Root => MetadataError::PalletNotFound,
|
||||
subxt_metadata::NotFound::Item => MetadataError::ConstantNotFound,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Obtain the unique hash for a call.
|
||||
pub fn call_hash(&self, pallet: &str, function: &str) -> Result<[u8; 32], MetadataError> {
|
||||
self.inner
|
||||
.cached_call_hashes
|
||||
.get_or_insert(pallet, function, || {
|
||||
subxt_metadata::get_call_hash(&self.inner.metadata, pallet, function).map_err(|e| {
|
||||
match e {
|
||||
subxt_metadata::NotFound::Root => MetadataError::PalletNotFound,
|
||||
subxt_metadata::NotFound::Item => MetadataError::CallNotFound,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Obtain the unique hash for a runtime API function.
|
||||
pub fn runtime_api_hash(
|
||||
&self,
|
||||
trait_name: &str,
|
||||
method_name: &str,
|
||||
) -> Result<[u8; 32], MetadataError> {
|
||||
self.inner
|
||||
.cached_runtime_hashes
|
||||
.get_or_insert(trait_name, method_name, || {
|
||||
subxt_metadata::get_runtime_api_hash(&self.inner.metadata, trait_name, method_name)
|
||||
.map_err(|_| MetadataError::RuntimeFnNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
/// Obtain the unique hash for this metadata.
|
||||
pub fn metadata_hash<T: AsRef<str>>(&self, pallets: &[T]) -> [u8; 32] {
|
||||
if let Some(hash) = *self.inner.cached_metadata_hash.read() {
|
||||
return hash;
|
||||
pub(crate) fn new(md: subxt_metadata::Metadata) -> Self {
|
||||
Metadata {
|
||||
inner: Arc::new(md),
|
||||
}
|
||||
|
||||
let hash = subxt_metadata::MetadataHasher::new()
|
||||
.only_these_pallets(pallets)
|
||||
.hash(self.runtime_metadata());
|
||||
*self.inner.cached_metadata_hash.write() = Some(hash);
|
||||
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a specific runtime API function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RuntimeFnMetadata {
|
||||
/// The trait name of the runtime function.
|
||||
trait_name: String,
|
||||
/// The method name of the runtime function.
|
||||
method_name: String,
|
||||
/// The parameter name and type IDs interpreted as `scale_info::Field`
|
||||
/// for ease of decoding.
|
||||
fields: Vec<scale_info::Field<scale_info::form::PortableForm>>,
|
||||
/// The type ID of the return type.
|
||||
return_id: u32,
|
||||
}
|
||||
|
||||
impl RuntimeFnMetadata {
|
||||
/// Get the parameters as fields.
|
||||
pub fn fields(&self) -> &[scale_info::Field<scale_info::form::PortableForm>] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
/// Return the trait name of the runtime function.
|
||||
pub fn trait_name(&self) -> &str {
|
||||
&self.trait_name
|
||||
}
|
||||
|
||||
/// Return the method name of the runtime function.
|
||||
pub fn method_name(&self) -> &str {
|
||||
&self.method_name
|
||||
}
|
||||
|
||||
/// Get the type ID of the return type.
|
||||
pub fn return_id(&self) -> u32 {
|
||||
self.return_id
|
||||
impl From<subxt_metadata::Metadata> for Metadata {
|
||||
fn from(md: subxt_metadata::Metadata) -> Self {
|
||||
Metadata::new(md)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a specific pallet.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PalletMetadata {
|
||||
index: u8,
|
||||
name: String,
|
||||
call_metadata: HashMap<String, CallMetadata>,
|
||||
call_ty_id: Option<u32>,
|
||||
event_ty_id: Option<u32>,
|
||||
storage: HashMap<String, StorageEntryMetadata<PortableForm>>,
|
||||
constants: HashMap<String, PalletConstantMetadata<PortableForm>>,
|
||||
}
|
||||
|
||||
impl PalletMetadata {
|
||||
/// Get the name of the pallet.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Get the index of this pallet.
|
||||
pub fn index(&self) -> u8 {
|
||||
self.index
|
||||
}
|
||||
|
||||
/// If calls exist for this pallet, this returns the type ID of the variant
|
||||
/// representing the different possible calls.
|
||||
pub fn call_ty_id(&self) -> Option<u32> {
|
||||
self.call_ty_id
|
||||
}
|
||||
|
||||
/// If events exist for this pallet, this returns the type ID of the variant
|
||||
/// representing the different possible events.
|
||||
pub fn event_ty_id(&self) -> Option<u32> {
|
||||
self.event_ty_id
|
||||
}
|
||||
|
||||
/// Attempt to resolve a call into an index in this pallet, failing
|
||||
/// if the call is not found in this pallet.
|
||||
pub fn call(&self, function: &str) -> Result<&CallMetadata, MetadataError> {
|
||||
let fn_index = self
|
||||
.call_metadata
|
||||
.get(function)
|
||||
.ok_or(MetadataError::CallNotFound)?;
|
||||
Ok(fn_index)
|
||||
}
|
||||
|
||||
/// Return [`StorageEntryMetadata`] given some storage key.
|
||||
pub fn storage(&self, key: &str) -> Result<&StorageEntryMetadata<PortableForm>, MetadataError> {
|
||||
self.storage.get(key).ok_or(MetadataError::StorageNotFound)
|
||||
}
|
||||
|
||||
/// Get a constant's metadata by name.
|
||||
pub fn constant(
|
||||
&self,
|
||||
key: &str,
|
||||
) -> Result<&PalletConstantMetadata<PortableForm>, MetadataError> {
|
||||
self.constants
|
||||
.get(key)
|
||||
.ok_or(MetadataError::ConstantNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CallMetadata {
|
||||
call_index: u8,
|
||||
fields: Vec<scale_info::Field<scale_info::form::PortableForm>>,
|
||||
}
|
||||
|
||||
impl CallMetadata {
|
||||
/// Index of this call.
|
||||
pub fn index(&self) -> u8 {
|
||||
self.call_index
|
||||
}
|
||||
|
||||
/// The names, type names & types of each field in the call data.
|
||||
pub fn fields(&self) -> &[scale_info::Field<scale_info::form::PortableForm>] {
|
||||
&self.fields
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for specific events.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EventMetadata {
|
||||
// The pallet name is shared across every event, so put it
|
||||
// behind an Arc to avoid lots of needless clones of it existing.
|
||||
pallet: Arc<str>,
|
||||
event: String,
|
||||
fields: Vec<scale_info::Field<scale_info::form::PortableForm>>,
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl EventMetadata {
|
||||
/// Get the name of the pallet from which the event was emitted.
|
||||
pub fn pallet(&self) -> &str {
|
||||
&self.pallet
|
||||
}
|
||||
|
||||
/// Get the name of the pallet event which was emitted.
|
||||
pub fn event(&self) -> &str {
|
||||
&self.event
|
||||
}
|
||||
|
||||
/// The names, type names & types of each field in the event.
|
||||
pub fn fields(&self) -> &[scale_info::Field<scale_info::form::PortableForm>] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
/// Documentation for this event.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for specific extrinsics.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtrinsicMetadata {
|
||||
// The pallet name is shared across every extrinsic, so put it
|
||||
// behind an Arc to avoid lots of needless clones of it existing.
|
||||
pallet: Arc<str>,
|
||||
call: String,
|
||||
fields: Vec<scale_info::Field<scale_info::form::PortableForm>>,
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl ExtrinsicMetadata {
|
||||
/// Get the name of the pallet from which the extrinsic was emitted.
|
||||
pub fn pallet(&self) -> &str {
|
||||
&self.pallet
|
||||
}
|
||||
|
||||
/// Get the name of the extrinsic call.
|
||||
pub fn call(&self) -> &str {
|
||||
&self.call
|
||||
}
|
||||
|
||||
/// The names, type names & types of each field in the extrinsic.
|
||||
pub fn fields(&self) -> &[scale_info::Field<scale_info::form::PortableForm>] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
/// Documentation for this extrinsic.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Details about a specific runtime error.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ErrorMetadata {
|
||||
// The pallet name is shared across every event, so put it
|
||||
// behind an Arc to avoid lots of needless clones of it existing.
|
||||
pallet: Arc<str>,
|
||||
error: String,
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl ErrorMetadata {
|
||||
/// Get the name of the pallet from which the error originates.
|
||||
pub fn pallet(&self) -> &str {
|
||||
&self.pallet
|
||||
}
|
||||
|
||||
/// The name of the error.
|
||||
pub fn error(&self) -> &str {
|
||||
&self.error
|
||||
}
|
||||
|
||||
/// Documentation for the error.
|
||||
pub fn docs(&self) -> &[String] {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Error originated from converting a runtime metadata [RuntimeMetadataPrefixed] to
|
||||
/// the internal [Metadata] representation.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum InvalidMetadataError {
|
||||
/// Invalid prefix
|
||||
#[error("Invalid prefix")]
|
||||
InvalidPrefix,
|
||||
/// Invalid version
|
||||
#[error("Invalid version")]
|
||||
InvalidVersion,
|
||||
/// Type missing from type registry
|
||||
#[error("Type {0} missing from type registry")]
|
||||
MissingType(u32),
|
||||
/// Type missing extrinsic "Call" type
|
||||
#[error("Missing extrinsic Call type")]
|
||||
MissingCallType,
|
||||
/// The extrinsic variant expected to contain a single field.
|
||||
#[error("Extrinsic variant at index {0} expected to contain a single field")]
|
||||
InvalidExtrinsicVariant(u8),
|
||||
/// Type was not a variant/enum type
|
||||
#[error("Type {0} was not a variant/enum type")]
|
||||
TypeDefNotVariant(u32),
|
||||
}
|
||||
|
||||
impl TryFrom<RuntimeMetadataPrefixed> for Metadata {
|
||||
type Error = InvalidMetadataError;
|
||||
|
||||
fn try_from(metadata: RuntimeMetadataPrefixed) -> Result<Self, Self::Error> {
|
||||
if metadata.0 != META_RESERVED {
|
||||
return Err(InvalidMetadataError::InvalidPrefix);
|
||||
}
|
||||
let metadata = match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => subxt_metadata::metadata_v14_to_latest(v14),
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
_ => return Err(InvalidMetadataError::InvalidVersion),
|
||||
};
|
||||
|
||||
let runtime_apis: HashMap<String, RuntimeFnMetadata> = metadata
|
||||
.apis
|
||||
.iter()
|
||||
.flat_map(|trait_metadata| {
|
||||
let trait_name = &trait_metadata.name;
|
||||
|
||||
trait_metadata
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method_metadata| {
|
||||
// Function named used by substrate to identify the runtime call.
|
||||
let fn_name = format!("{}_{}", trait_name, method_metadata.name);
|
||||
|
||||
// Parameters mapped as `scale_info::Field` to allow dynamic decoding.
|
||||
let fields: Vec<_> = method_metadata
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|input| {
|
||||
let name = input.name.clone();
|
||||
let ty = input.ty.id;
|
||||
scale_info::Field {
|
||||
name: Some(name),
|
||||
ty: ty.into(),
|
||||
type_name: None,
|
||||
docs: Default::default(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let return_id = method_metadata.output.id;
|
||||
let metadata = RuntimeFnMetadata {
|
||||
fields,
|
||||
return_id,
|
||||
trait_name: trait_name.clone(),
|
||||
method_name: method_metadata.name.clone(),
|
||||
};
|
||||
|
||||
(fn_name, metadata)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let get_type_def_variant = |type_id: u32| {
|
||||
let ty = metadata
|
||||
.types
|
||||
.resolve(type_id)
|
||||
.ok_or(InvalidMetadataError::MissingType(type_id))?;
|
||||
if let scale_info::TypeDef::Variant(var) = &ty.type_def {
|
||||
Ok(var)
|
||||
} else {
|
||||
Err(InvalidMetadataError::TypeDefNotVariant(type_id))
|
||||
}
|
||||
};
|
||||
let pallets = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.map(|pallet| {
|
||||
let call_ty_id = pallet.calls.as_ref().map(|c| c.ty.id);
|
||||
let event_ty_id = pallet.event.as_ref().map(|e| e.ty.id);
|
||||
|
||||
let call_metadata = pallet.calls.as_ref().map_or(Ok(HashMap::new()), |call| {
|
||||
let type_def_variant = get_type_def_variant(call.ty.id)?;
|
||||
let call_indexes = type_def_variant
|
||||
.variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
(
|
||||
v.name.clone(),
|
||||
CallMetadata {
|
||||
call_index: v.index,
|
||||
fields: v.fields.clone(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Ok(call_indexes)
|
||||
})?;
|
||||
|
||||
let storage = pallet.storage.as_ref().map_or(HashMap::new(), |storage| {
|
||||
storage
|
||||
.entries
|
||||
.iter()
|
||||
.map(|entry| (entry.name.clone(), entry.clone()))
|
||||
.collect()
|
||||
});
|
||||
|
||||
let constants = pallet
|
||||
.constants
|
||||
.iter()
|
||||
.map(|constant| (constant.name.clone(), constant.clone()))
|
||||
.collect();
|
||||
|
||||
let pallet_metadata = PalletMetadata {
|
||||
index: pallet.index,
|
||||
name: pallet.name.to_string(),
|
||||
call_metadata,
|
||||
call_ty_id,
|
||||
event_ty_id,
|
||||
storage,
|
||||
constants,
|
||||
};
|
||||
|
||||
Ok((pallet.name.to_string(), pallet_metadata))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let mut events = HashMap::<(u8, u8), EventMetadata>::new();
|
||||
for pallet in &metadata.pallets {
|
||||
if let Some(event) = &pallet.event {
|
||||
let pallet_name: Arc<str> = pallet.name.to_string().into();
|
||||
let event_type_id = event.ty.id;
|
||||
let event_variant = get_type_def_variant(event_type_id)?;
|
||||
for variant in &event_variant.variants {
|
||||
events.insert(
|
||||
(pallet.index, variant.index),
|
||||
EventMetadata {
|
||||
pallet: pallet_name.clone(),
|
||||
event: variant.name.clone(),
|
||||
fields: variant.fields.clone(),
|
||||
docs: variant.docs.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut errors = HashMap::<(u8, u8), ErrorMetadata>::new();
|
||||
for pallet in &metadata.pallets {
|
||||
if let Some(error) = &pallet.error {
|
||||
let pallet_name: Arc<str> = pallet.name.to_string().into();
|
||||
let error_variant = get_type_def_variant(error.ty.id)?;
|
||||
for variant in &error_variant.variants {
|
||||
errors.insert(
|
||||
(pallet.index, variant.index),
|
||||
ErrorMetadata {
|
||||
pallet: pallet_name.clone(),
|
||||
error: variant.name.clone(),
|
||||
docs: variant.docs.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch_error_ty = metadata
|
||||
.types
|
||||
.types
|
||||
.iter()
|
||||
.find(|ty| ty.ty.path.segments == ["sp_runtime", "DispatchError"])
|
||||
.map(|ty| ty.id);
|
||||
|
||||
let extrinsic_ty = metadata
|
||||
.types
|
||||
.resolve(metadata.extrinsic.ty.id)
|
||||
.ok_or(InvalidMetadataError::MissingType(metadata.extrinsic.ty.id))?;
|
||||
|
||||
let Some(call_id) = extrinsic_ty.type_params
|
||||
.iter()
|
||||
.find(|ty| ty.name == "Call")
|
||||
.and_then(|ty| ty.ty)
|
||||
.map(|ty| ty.id) else {
|
||||
return Err(InvalidMetadataError::MissingCallType);
|
||||
};
|
||||
|
||||
let call_type_variants = get_type_def_variant(call_id)?;
|
||||
|
||||
let mut extrinsics = HashMap::<(u8, u8), ExtrinsicMetadata>::new();
|
||||
for variant in &call_type_variants.variants {
|
||||
let pallet_name: Arc<str> = variant.name.to_string().into();
|
||||
let pallet_index = variant.index;
|
||||
|
||||
// Pallet variants must contain one single call variant.
|
||||
// In the following form:
|
||||
//
|
||||
// enum RuntimeCall {
|
||||
// Pallet(pallet_call)
|
||||
// }
|
||||
if variant.fields.len() != 1 {
|
||||
return Err(InvalidMetadataError::InvalidExtrinsicVariant(pallet_index));
|
||||
}
|
||||
let Some(ty) = variant.fields.first() else {
|
||||
return Err(InvalidMetadataError::InvalidExtrinsicVariant(pallet_index));
|
||||
};
|
||||
|
||||
// Get the call variant.
|
||||
let call_type_variant = get_type_def_variant(ty.ty.id)?;
|
||||
for variant in &call_type_variant.variants {
|
||||
extrinsics.insert(
|
||||
(pallet_index, variant.index),
|
||||
ExtrinsicMetadata {
|
||||
pallet: pallet_name.clone(),
|
||||
call: variant.name.to_string(),
|
||||
fields: variant.fields.clone(),
|
||||
docs: variant.docs.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Metadata {
|
||||
inner: Arc::new(MetadataInner {
|
||||
metadata,
|
||||
pallets,
|
||||
events,
|
||||
extrinsics,
|
||||
errors,
|
||||
dispatch_error_ty,
|
||||
runtime_apis,
|
||||
cached_metadata_hash: Default::default(),
|
||||
cached_call_hashes: Default::default(),
|
||||
cached_constant_hashes: Default::default(),
|
||||
cached_storage_hashes: Default::default(),
|
||||
cached_runtime_hashes: Default::default(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, PalletStorageMetadata,
|
||||
StorageEntryModifier, StorageEntryType,
|
||||
};
|
||||
use scale_info::{meta_type, TypeInfo};
|
||||
|
||||
fn load_metadata() -> Metadata {
|
||||
// Extrinsic needs to contain at least the generic type parameter "Call"
|
||||
// for the metadata to be valid.
|
||||
// The "Call" type from the metadata is used to decode extrinsics.
|
||||
// In reality, the extrinsic type has "Call", "Address", "Extra", "Signature" generic types.
|
||||
#[allow(unused)]
|
||||
#[derive(TypeInfo)]
|
||||
struct ExtrinsicType<Call> {
|
||||
call: Call,
|
||||
}
|
||||
// Because this type is used to decode extrinsics, we expect this to be a TypeDefVariant.
|
||||
// Each pallet must contain one single variant.
|
||||
#[allow(unused)]
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeCall {
|
||||
PalletName(Pallet),
|
||||
}
|
||||
// The calls of the pallet.
|
||||
#[allow(unused)]
|
||||
#[derive(TypeInfo)]
|
||||
enum Pallet {
|
||||
#[allow(unused)]
|
||||
SomeCall,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(TypeInfo)]
|
||||
enum Call {
|
||||
fill_block { param: u128 },
|
||||
}
|
||||
let storage = PalletStorageMetadata {
|
||||
prefix: "System",
|
||||
entries: vec![StorageEntryMetadata {
|
||||
name: "Account",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(meta_type::<u32>()),
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
}],
|
||||
};
|
||||
let constant = PalletConstantMetadata {
|
||||
name: "BlockWeights",
|
||||
ty: meta_type::<u32>(),
|
||||
value: vec![1, 2, 3],
|
||||
docs: vec![],
|
||||
};
|
||||
let pallet = PalletMetadata {
|
||||
index: 0,
|
||||
name: "System",
|
||||
calls: Some(PalletCallMetadata {
|
||||
ty: meta_type::<Call>(),
|
||||
}),
|
||||
storage: Some(storage),
|
||||
constants: vec![constant],
|
||||
event: None,
|
||||
error: None,
|
||||
docs: vec![],
|
||||
};
|
||||
|
||||
let metadata = RuntimeMetadataV15::new(
|
||||
vec![pallet],
|
||||
ExtrinsicMetadata {
|
||||
ty: meta_type::<ExtrinsicType<RuntimeCall>>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
},
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
);
|
||||
let prefixed = RuntimeMetadataPrefixed::from(metadata);
|
||||
|
||||
Metadata::try_from(prefixed)
|
||||
.expect("Cannot translate runtime metadata to internal Metadata")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_inner_cache() {
|
||||
// Note: Dependency on test_runtime can be removed if complex metadata
|
||||
// is manually constructed.
|
||||
let metadata = load_metadata();
|
||||
|
||||
let hash = metadata.metadata_hash(&["System"]);
|
||||
// Check inner caching.
|
||||
assert_eq!(metadata.inner.cached_metadata_hash.read().unwrap(), hash);
|
||||
|
||||
// The cache `metadata.inner.cached_metadata_hash` is already populated from
|
||||
// the previous call. Therefore, changing the pallets argument must not
|
||||
// change the methods behavior.
|
||||
let hash_old = metadata.metadata_hash(&["no-pallet"]);
|
||||
assert_eq!(hash_old, hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_call_inner_cache() {
|
||||
let metadata = load_metadata();
|
||||
|
||||
let hash = metadata.call_hash("System", "fill_block");
|
||||
|
||||
let mut call_number = 0;
|
||||
let hash_cached = metadata.inner.cached_call_hashes.get_or_insert(
|
||||
"System",
|
||||
"fill_block",
|
||||
|| -> Result<[u8; 32], MetadataError> {
|
||||
call_number += 1;
|
||||
Ok([0; 32])
|
||||
},
|
||||
);
|
||||
|
||||
// Check function is never called (e.i, value fetched from cache).
|
||||
assert_eq!(call_number, 0);
|
||||
assert_eq!(hash.unwrap(), hash_cached.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_constant_inner_cache() {
|
||||
let metadata = load_metadata();
|
||||
|
||||
let hash = metadata.constant_hash("System", "BlockWeights");
|
||||
|
||||
let mut call_number = 0;
|
||||
let hash_cached = metadata.inner.cached_constant_hashes.get_or_insert(
|
||||
"System",
|
||||
"BlockWeights",
|
||||
|| -> Result<[u8; 32], MetadataError> {
|
||||
call_number += 1;
|
||||
Ok([0; 32])
|
||||
},
|
||||
);
|
||||
|
||||
// Check function is never called (e.i, value fetched from cache).
|
||||
assert_eq!(call_number, 0);
|
||||
assert_eq!(hash.unwrap(), hash_cached.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_storage_inner_cache() {
|
||||
let metadata = load_metadata();
|
||||
let hash = metadata.storage_hash("System", "Account");
|
||||
|
||||
let mut call_number = 0;
|
||||
let hash_cached = metadata.inner.cached_storage_hashes.get_or_insert(
|
||||
"System",
|
||||
"Account",
|
||||
|| -> Result<[u8; 32], MetadataError> {
|
||||
call_number += 1;
|
||||
Ok([0; 32])
|
||||
},
|
||||
);
|
||||
|
||||
// Check function is never called (e.i, value fetched from cache).
|
||||
assert_eq!(call_number, 0);
|
||||
assert_eq!(hash.unwrap(), hash_cached.unwrap());
|
||||
impl codec::Decode for Metadata {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
subxt_metadata::Metadata::decode(input).map(Metadata::new)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,10 @@
|
||||
//! Types representing the metadata obtained from a node.
|
||||
|
||||
mod decode_encode_traits;
|
||||
mod hash_cache;
|
||||
mod metadata_location;
|
||||
mod metadata_type;
|
||||
|
||||
pub use metadata_location::MetadataLocation;
|
||||
|
||||
pub use metadata_type::{
|
||||
ErrorMetadata, EventMetadata, ExtrinsicMetadata, InvalidMetadataError, Metadata, MetadataError,
|
||||
PalletMetadata, RuntimeFnMetadata,
|
||||
};
|
||||
|
||||
pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata};
|
||||
pub use metadata_type::Metadata;
|
||||
|
||||
// Expose metadata types under a sub module in case somebody needs to reference them:
|
||||
pub use subxt_metadata as types;
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{error::Error, utils::PhantomDataSendSync, Config, Metadata};
|
||||
@@ -149,8 +148,7 @@ impl<T: Config> Rpc<T> {
|
||||
.client
|
||||
.request("state_getMetadata", rpc_params![at])
|
||||
.await?;
|
||||
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])?;
|
||||
let metadata: Metadata = meta.try_into()?;
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
@@ -387,9 +385,7 @@ impl<T: Config> Rpc<T> {
|
||||
|
||||
let bytes = opaque.ok_or(Error::Other("Metadata version not found".into()))?;
|
||||
|
||||
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes.0[..])?;
|
||||
|
||||
let metadata: Metadata = meta.try_into()?;
|
||||
let metadata: Metadata = Decode::decode(&mut &bytes.0[..])?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
@@ -404,9 +400,7 @@ impl<T: Config> Rpc<T> {
|
||||
let bytes: frame_metadata::OpaqueMetadata =
|
||||
self.state_call("Metadata_metadata", None, None).await?;
|
||||
|
||||
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes.0[..])?;
|
||||
|
||||
let metadata: Metadata = meta.try_into()?;
|
||||
let metadata: Metadata = Decode::decode(&mut &bytes.0[..])?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use scale_value::Composite;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::dynamic::DecodedValueThunk;
|
||||
use crate::error::MetadataError;
|
||||
use crate::{metadata::DecodeWithMetadata, Error, Metadata};
|
||||
|
||||
/// This represents a runtime API payload that can call into the runtime of node.
|
||||
@@ -36,8 +37,11 @@ pub trait RuntimeApiPayload {
|
||||
// with the `subxt::Metadata.
|
||||
type ReturnType: DecodeWithMetadata;
|
||||
|
||||
/// The runtime API function name.
|
||||
fn fn_name(&self) -> &str;
|
||||
/// The runtime API trait name.
|
||||
fn trait_name(&self) -> &str;
|
||||
|
||||
/// The runtime API method name.
|
||||
fn method_name(&self) -> &str;
|
||||
|
||||
/// Scale encode the arguments data.
|
||||
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error>;
|
||||
@@ -63,7 +67,8 @@ pub trait RuntimeApiPayload {
|
||||
/// via the `subxt` macro) or dynamic values via [`dynamic`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Payload<ArgsData, ReturnTy> {
|
||||
fn_name: Cow<'static, str>,
|
||||
trait_name: Cow<'static, str>,
|
||||
method_name: Cow<'static, str>,
|
||||
args_data: ArgsData,
|
||||
validation_hash: Option<[u8; 32]>,
|
||||
_marker: PhantomData<ReturnTy>,
|
||||
@@ -74,16 +79,42 @@ impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> RuntimeApiPayload
|
||||
{
|
||||
type ReturnType = ReturnTy;
|
||||
|
||||
fn fn_name(&self) -> &str {
|
||||
&self.fn_name
|
||||
fn trait_name(&self) -> &str {
|
||||
&self.trait_name
|
||||
}
|
||||
|
||||
fn method_name(&self) -> &str {
|
||||
&self.method_name
|
||||
}
|
||||
|
||||
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
|
||||
let fn_metadata = metadata.runtime_fn(&self.fn_name)?;
|
||||
let api_trait = metadata
|
||||
.runtime_api_trait_by_name(&self.trait_name)
|
||||
.ok_or_else(|| MetadataError::RuntimeTraitNotFound((*self.trait_name).to_owned()))?;
|
||||
let api_method = api_trait
|
||||
.method_by_name(&self.method_name)
|
||||
.ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?;
|
||||
|
||||
// TODO [jsdw]: This isn't good. The options are:
|
||||
// 1. Tweak the Metadata to store things in this way (not great, but
|
||||
// it's what we did previously).
|
||||
// 2. Tweak scale-encode's methods to accept iterators over
|
||||
// { name: Option<&str>, type_id: u32 }, because they shouldn't
|
||||
// need to allocate anyway. I'd like us to do this, and then we can
|
||||
// remove allocations from this code and probably a couple of other
|
||||
// places.
|
||||
let fields: Vec<_> = api_method
|
||||
.inputs()
|
||||
.map(|input| scale_info::Field {
|
||||
name: Some(input.name.to_owned()),
|
||||
ty: input.ty.into(),
|
||||
type_name: None,
|
||||
docs: Default::default(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.args_data
|
||||
.encode_as_fields_to(fn_metadata.fields(), metadata.types(), out)?;
|
||||
|
||||
.encode_as_fields_to(&fields, metadata.types(), out)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -97,9 +128,14 @@ pub type DynamicRuntimeApiPayload = Payload<Composite<()>, DecodedValueThunk>;
|
||||
|
||||
impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
|
||||
/// Create a new [`Payload`].
|
||||
pub fn new(fn_name: impl Into<String>, args_data: ArgsData) -> Self {
|
||||
pub fn new(
|
||||
trait_name: impl Into<String>,
|
||||
method_name: impl Into<String>,
|
||||
args_data: ArgsData,
|
||||
) -> Self {
|
||||
Payload {
|
||||
fn_name: Cow::Owned(fn_name.into()),
|
||||
trait_name: Cow::Owned(trait_name.into()),
|
||||
method_name: Cow::Owned(method_name.into()),
|
||||
args_data,
|
||||
validation_hash: None,
|
||||
_marker: PhantomData,
|
||||
@@ -112,12 +148,14 @@ impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
|
||||
/// This is only expected to be used from codegen.
|
||||
#[doc(hidden)]
|
||||
pub fn new_static(
|
||||
fn_name: &'static str,
|
||||
trait_name: &'static str,
|
||||
method_name: &'static str,
|
||||
args_data: ArgsData,
|
||||
hash: [u8; 32],
|
||||
) -> Payload<ArgsData, ReturnTy> {
|
||||
Payload {
|
||||
fn_name: Cow::Borrowed(fn_name),
|
||||
trait_name: Cow::Borrowed(trait_name),
|
||||
method_name: Cow::Borrowed(method_name),
|
||||
args_data,
|
||||
validation_hash: Some(hash),
|
||||
_marker: std::marker::PhantomData,
|
||||
@@ -132,9 +170,14 @@ impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the function name.
|
||||
pub fn fn_name(&self) -> &str {
|
||||
&self.fn_name
|
||||
/// Returns the trait name.
|
||||
pub fn trait_name(&self) -> &str {
|
||||
&self.trait_name
|
||||
}
|
||||
|
||||
/// Returns the method name.
|
||||
pub fn method_name(&self) -> &str {
|
||||
&self.method_name
|
||||
}
|
||||
|
||||
/// Returns the arguments data.
|
||||
@@ -145,8 +188,9 @@ impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
|
||||
|
||||
/// Create a new [`DynamicRuntimeApiPayload`].
|
||||
pub fn dynamic(
|
||||
fn_name: impl Into<String>,
|
||||
trait_name: impl Into<String>,
|
||||
method_name: impl Into<String>,
|
||||
args_data: impl Into<Composite<()>>,
|
||||
) -> DynamicRuntimeApiPayload {
|
||||
Payload::new(fn_name, args_data.into())
|
||||
Payload::new(trait_name, method_name, args_data.into())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{client::OnlineClientT, error::Error, metadata::DecodeWithMetadata, Config};
|
||||
use crate::{
|
||||
client::OnlineClientT,
|
||||
error::{Error, MetadataError},
|
||||
metadata::DecodeWithMetadata,
|
||||
Config,
|
||||
};
|
||||
use codec::Decode;
|
||||
use derivative::Derivative;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
@@ -64,26 +69,25 @@ where
|
||||
// which is a temporary thing we'll be throwing away quickly:
|
||||
async move {
|
||||
let metadata = client.metadata();
|
||||
let function = payload.fn_name();
|
||||
|
||||
// Check if the function is present in the runtime metadata.
|
||||
let fn_metadata = metadata.runtime_fn(function)?;
|
||||
// Return type ID used for dynamic decoding.
|
||||
let return_id = fn_metadata.return_id();
|
||||
let api_trait = metadata
|
||||
.runtime_api_trait_by_name(payload.trait_name())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::RuntimeTraitNotFound(payload.trait_name().to_owned())
|
||||
})?;
|
||||
let api_method = api_trait
|
||||
.method_by_name(payload.method_name())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::RuntimeMethodNotFound(payload.method_name().to_owned())
|
||||
})?;
|
||||
|
||||
// Validate the runtime API payload hash against the compile hash from codegen.
|
||||
if let Some(static_hash) = payload.validation_hash() {
|
||||
let runtime_hash = metadata
|
||||
.runtime_api_hash(fn_metadata.trait_name(), fn_metadata.method_name())?;
|
||||
|
||||
let Some(runtime_hash) = api_trait.method_hash(payload.method_name()) else {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
};
|
||||
if static_hash != runtime_hash {
|
||||
return Err(
|
||||
crate::metadata::MetadataError::IncompatibleRuntimeApiMetadata(
|
||||
fn_metadata.trait_name().into(),
|
||||
fn_metadata.method_name().into(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +95,16 @@ where
|
||||
// For static payloads (codegen) this is pass-through, bytes are not altered.
|
||||
// For dynamic payloads this relies on `scale_value::encode_as_fields_to`.
|
||||
let params = payload.encode_args(&metadata)?;
|
||||
let call_name = format!("{}_{}", payload.trait_name(), payload.method_name());
|
||||
|
||||
let bytes = client
|
||||
.rpc()
|
||||
.state_call_raw(function, Some(params.as_slice()), Some(block_hash))
|
||||
.state_call_raw(&call_name, Some(params.as_slice()), Some(block_hash))
|
||||
.await?;
|
||||
|
||||
let value = <Call::ReturnType as DecodeWithMetadata>::decode_with_metadata(
|
||||
&mut &bytes[..],
|
||||
return_id,
|
||||
api_method.output_ty(),
|
||||
&metadata,
|
||||
)?;
|
||||
Ok(value)
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
use crate::{
|
||||
dynamic::{DecodedValueThunk, Value},
|
||||
error::{Error, StorageAddressError},
|
||||
error::{Error, MetadataError, StorageAddressError},
|
||||
metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata},
|
||||
utils::{Encoded, Static},
|
||||
};
|
||||
use frame_metadata::v15::{StorageEntryType, StorageHasher};
|
||||
use scale_info::TypeDef;
|
||||
use std::borrow::Cow;
|
||||
use subxt_metadata::{StorageEntryType, StorageHasher};
|
||||
|
||||
/// This represents a storage address. Anything implementing this trait
|
||||
/// can be used to fetch and iterate over storage entries.
|
||||
@@ -138,10 +138,17 @@ where
|
||||
}
|
||||
|
||||
fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error> {
|
||||
let pallet = metadata.pallet(&self.pallet_name)?;
|
||||
let storage = pallet.storage(&self.entry_name)?;
|
||||
let pallet = metadata
|
||||
.pallet_by_name(self.pallet_name())
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(self.pallet_name().to_owned()))?;
|
||||
let storage = pallet
|
||||
.storage()
|
||||
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?;
|
||||
let entry = storage
|
||||
.entry_by_name(self.entry_name())
|
||||
.ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?;
|
||||
|
||||
match &storage.ty {
|
||||
match entry.entry_type() {
|
||||
StorageEntryType::Plain(_) => {
|
||||
if !self.storage_entry_keys.is_empty() {
|
||||
Err(StorageAddressError::WrongNumberOfKeys {
|
||||
@@ -153,10 +160,13 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
StorageEntryType::Map { hashers, key, .. } => {
|
||||
StorageEntryType::Map {
|
||||
hashers, key_ty, ..
|
||||
} => {
|
||||
let ty = metadata
|
||||
.resolve_type(key.id)
|
||||
.ok_or(StorageAddressError::TypeNotFound(key.id))?;
|
||||
.types()
|
||||
.resolve(*key_ty)
|
||||
.ok_or(MetadataError::TypeNotFound(*key_ty))?;
|
||||
|
||||
// If the key is a tuple, we encode each value to the corresponding tuple type.
|
||||
// If the key is not a tuple, encode a single value to the key type.
|
||||
@@ -164,7 +174,7 @@ where
|
||||
TypeDef::Tuple(tuple) => {
|
||||
either::Either::Left(tuple.fields.iter().map(|f| f.id))
|
||||
}
|
||||
_other => either::Either::Right(std::iter::once(key.id)),
|
||||
_other => either::Either::Right(std::iter::once(*key_ty)),
|
||||
};
|
||||
|
||||
if type_ids.len() != self.storage_entry_keys.len() {
|
||||
|
||||
@@ -9,7 +9,7 @@ use super::{
|
||||
|
||||
use crate::{
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
error::Error,
|
||||
error::{Error, MetadataError},
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
@@ -43,7 +43,11 @@ where
|
||||
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
|
||||
/// the pallet or storage entry in question do not exist at all).
|
||||
pub fn validate<Address: StorageAddress>(&self, address: &Address) -> Result<(), Error> {
|
||||
validate_storage_address(address, &self.client.metadata())
|
||||
let metadata = self.client.metadata();
|
||||
let pallet_metadata = metadata
|
||||
.pallet_by_name(address.pallet_name())
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?;
|
||||
validate_storage_address(address, pallet_metadata)
|
||||
}
|
||||
|
||||
/// Convert some storage address into the raw bytes that would be submitted to the node in order
|
||||
|
||||
@@ -5,15 +5,14 @@
|
||||
use super::storage_address::{StorageAddress, Yes};
|
||||
use crate::{
|
||||
client::OnlineClientT,
|
||||
error::Error,
|
||||
error::{Error, MetadataError},
|
||||
metadata::{DecodeWithMetadata, Metadata},
|
||||
rpc::types::{StorageData, StorageKey},
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use frame_metadata::v15::StorageEntryType;
|
||||
use scale_info::form::PortableForm;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType};
|
||||
|
||||
/// Query the runtime storage.
|
||||
#[derive(Derivative)]
|
||||
@@ -94,22 +93,21 @@ where
|
||||
{
|
||||
let client = self.clone();
|
||||
async move {
|
||||
let metadata = client.client.metadata();
|
||||
let (pallet, entry) =
|
||||
lookup_entry_details(address.pallet_name(), address.entry_name(), &metadata)?;
|
||||
|
||||
// Metadata validation checks whether the static address given
|
||||
// is likely to actually correspond to a real storage entry or not.
|
||||
// if not, it means static codegen doesn't line up with runtime
|
||||
// metadata.
|
||||
validate_storage_address(address, &client.client.metadata())?;
|
||||
validate_storage_address(address, pallet)?;
|
||||
|
||||
// Look up the return type ID to enable DecodeWithMetadata:
|
||||
let metadata = client.client.metadata();
|
||||
let lookup_bytes = super::utils::storage_address_bytes(address, &metadata)?;
|
||||
if let Some(data) = client.fetch_raw(&lookup_bytes).await? {
|
||||
let val = decode_storage_with_metadata::<Address::Target>(
|
||||
&mut &*data,
|
||||
address.pallet_name(),
|
||||
address.entry_name(),
|
||||
&metadata,
|
||||
)?;
|
||||
let val =
|
||||
decode_storage_with_metadata::<Address::Target>(&mut &*data, &metadata, entry)?;
|
||||
Ok(Some(val))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -128,18 +126,17 @@ where
|
||||
let client = self.clone();
|
||||
async move {
|
||||
let pallet_name = address.pallet_name();
|
||||
let storage_name = address.entry_name();
|
||||
let entry_name = address.entry_name();
|
||||
// Metadata validation happens via .fetch():
|
||||
if let Some(data) = client.fetch(address).await? {
|
||||
Ok(data)
|
||||
} else {
|
||||
let metadata = client.client.metadata();
|
||||
let (_pallet_metadata, storage_entry) =
|
||||
lookup_entry_details(pallet_name, entry_name, &metadata)?;
|
||||
|
||||
// We have to dig into metadata already, so no point using the optimised `decode_storage_with_metadata` call.
|
||||
let pallet_metadata = metadata.pallet(pallet_name)?;
|
||||
let storage_metadata = pallet_metadata.storage(storage_name)?;
|
||||
let return_ty_id = return_type_from_storage_entry_type(&storage_metadata.ty);
|
||||
let bytes = &mut &storage_metadata.default[..];
|
||||
let return_ty_id = return_type_from_storage_entry_type(storage_entry.entry_type());
|
||||
let bytes = &mut storage_entry.default_bytes();
|
||||
|
||||
let val = Address::Target::decode_with_metadata(bytes, return_ty_id, &metadata)?;
|
||||
Ok(val)
|
||||
@@ -209,19 +206,20 @@ where
|
||||
let client = self.clone();
|
||||
let block_hash = self.block_hash;
|
||||
async move {
|
||||
let metadata = client.client.metadata();
|
||||
let (pallet, entry) =
|
||||
lookup_entry_details(address.pallet_name(), address.entry_name(), &metadata)?;
|
||||
|
||||
// Metadata validation checks whether the static address given
|
||||
// is likely to actually correspond to a real storage entry or not.
|
||||
// if not, it means static codegen doesn't line up with runtime
|
||||
// metadata.
|
||||
validate_storage_address(&address, &client.client.metadata())?;
|
||||
|
||||
let metadata = client.client.metadata();
|
||||
validate_storage_address(&address, pallet)?;
|
||||
|
||||
// Look up the return type for flexible decoding. Do this once here to avoid
|
||||
// potentially doing it every iteration if we used `decode_storage_with_metadata`
|
||||
// in the iterator.
|
||||
let return_type_id =
|
||||
lookup_storage_return_type(&metadata, address.pallet_name(), address.entry_name())?;
|
||||
let return_type_id = return_type_from_storage_entry_type(entry.entry_type());
|
||||
|
||||
// The root pallet/entry bytes for this storage entry:
|
||||
let address_root_bytes = super::utils::storage_address_root_bytes(&address);
|
||||
@@ -309,68 +307,63 @@ where
|
||||
/// Validate a storage address against the metadata.
|
||||
pub(crate) fn validate_storage_address<Address: StorageAddress>(
|
||||
address: &Address,
|
||||
metadata: &Metadata,
|
||||
pallet: PalletMetadata<'_>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(hash) = address.validation_hash() {
|
||||
validate_storage(address.pallet_name(), address.entry_name(), hash, metadata)?;
|
||||
validate_storage(pallet, address.entry_name(), hash)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate a storage entry against the metadata.
|
||||
fn validate_storage(
|
||||
/// Return details about the given storage entry.
|
||||
fn lookup_entry_details<'a>(
|
||||
pallet_name: &str,
|
||||
storage_name: &str,
|
||||
hash: [u8; 32],
|
||||
metadata: &Metadata,
|
||||
) -> Result<(), Error> {
|
||||
let expected_hash = match metadata.storage_hash(pallet_name, storage_name) {
|
||||
Ok(hash) => hash,
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
match expected_hash == hash {
|
||||
true => Ok(()),
|
||||
false => Err(crate::error::MetadataError::IncompatibleStorageMetadata(
|
||||
pallet_name.into(),
|
||||
storage_name.into(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
entry_name: &str,
|
||||
metadata: &'a Metadata,
|
||||
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
|
||||
let pallet_metadata = metadata
|
||||
.pallet_by_name(pallet_name)
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(pallet_name.to_owned()))?;
|
||||
let storage_metadata = pallet_metadata
|
||||
.storage()
|
||||
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(pallet_name.to_owned()))?;
|
||||
let storage_entry = storage_metadata
|
||||
.entry_by_name(entry_name)
|
||||
.ok_or_else(|| MetadataError::StorageEntryNotFound(entry_name.to_owned()))?;
|
||||
Ok((pallet_metadata, storage_entry))
|
||||
}
|
||||
|
||||
/// look up a return type ID for some storage entry.
|
||||
fn lookup_storage_return_type(
|
||||
metadata: &Metadata,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
) -> Result<u32, Error> {
|
||||
let storage_entry_type = &metadata.pallet(pallet)?.storage(entry)?.ty;
|
||||
|
||||
Ok(return_type_from_storage_entry_type(storage_entry_type))
|
||||
/// Validate a storage entry against the metadata.
|
||||
fn validate_storage(
|
||||
pallet: PalletMetadata<'_>,
|
||||
storage_name: &str,
|
||||
hash: [u8; 32],
|
||||
) -> Result<(), Error> {
|
||||
let Some(expected_hash) = pallet.storage_hash(storage_name) else {
|
||||
return Err(MetadataError::IncompatibleCodegen.into())
|
||||
};
|
||||
if expected_hash != hash {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch the return type out of a [`StorageEntryType`].
|
||||
fn return_type_from_storage_entry_type(entry: &StorageEntryType<PortableForm>) -> u32 {
|
||||
fn return_type_from_storage_entry_type(entry: &StorageEntryType) -> u32 {
|
||||
match entry {
|
||||
StorageEntryType::Plain(ty) => ty.id,
|
||||
StorageEntryType::Map { value, .. } => value.id,
|
||||
StorageEntryType::Plain(ty) => *ty,
|
||||
StorageEntryType::Map { value_ty, .. } => *value_ty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given some bytes, a pallet and storage name, decode the response.
|
||||
fn decode_storage_with_metadata<T: DecodeWithMetadata>(
|
||||
bytes: &mut &[u8],
|
||||
pallet_name: &str,
|
||||
storage_entry: &str,
|
||||
metadata: &Metadata,
|
||||
storage_metadata: &StorageEntryMetadata,
|
||||
) -> Result<T, Error> {
|
||||
let ty = &metadata.pallet(pallet_name)?.storage(storage_entry)?.ty;
|
||||
|
||||
let id = match ty {
|
||||
StorageEntryType::Plain(ty) => ty.id,
|
||||
StorageEntryType::Map { value, .. } => value.id,
|
||||
};
|
||||
|
||||
let val = T::decode_with_metadata(bytes, id, metadata)?;
|
||||
let ty = storage_metadata.entry_type();
|
||||
let return_ty = return_type_from_storage_entry_type(ty);
|
||||
let val = T::decode_with_metadata(bytes, return_ty, metadata)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use sp_core_hashing::blake2_256;
|
||||
use crate::{
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, ExtrinsicParams, Hasher},
|
||||
error::Error,
|
||||
error::{Error, MetadataError},
|
||||
tx::{Signer as SignerT, TxPayload, TxProgress},
|
||||
utils::{Encoded, PhantomDataSendSync},
|
||||
};
|
||||
@@ -47,14 +47,16 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
Call: TxPayload,
|
||||
{
|
||||
if let Some(details) = call.validation_details() {
|
||||
let metadata = self.client.metadata();
|
||||
let expected_hash = metadata.call_hash(details.pallet_name, details.call_name)?;
|
||||
let expected_hash = self
|
||||
.client
|
||||
.metadata()
|
||||
.pallet_by_name(details.pallet_name)
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(details.pallet_name.to_owned()))?
|
||||
.call_hash(details.call_name)
|
||||
.ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?;
|
||||
|
||||
if details.hash != expected_hash {
|
||||
return Err(crate::metadata::MetadataError::IncompatibleCallMetadata(
|
||||
details.pallet_name.into(),
|
||||
details.call_name.into(),
|
||||
)
|
||||
.into());
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
//! This module contains the trait and types used to represent
|
||||
//! transactions that can be submitted.
|
||||
|
||||
use crate::{dynamic::Value, error::Error, metadata::Metadata};
|
||||
use crate::{
|
||||
dynamic::Value,
|
||||
error::{Error, MetadataError},
|
||||
metadata::Metadata,
|
||||
};
|
||||
use codec::Encode;
|
||||
use scale_encode::EncodeAsFields;
|
||||
use scale_value::{Composite, ValueDef, Variant};
|
||||
@@ -137,17 +141,21 @@ impl Payload<Composite<()>> {
|
||||
|
||||
impl<CallData: EncodeAsFields> TxPayload for Payload<CallData> {
|
||||
fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
|
||||
let pallet = metadata.pallet(&self.pallet_name)?;
|
||||
let call = pallet.call(&self.call_name)?;
|
||||
let pallet = metadata
|
||||
.pallet_by_name(&self.pallet_name)
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound((*self.pallet_name).to_owned()))?;
|
||||
let call = pallet
|
||||
.call_variant_by_name(&self.call_name)
|
||||
.ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?;
|
||||
|
||||
let pallet_index = pallet.index();
|
||||
let call_index = call.index();
|
||||
let call_index = call.index;
|
||||
|
||||
pallet_index.encode_to(out);
|
||||
call_index.encode_to(out);
|
||||
|
||||
self.call_data
|
||||
.encode_as_fields_to(call.fields(), metadata.types(), out)?;
|
||||
.encode_as_fields_to(&call.fields, metadata.types(), out)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{pair_signer, test_context, utils::node_runtime};
|
||||
use codec::Compact;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use codec::{Compact, Encode};
|
||||
use futures::StreamExt;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::blocks::BlocksClient;
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
// Check that we can subscribe to non-finalized blocks.
|
||||
#[tokio::test]
|
||||
@@ -103,21 +103,17 @@ async fn runtime_api_call() -> Result<(), subxt::Error> {
|
||||
let block = sub.next().await.unwrap()?;
|
||||
let rt = block.runtime_api().await?;
|
||||
|
||||
let (_, meta) = rt
|
||||
.call_raw::<(Compact<u32>, RuntimeMetadataPrefixed)>("Metadata_metadata", None)
|
||||
// get metadata via state_call.
|
||||
let (_, meta1) = rt
|
||||
.call_raw::<(Compact<u32>, Metadata)>("Metadata_metadata", None)
|
||||
.await?;
|
||||
let metadata_call = match meta.1 {
|
||||
frame_metadata::RuntimeMetadata::V14(metadata) => {
|
||||
subxt_metadata::metadata_v14_to_latest(metadata)
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V15(metadata) => metadata,
|
||||
_ => panic!("Metadata V14 or V15 unavailable"),
|
||||
};
|
||||
|
||||
// Compare the runtime API call against the `state_getMetadata`.
|
||||
let metadata = api.rpc().metadata_legacy(None).await?;
|
||||
let metadata = metadata.runtime_metadata();
|
||||
assert_eq!(&metadata_call, metadata);
|
||||
// get metadata via `state_getMetadata`.
|
||||
let meta2 = api.rpc().metadata_legacy(None).await?;
|
||||
|
||||
// They should be the same.
|
||||
assert_eq!(meta1.encode(), meta2.encode());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::{
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use sp_core::storage::well_known_keys;
|
||||
use sp_core::{sr25519::Pair as Sr25519Pair, Pair};
|
||||
use sp_keyring::AccountKeyring;
|
||||
@@ -21,6 +20,7 @@ use subxt::{
|
||||
tx::Signer,
|
||||
utils::AccountId32,
|
||||
};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
#[tokio::test]
|
||||
async fn insert_key() {
|
||||
@@ -420,27 +420,23 @@ async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rpc_state_call() {
|
||||
async fn rpc_state_call() -> Result<(), subxt::Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
// Call into the runtime of the chain to get the Metadata.
|
||||
let (_, meta) = api
|
||||
// get metadata via state_call.
|
||||
let (_, meta1) = api
|
||||
.rpc()
|
||||
.state_call::<(Compact<u32>, RuntimeMetadataPrefixed)>("Metadata_metadata", None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let metadata_call = match meta.1 {
|
||||
frame_metadata::RuntimeMetadata::V14(metadata) => {
|
||||
subxt_metadata::metadata_v14_to_latest(metadata)
|
||||
}
|
||||
frame_metadata::RuntimeMetadata::V15(metadata) => metadata,
|
||||
_ => panic!("Metadata V14 or V15 unavailable"),
|
||||
};
|
||||
// Compare the runtime API call against the `state_getMetadata`.
|
||||
let metadata = api.rpc().metadata_legacy(None).await.unwrap();
|
||||
let metadata = metadata.runtime_metadata();
|
||||
assert_eq!(&metadata_call, metadata);
|
||||
.state_call::<(Compact<u32>, Metadata)>("Metadata_metadata", None, None)
|
||||
.await?;
|
||||
|
||||
// get metadata via `state_getMetadata`.
|
||||
let meta2 = api.rpc().metadata_legacy(None).await?;
|
||||
|
||||
// They should be the same.
|
||||
assert_eq!(meta1.encode(), meta2.encode());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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 frame_metadata::{
|
||||
v15::{ExtrinsicMetadata, RuntimeMetadataV15},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use scale_info::{meta_type, IntoPortable, TypeInfo};
|
||||
use subxt_codegen::{CratePath, DerivesRegistry, RuntimeGenerator, TypeSubstitutes};
|
||||
|
||||
fn generate_runtime_interface_from_metadata(metadata: RuntimeMetadataPrefixed) -> String {
|
||||
// Generate a runtime interface from the provided metadata.
|
||||
let metadata = metadata
|
||||
.try_into()
|
||||
.expect("frame_metadata should be convertible into Metadata");
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
let item_mod = syn::parse_quote!(
|
||||
pub mod api {}
|
||||
);
|
||||
let crate_path = CratePath::default();
|
||||
let derives = DerivesRegistry::with_default_derives(&crate_path);
|
||||
let type_substitutes = TypeSubstitutes::with_default_substitutes(&crate_path);
|
||||
generator
|
||||
.generate_runtime(item_mod, derives, type_substitutes, crate_path, false)
|
||||
.expect("API generation must be valid")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn generate_runtime_interface_with_type_registry<F>(f: F) -> String
|
||||
where
|
||||
F: Fn(&mut scale_info::Registry),
|
||||
{
|
||||
#[derive(TypeInfo)]
|
||||
struct Runtime;
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeCall {}
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeEvent {}
|
||||
#[derive(TypeInfo)]
|
||||
pub enum DispatchError {}
|
||||
|
||||
// We need these types for codegen to work:
|
||||
let mut registry = scale_info::Registry::new();
|
||||
let ty = registry.register_type(&meta_type::<Runtime>());
|
||||
registry.register_type(&meta_type::<RuntimeCall>());
|
||||
registry.register_type(&meta_type::<RuntimeEvent>());
|
||||
registry.register_type(&meta_type::<DispatchError>());
|
||||
|
||||
// Allow custom types to be added for testing:
|
||||
f(&mut registry);
|
||||
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
}
|
||||
.into_portable(&mut registry);
|
||||
let metadata = RuntimeMetadataV15 {
|
||||
types: registry.into(),
|
||||
pallets: Vec::new(),
|
||||
extrinsic,
|
||||
ty,
|
||||
apis: vec![],
|
||||
};
|
||||
|
||||
let metadata = RuntimeMetadataPrefixed::from(metadata);
|
||||
generate_runtime_interface_from_metadata(metadata)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dupe_types_do_not_overwrite_each_other() {
|
||||
let interface = generate_runtime_interface_with_type_registry(|registry| {
|
||||
// Now we duplicate some types with same type info. We need two unique types here,
|
||||
// and can't just add one type to the registry twice, because the registry knows if
|
||||
// type IDs are the same.
|
||||
enum Foo {}
|
||||
impl TypeInfo for Foo {
|
||||
type Identity = Self;
|
||||
fn type_info() -> scale_info::Type {
|
||||
scale_info::Type::builder()
|
||||
.path(scale_info::Path::new("DuplicateType", "dupe_mod"))
|
||||
.variant(
|
||||
scale_info::build::Variants::new()
|
||||
.variant("FirstDupeTypeVariant", |builder| builder.index(0)),
|
||||
)
|
||||
}
|
||||
}
|
||||
enum Bar {}
|
||||
impl TypeInfo for Bar {
|
||||
type Identity = Self;
|
||||
fn type_info() -> scale_info::Type {
|
||||
scale_info::Type::builder()
|
||||
.path(scale_info::Path::new("DuplicateType", "dupe_mod"))
|
||||
.variant(
|
||||
scale_info::build::Variants::new()
|
||||
.variant("SecondDupeTypeVariant", |builder| builder.index(0)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
registry.register_type(&meta_type::<Foo>());
|
||||
registry.register_type(&meta_type::<Bar>());
|
||||
});
|
||||
|
||||
assert!(interface.contains("DuplicateType"));
|
||||
assert!(interface.contains("FirstDupeTypeVariant"));
|
||||
|
||||
assert!(interface.contains("DuplicateType2"));
|
||||
assert!(interface.contains("SecondDupeTypeVariant"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_types_overwrite_each_other() {
|
||||
let interface = generate_runtime_interface_with_type_registry(|registry| {
|
||||
// If we have two types mentioned in the registry that have generic params,
|
||||
// only one type will be output (the codegen assumes that the generic param will disambiguate)
|
||||
enum Foo {}
|
||||
impl TypeInfo for Foo {
|
||||
type Identity = Self;
|
||||
fn type_info() -> scale_info::Type {
|
||||
scale_info::Type::builder()
|
||||
.path(scale_info::Path::new("DuplicateType", "dupe_mod"))
|
||||
.type_params([scale_info::TypeParameter::new("T", Some(meta_type::<u8>()))])
|
||||
.variant(scale_info::build::Variants::new())
|
||||
}
|
||||
}
|
||||
enum Bar {}
|
||||
impl TypeInfo for Bar {
|
||||
type Identity = Self;
|
||||
fn type_info() -> scale_info::Type {
|
||||
scale_info::Type::builder()
|
||||
.path(scale_info::Path::new("DuplicateType", "dupe_mod"))
|
||||
.type_params([scale_info::TypeParameter::new("T", Some(meta_type::<u8>()))])
|
||||
.variant(scale_info::build::Variants::new())
|
||||
}
|
||||
}
|
||||
|
||||
registry.register_type(&meta_type::<Foo>());
|
||||
registry.register_type(&meta_type::<Bar>());
|
||||
});
|
||||
|
||||
assert!(interface.contains("DuplicateType"));
|
||||
// We do _not_ expect this to exist, since a generic is present on the type:
|
||||
assert!(!interface.contains("DuplicateType2"));
|
||||
}
|
||||
+16
-19
@@ -2,49 +2,46 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use codec::Decode;
|
||||
use regex::Regex;
|
||||
use subxt_codegen::{CratePath, DerivesRegistry, RuntimeGenerator, TypeSubstitutes};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
fn load_test_metadata() -> frame_metadata::RuntimeMetadataPrefixed {
|
||||
fn load_test_metadata() -> Metadata {
|
||||
let bytes = test_runtime::METADATA;
|
||||
codec::Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata")
|
||||
Metadata::decode(&mut &*bytes).expect("Cannot decode scale metadata")
|
||||
}
|
||||
|
||||
fn metadata_docs() -> Vec<String> {
|
||||
// Load the runtime metadata downloaded from a node via `test-runtime`.
|
||||
let meta = load_test_metadata();
|
||||
let metadata = match meta.1 {
|
||||
frame_metadata::RuntimeMetadata::V14(v14) => subxt_metadata::metadata_v14_to_latest(v14),
|
||||
frame_metadata::RuntimeMetadata::V15(v15) => v15,
|
||||
_ => panic!("Unsupported metadata version {:?}", meta.1),
|
||||
};
|
||||
let metadata = load_test_metadata();
|
||||
|
||||
// Inspect the metadata types and collect the documentation.
|
||||
let mut docs = Vec::new();
|
||||
for ty in &metadata.types.types {
|
||||
for ty in &metadata.types().types {
|
||||
docs.extend_from_slice(&ty.ty.docs);
|
||||
}
|
||||
|
||||
for pallet in metadata.pallets {
|
||||
if let Some(storage) = pallet.storage {
|
||||
for entry in storage.entries {
|
||||
docs.extend(entry.docs);
|
||||
for pallet in metadata.pallets() {
|
||||
if let Some(storage) = pallet.storage() {
|
||||
for entry in storage.entries() {
|
||||
docs.extend_from_slice(entry.docs());
|
||||
}
|
||||
}
|
||||
// Note: Calls, Events and Errors are deduced directly to
|
||||
// PortableTypes which are handled above.
|
||||
for constant in pallet.constants {
|
||||
docs.extend(constant.docs);
|
||||
for constant in pallet.constants() {
|
||||
docs.extend_from_slice(constant.docs());
|
||||
}
|
||||
}
|
||||
// Note: Extrinsics do not have associated documentation, but is implied by
|
||||
// associated Type.
|
||||
|
||||
// Inspect the runtime API types and collect the documentation.
|
||||
for api in metadata.apis {
|
||||
docs.extend(api.docs);
|
||||
for method in api.methods {
|
||||
docs.extend(method.docs);
|
||||
for api in metadata.runtime_api_traits() {
|
||||
docs.extend_from_slice(api.docs());
|
||||
for method in api.methods() {
|
||||
docs.extend_from_slice(method.docs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
#[allow(clippy::all)]
|
||||
mod polkadot;
|
||||
|
||||
mod codegen_documentation;
|
||||
mod codegen_tests;
|
||||
mod documentation;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -383,9 +383,11 @@ async fn constant_existential_deposit() {
|
||||
|
||||
// get and decode constant manually via metadata:
|
||||
let metadata = api.metadata();
|
||||
let balances_metadata = metadata.pallet("Balances").unwrap();
|
||||
let constant_metadata = balances_metadata.constant("ExistentialDeposit").unwrap();
|
||||
let existential_deposit = u128::decode(&mut &constant_metadata.value[..]).unwrap();
|
||||
let balances_metadata = metadata.pallet_by_name("Balances").unwrap();
|
||||
let constant_metadata = balances_metadata
|
||||
.constant_by_name("ExistentialDeposit")
|
||||
.unwrap();
|
||||
let existential_deposit = u128::decode(&mut constant_metadata.value()).unwrap();
|
||||
assert_eq!(existential_deposit, 100_000_000_000_000);
|
||||
|
||||
// constant address for API access:
|
||||
|
||||
@@ -69,8 +69,8 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
.await;
|
||||
assert_matches!(announce_validator, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
let details = err.details().unwrap();
|
||||
assert_eq!(details.pallet(), "Staking");
|
||||
assert_eq!(details.error(), "NotController");
|
||||
assert_eq!(details.pallet.name(), "Staking");
|
||||
assert_eq!(&details.variant.name, "NotController");
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -118,8 +118,8 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
|
||||
assert_matches!(nomination, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
let details = err.details().unwrap();
|
||||
assert_eq!(details.pallet(), "Staking");
|
||||
assert_eq!(details.error(), "NotController");
|
||||
assert_eq!(details.pallet.name(), "Staking");
|
||||
assert_eq!(&details.variant.name, "NotController");
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -164,8 +164,8 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
|
||||
assert_matches!(chill, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
let details = err.details().unwrap();
|
||||
assert_eq!(details.pallet(), "Staking");
|
||||
assert_eq!(details.error(), "NotController");
|
||||
assert_eq!(details.pallet.name(), "Staking");
|
||||
assert_eq!(&details.variant.name, "NotController");
|
||||
});
|
||||
|
||||
let is_chilled = api
|
||||
@@ -211,8 +211,8 @@ async fn tx_bond() -> Result<(), Error> {
|
||||
|
||||
assert_matches!(bond_again, Err(Error::Runtime(DispatchError::Module(err))) => {
|
||||
let details = err.details().unwrap();
|
||||
assert_eq!(details.pallet(), "Staking");
|
||||
assert_eq!(details.error(), "AlreadyBonded");
|
||||
assert_eq!(details.pallet.name(), "Staking");
|
||||
assert_eq!(&details.variant.name, "AlreadyBonded");
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{node_runtime, test_context, TestContext};
|
||||
use frame_metadata::{
|
||||
v15::{
|
||||
ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, PalletStorageMetadata,
|
||||
RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
},
|
||||
RuntimeMetadataPrefixed,
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, PalletStorageMetadata,
|
||||
RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
};
|
||||
use scale_info::{
|
||||
build::{Fields, Variants},
|
||||
@@ -16,13 +13,7 @@ use scale_info::{
|
||||
};
|
||||
use subxt::{Metadata, OfflineClient, SubstrateConfig};
|
||||
|
||||
async fn metadata_to_api(
|
||||
metadata: RuntimeMetadataV15,
|
||||
ctx: &TestContext,
|
||||
) -> OfflineClient<SubstrateConfig> {
|
||||
let prefixed = RuntimeMetadataPrefixed::from(metadata);
|
||||
let metadata = Metadata::try_from(prefixed).unwrap();
|
||||
|
||||
async fn metadata_to_api(metadata: Metadata, ctx: &TestContext) -> OfflineClient<SubstrateConfig> {
|
||||
OfflineClient::new(
|
||||
ctx.client().genesis_hash(),
|
||||
ctx.client().runtime_version(),
|
||||
@@ -30,56 +21,18 @@ async fn metadata_to_api(
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn full_metadata_check() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
// Runtime metadata is identical to the metadata used during API generation.
|
||||
assert!(node_runtime::validate_codegen(&api).is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let mut metadata = api.metadata().runtime_metadata().clone();
|
||||
metadata.pallets[0].name = "NewPallet".to_string();
|
||||
|
||||
let api = metadata_to_api(metadata, &ctx).await;
|
||||
assert_eq!(
|
||||
node_runtime::validate_codegen(&api)
|
||||
.expect_err("Validation should fail for incompatible metadata"),
|
||||
::subxt::error::MetadataError::IncompatibleMetadata
|
||||
);
|
||||
fn v15_to_metadata(v15: RuntimeMetadataV15) -> Metadata {
|
||||
let subxt_md: subxt_metadata::Metadata = v15.try_into().unwrap();
|
||||
subxt_md.into()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn constant_values_are_not_validated() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let deposit_addr = node_runtime::constants().balances().existential_deposit();
|
||||
|
||||
// Retrieve existential deposit to validate it and confirm that it's OK.
|
||||
assert!(api.constants().at(&deposit_addr).is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let mut metadata = api.metadata().runtime_metadata().clone();
|
||||
|
||||
let mut existential = metadata
|
||||
.pallets
|
||||
.iter_mut()
|
||||
.find(|pallet| pallet.name == "Balances")
|
||||
.expect("Metadata must contain Balances pallet")
|
||||
.constants
|
||||
.iter_mut()
|
||||
.find(|constant| constant.name == "ExistentialDeposit")
|
||||
.expect("ExistentialDeposit constant must be present");
|
||||
|
||||
// Modifying a constant value should not lead to an error:
|
||||
existential.value = vec![0u8; 32];
|
||||
|
||||
let api = metadata_to_api(metadata, &ctx).await;
|
||||
|
||||
assert!(node_runtime::validate_codegen(&api).is_ok());
|
||||
assert!(api.constants().at(&deposit_addr).is_ok());
|
||||
fn modified_metadata<F>(metadata: Metadata, f: F) -> Metadata
|
||||
where
|
||||
F: FnOnce(&mut RuntimeMetadataV15),
|
||||
{
|
||||
let mut metadata = RuntimeMetadataV15::from((*metadata).clone());
|
||||
f(&mut metadata);
|
||||
v15_to_metadata(metadata)
|
||||
}
|
||||
|
||||
fn default_pallet() -> PalletMetadata {
|
||||
@@ -95,7 +48,7 @@ fn default_pallet() -> PalletMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> RuntimeMetadataV15 {
|
||||
fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> Metadata {
|
||||
// Extrinsic needs to contain at least the generic type parameter "Call"
|
||||
// for the metadata to be valid.
|
||||
// The "Call" type from the metadata is used to decode extrinsics.
|
||||
@@ -120,7 +73,7 @@ fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> RuntimeMetadataV15 {
|
||||
SomeCall,
|
||||
}
|
||||
|
||||
RuntimeMetadataV15::new(
|
||||
v15_to_metadata(RuntimeMetadataV15::new(
|
||||
pallets,
|
||||
ExtrinsicMetadata {
|
||||
ty: meta_type::<ExtrinsicType<RuntimeCall>>(),
|
||||
@@ -129,7 +82,60 @@ fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> RuntimeMetadataV15 {
|
||||
},
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn full_metadata_check() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
// Runtime metadata is identical to the metadata used during API generation.
|
||||
assert!(node_runtime::validate_codegen(&api).is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let metadata = modified_metadata(api.metadata(), |md| {
|
||||
md.pallets[0].name = "NewPallet".to_string();
|
||||
});
|
||||
|
||||
let api = metadata_to_api(metadata, &ctx).await;
|
||||
assert_eq!(
|
||||
node_runtime::validate_codegen(&api)
|
||||
.expect_err("Validation should fail for incompatible metadata"),
|
||||
::subxt::error::MetadataError::IncompatibleCodegen
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn constant_values_are_not_validated() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let deposit_addr = node_runtime::constants().balances().existential_deposit();
|
||||
|
||||
// Retrieve existential deposit to validate it and confirm that it's OK.
|
||||
assert!(api.constants().at(&deposit_addr).is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let metadata = modified_metadata(api.metadata(), |md| {
|
||||
let mut existential = md
|
||||
.pallets
|
||||
.iter_mut()
|
||||
.find(|pallet| pallet.name == "Balances")
|
||||
.expect("Metadata must contain Balances pallet")
|
||||
.constants
|
||||
.iter_mut()
|
||||
.find(|constant| constant.name == "ExistentialDeposit")
|
||||
.expect("ExistentialDeposit constant must be present");
|
||||
|
||||
// Modifying a constant value should not lead to an error:
|
||||
existential.value = vec![0u8; 32];
|
||||
});
|
||||
|
||||
let api = metadata_to_api(metadata, &ctx).await;
|
||||
|
||||
assert!(node_runtime::validate_codegen(&api).is_ok());
|
||||
assert!(api.constants().at(&deposit_addr).is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadataPrefixed};
|
||||
use std::io::Read;
|
||||
use subxt_metadata::{metadata_v14_to_latest, retain_metadata_pallets};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
static TEST_DIR_PREFIX: &str = "subxt_generated_pallets_ui_tests_";
|
||||
static METADATA_FILE: &str = "../../artifacts/polkadot_metadata_full.scale";
|
||||
|
||||
pub struct PalletMetadataTestRunner {
|
||||
metadata: RuntimeMetadataV15,
|
||||
metadata: Metadata,
|
||||
index: usize,
|
||||
pallet_names: Option<Vec<String>>,
|
||||
}
|
||||
@@ -26,15 +25,9 @@ impl PalletMetadataTestRunner {
|
||||
file.read_to_end(&mut bytes)
|
||||
.expect("Failed to read metadata.scale file");
|
||||
|
||||
let meta: RuntimeMetadataPrefixed =
|
||||
Decode::decode(&mut &*bytes).expect("Cannot decode metadata bytes");
|
||||
|
||||
let metadata = match meta.1 {
|
||||
frame_metadata::RuntimeMetadata::V14(v14) => metadata_v14_to_latest(v14),
|
||||
frame_metadata::RuntimeMetadata::V15(v15) => v15,
|
||||
_ => panic!("Unsupported metadata version {:?}", meta.1),
|
||||
};
|
||||
let metadata = Metadata::decode(&mut &*bytes).expect("Cannot decode metadata bytes");
|
||||
let pallet_names = pallet_names.map(|v| v.iter().map(|e| e.to_string()).collect());
|
||||
|
||||
PalletMetadataTestRunner {
|
||||
metadata,
|
||||
index: 0,
|
||||
@@ -43,23 +36,20 @@ impl PalletMetadataTestRunner {
|
||||
}
|
||||
|
||||
pub fn path_to_next_ui_test(&mut self) -> Option<String> {
|
||||
let Some(pallet) = self.metadata.pallets.get(self.index) else {
|
||||
return None
|
||||
let pallet = match self.pallet_names.as_ref() {
|
||||
Some(names) => self.metadata.pallet_by_name(names.get(self.index)?)?,
|
||||
None => self.metadata.pallets().nth(self.index)?,
|
||||
};
|
||||
let test_name = &pallet.name;
|
||||
// Increment test index to avoid overlaps.
|
||||
|
||||
let test_name = pallet.name();
|
||||
|
||||
// Increment test index to point at the next pallet.
|
||||
let index = self.index;
|
||||
self.index += 1;
|
||||
// if a pallet filter is set (pallet_names is Some), return the next pallet if this pallet is not in the filter.
|
||||
if let Some(name_filter) = self.pallet_names.as_ref() {
|
||||
if !name_filter.contains(test_name) {
|
||||
return self.path_to_next_ui_test();
|
||||
}
|
||||
}
|
||||
|
||||
// Build custom metadata containing only this pallet.
|
||||
let mut metadata = self.metadata.clone();
|
||||
retain_metadata_pallets(&mut metadata, |pallet_filter| pallet_filter == pallet.name);
|
||||
metadata.retain(|pallet_filter| pallet_filter == pallet.name(), |_| true);
|
||||
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{index}"));
|
||||
@@ -75,8 +65,7 @@ impl PalletMetadataTestRunner {
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let metadata_prefixed: RuntimeMetadataPrefixed = metadata.into();
|
||||
let encoded_metadata = metadata_prefixed.encode();
|
||||
let encoded_metadata = metadata.encode();
|
||||
let rust_file = format!(
|
||||
r#"
|
||||
use subxt;
|
||||
@@ -102,18 +91,10 @@ impl PalletMetadataTestRunner {
|
||||
// are dropped too, to make sure that cleanup happens after tests are ran.
|
||||
impl Drop for PalletMetadataTestRunner {
|
||||
fn drop(&mut self) {
|
||||
for i in 0..self.index {
|
||||
if let Some(pallet) = self.metadata.pallets.get(self.index) {
|
||||
if let Some(name_filter) = self.pallet_names.as_ref() {
|
||||
if !name_filter.contains(&pallet.name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{i}"));
|
||||
std::fs::remove_dir_all(tmp_dir).expect("cannot cleanup temp files");
|
||||
};
|
||||
for idx in 0..self.index {
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{idx}"));
|
||||
let _ = std::fs::remove_dir_all(tmp_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+217
-171
@@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -37,7 +37,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -60,9 +60,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
checksum = "3f1e31e207a6b8fb791a38ea3105e6cb541f55e4d029902d3039a4ad07cc4105"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
@@ -97,7 +97,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -131,9 +131,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.1"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
@@ -228,8 +228,18 @@ version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"darling_core 0.14.4",
|
||||
"darling_macro 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
|
||||
dependencies = [
|
||||
"darling_core 0.20.1",
|
||||
"darling_macro 0.20.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -246,17 +256,42 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_core 0.14.4",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core 0.20.1",
|
||||
"quote",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
@@ -290,9 +325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common",
|
||||
@@ -492,9 +527,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.18"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21"
|
||||
checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@@ -663,9 +698,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.61"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||
checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -766,9 +801,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768"
|
||||
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
@@ -781,19 +816,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -852,9 +877,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec"
|
||||
version = "3.4.0"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac"
|
||||
checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitvec",
|
||||
@@ -876,47 +901,24 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.12"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
|
||||
checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.12"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||
checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -986,18 +988,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1038,15 +1040,6 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
@@ -1104,7 +1097,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1144,7 +1137,7 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b38741b2f78e4391b94eac6b102af0f6ea2b0f7fe65adb55d7f4004f507854db"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.14.4",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1171,7 +1164,7 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd983cf0a9effd76138554ead18a6de542d1af175ac12fd5e91836c5c0268082"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.14.4",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1180,9 +1173,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info"
|
||||
version = "2.5.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97"
|
||||
checksum = "b569c32c806ec3abdf3b5869fb8bf1e0d275a7c1c9b0b05603d9464632649edf"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"cfg-if",
|
||||
@@ -1194,9 +1187,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info-derive"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e"
|
||||
checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -1237,12 +1230,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
@@ -1255,9 +1242,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.8.2"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -1268,9 +1255,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
|
||||
checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -1284,22 +1271,22 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1334,16 +1321,16 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.7"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
@@ -1365,12 +1352,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.9"
|
||||
@@ -1404,7 +1385,7 @@ checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"sp-std",
|
||||
@@ -1437,9 +1418,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "subxt"
|
||||
@@ -1456,7 +1437,6 @@ dependencies = [
|
||||
"impl-serde",
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
"parking_lot",
|
||||
"primitive-types",
|
||||
"scale-bits",
|
||||
"scale-decode",
|
||||
@@ -1476,7 +1456,6 @@ dependencies = [
|
||||
name = "subxt-codegen"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"frame-metadata",
|
||||
"heck",
|
||||
"hex",
|
||||
@@ -1486,7 +1465,7 @@ dependencies = [
|
||||
"quote",
|
||||
"scale-info",
|
||||
"subxt-metadata",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.16",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -1495,10 +1474,10 @@ dependencies = [
|
||||
name = "subxt-macro"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.20.1",
|
||||
"proc-macro-error",
|
||||
"subxt-codegen",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1509,6 +1488,7 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core-hashing",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1524,9 +1504,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1556,7 +1536,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1571,9 +1551,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.27.0"
|
||||
version = "1.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
|
||||
checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -1583,18 +1563,18 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
|
||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1610,9 +1590,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.7"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
|
||||
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -1625,15 +1605,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
||||
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.8"
|
||||
version = "0.19.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
||||
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -1666,14 +1646,14 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
@@ -1713,7 +1693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"rand",
|
||||
"static_assertions",
|
||||
]
|
||||
@@ -1772,9 +1752,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1782,24 +1762,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.84"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.16",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.34"
|
||||
version = "0.4.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
||||
checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -1809,9 +1789,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.84"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1819,28 +1799,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.84"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.16",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.84"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.34"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db36fc0f9fb209e88fb3642590ae0205bb5a56216dabd963ba15879fe53a30b"
|
||||
checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
@@ -1852,9 +1832,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.34"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0734759ae6b3b1717d661fe4f016efcfb9828f5edb4520c18eaee05af3b43be9"
|
||||
checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1873,9 +1853,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.61"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
||||
checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -1928,13 +1908,13 @@ version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1943,7 +1923,16 @@ version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1952,13 +1941,28 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1967,36 +1971,72 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -2004,10 +2044,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.4.1"
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user