mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Introduce Metadata type (#974)
* WIP new Metadata type * Finish basic Metadata impl inc hashing and validation * remove caching from metadata; can add that higher up * remove caches * update retain to use Metadata * clippy fixes * update codegen to use Metadata * clippy * WIP fixing subxt lib * WIP fixing tests, rebuild artifacts, fix OrderedMap::retain * get --all-targets compiling * move DispatchError type lookup back to being optional * cargo clippy * fix docs * re-use VariantIndex to get variants * add docs and enforce docs on metadata crate * fix docs * add test and fix docs * cargo fmt * address review comments * update lockfiles * ExactSizeIter so we can ask for len() of things (and hopefully soon is_empty()
This commit is contained in:
Generated
+101
-177
@@ -194,7 +194,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -243,9 +243,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"
|
||||
@@ -298,7 +298,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]]
|
||||
@@ -353,9 +353,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bounded-collections"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3888522b497857eb606bf51695988dba7096941822c1bcf676e3a929a9ae7a0"
|
||||
checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
@@ -371,9 +371,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
|
||||
[[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"
|
||||
@@ -431,9 +431,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
|
||||
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
@@ -442,15 +442,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
|
||||
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
|
||||
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
@@ -502,7 +502,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -529,16 +529,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.2"
|
||||
@@ -781,50 +771,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.4"
|
||||
@@ -870,7 +816,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -892,7 +838,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core 0.20.1",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -943,9 +889,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",
|
||||
@@ -1183,7 +1129,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1344,9 +1290,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",
|
||||
@@ -1462,7 +1408,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]]
|
||||
@@ -1566,12 +1512,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1663,7 +1608,7 @@ dependencies = [
|
||||
"subxt",
|
||||
"subxt-codegen",
|
||||
"subxt-metadata",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
"test-runtime",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -1674,9 +1619,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",
|
||||
@@ -1712,9 +1657,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",
|
||||
]
|
||||
@@ -1815,9 +1760,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",
|
||||
]
|
||||
@@ -1830,15 +1775,15 @@ 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"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
|
||||
|
||||
[[package]]
|
||||
name = "libsecp256k1"
|
||||
@@ -1888,15 +1833,6 @@ dependencies = [
|
||||
"libsecp256k1-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
@@ -1905,9 +1841,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"
|
||||
@@ -2185,9 +2121,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[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 0.7.2",
|
||||
"bitvec",
|
||||
@@ -2260,7 +2196,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]]
|
||||
@@ -2271,22 +2207,22 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[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]]
|
||||
@@ -2546,18 +2482,18 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
@@ -2577,9 +2513,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"
|
||||
@@ -2616,9 +2552,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[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",
|
||||
@@ -2638,7 +2574,7 @@ dependencies = [
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.7",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@@ -2672,7 +2608,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]]
|
||||
@@ -2849,12 +2785,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
@@ -2894,9 +2824,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",
|
||||
@@ -2907,9 +2837,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",
|
||||
@@ -2938,7 +2868,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2998,16 +2928,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",
|
||||
]
|
||||
|
||||
@@ -3147,7 +3077,7 @@ checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
"digest 0.10.6",
|
||||
"digest 0.10.7",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
"sp-std",
|
||||
@@ -3550,7 +3480,6 @@ dependencies = [
|
||||
"impl-serde",
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
"parking_lot",
|
||||
"primitive-types",
|
||||
"scale-bits",
|
||||
"scale-decode",
|
||||
@@ -3588,7 +3517,7 @@ dependencies = [
|
||||
"subxt",
|
||||
"subxt-codegen",
|
||||
"subxt-metadata",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -3607,7 +3536,7 @@ dependencies = [
|
||||
"quote",
|
||||
"scale-info",
|
||||
"subxt-metadata",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -3630,7 +3559,7 @@ dependencies = [
|
||||
"darling 0.20.1",
|
||||
"proc-macro-error",
|
||||
"subxt-codegen",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3643,6 +3572,7 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core-hashing",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3658,9 +3588,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",
|
||||
@@ -3726,7 +3656,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3808,7 +3738,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3839,15 +3769,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",
|
||||
@@ -3880,14 +3810,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",
|
||||
"valuable",
|
||||
@@ -4010,7 +3940,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 0.8.5",
|
||||
"static_assertions",
|
||||
]
|
||||
@@ -4066,12 +3996,6 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@@ -4170,9 +4094,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",
|
||||
@@ -4180,24 +4104,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",
|
||||
@@ -4207,9 +4131,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",
|
||||
@@ -4217,22 +4141,22 @@ 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 = "wasmi"
|
||||
@@ -4390,7 +4314,7 @@ dependencies = [
|
||||
"memoffset 0.6.5",
|
||||
"paste",
|
||||
"rand 0.8.5",
|
||||
"rustix 0.36.13",
|
||||
"rustix 0.36.14",
|
||||
"wasmtime-asm-macros",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-jit-debug",
|
||||
@@ -4411,9 +4335,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",
|
||||
@@ -4683,5 +4607,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
+1
-2
@@ -48,9 +48,9 @@ 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-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.58"
|
||||
quote = "1.0.27"
|
||||
regex = "1.8.1"
|
||||
@@ -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"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
use crate::utils::type_description::print_type_description;
|
||||
use crate::utils::type_example::print_type_examples;
|
||||
use crate::utils::with_indent;
|
||||
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 color_eyre::eyre::eyre;
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
|
||||
use scale_info::form::PortableForm;
|
||||
use scale_info::{PortableRegistry, Type, TypeDef, TypeDefVariant};
|
||||
use scale_value::{Composite, ValueDef};
|
||||
|
||||
use subxt::tx;
|
||||
use subxt::utils::H256;
|
||||
use subxt::{config::SubstrateConfig, Metadata, OfflineClient};
|
||||
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 {
|
||||
@@ -28,13 +29,13 @@ pub struct CallsSubcommand {
|
||||
pub(crate) fn explore_calls(
|
||||
command: CallsSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: &PalletMetadata<PortableForm>,
|
||||
pallet_metadata: PalletMetadata,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name.as_str();
|
||||
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.runtime_metadata().types)?;
|
||||
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 {
|
||||
@@ -55,14 +56,9 @@ pub(crate) fn explore_calls(
|
||||
|
||||
// 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.runtime_metadata().types)?;
|
||||
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.runtime_metadata().types,
|
||||
"SCALE_VALUE",
|
||||
)?;
|
||||
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")?;
|
||||
@@ -102,22 +98,19 @@ fn print_available_calls(pallet_calls: &TypeDefVariant<PortableForm>, pallet_nam
|
||||
}
|
||||
|
||||
fn get_calls_enum_type<'a>(
|
||||
pallet: &'a frame_metadata::v15::PalletMetadata<PortableForm>,
|
||||
pallet: PalletMetadata,
|
||||
registry: &'a PortableRegistry,
|
||||
) -> color_eyre::Result<(&'a TypeDefVariant<PortableForm>, &'a Type<PortableForm>)> {
|
||||
let calls = pallet
|
||||
.calls
|
||||
.as_ref()
|
||||
.ok_or(eyre!("The \"{}\" pallet has no calls.", pallet.name))?;
|
||||
let call_ty = pallet
|
||||
.call_ty_id()
|
||||
.ok_or(eyre!("The \"{}\" pallet has no calls.", pallet.name()))?;
|
||||
let calls_enum_type = registry
|
||||
.resolve(calls.ty.id)
|
||||
.ok_or(eyre!("calls type with id {} not found.", calls.ty.id))?;
|
||||
.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 calls_enum_type_def = match &calls_enum_type.type_def {
|
||||
TypeDef::Variant(variant) => variant,
|
||||
_ => {
|
||||
return Err(eyre!("calls type is not a variant"));
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
use crate::utils::type_description::print_type_description;
|
||||
use crate::utils::{print_docs_with_indent, with_indent};
|
||||
use clap::Args;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
use subxt::metadata::{types::PalletMetadata, Metadata};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
use subxt::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 {
|
||||
@@ -20,9 +15,9 @@ pub struct ConstantsSubcommand {
|
||||
pub(crate) fn explore_constants(
|
||||
command: ConstantsSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: &PalletMetadata<PortableForm>,
|
||||
pallet_metadata: PalletMetadata,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name.as_str();
|
||||
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}", );
|
||||
@@ -30,7 +25,7 @@ pub(crate) fn explore_constants(
|
||||
};
|
||||
|
||||
// if specified constant is wrong, show user the constants to choose from (but this time as an error):
|
||||
let Some(constant) = pallet_metadata.constants.iter().find(|constant| constant.name.to_lowercase() == constant_name.to_lowercase()) else {
|
||||
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}");
|
||||
@@ -39,13 +34,13 @@ pub(crate) fn explore_constants(
|
||||
|
||||
// docs
|
||||
let mut output = String::new();
|
||||
let doc_string = print_docs_with_indent(&constant.docs, 4);
|
||||
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.id, metadata.types())?;
|
||||
let mut type_description = print_type_description(&constant.ty(), metadata.types())?;
|
||||
type_description = with_indent(type_description, 4);
|
||||
write!(
|
||||
output,
|
||||
@@ -53,11 +48,8 @@ pub(crate) fn explore_constants(
|
||||
)?;
|
||||
|
||||
// value
|
||||
let scale_val = scale_value::scale::decode_as_type(
|
||||
&mut &constant.value[..],
|
||||
constant.ty.id,
|
||||
metadata.types(),
|
||||
)?;
|
||||
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 {}",
|
||||
@@ -68,15 +60,12 @@ pub(crate) fn explore_constants(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_available_constants(
|
||||
pallet_metadata: &PalletMetadata<PortableForm>,
|
||||
pallet_name: &str,
|
||||
) -> String {
|
||||
if pallet_metadata.constants.is_empty() {
|
||||
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.iter().map(|c| &c.name).collect();
|
||||
let mut strings: Vec<_> = pallet_metadata.constants().map(|c| c.name()).collect();
|
||||
strings.sort();
|
||||
for constant in strings {
|
||||
output.push_str("\n ");
|
||||
|
||||
@@ -7,10 +7,6 @@ use std::write;
|
||||
|
||||
use codec::Decode;
|
||||
use color_eyre::eyre::eyre;
|
||||
use frame_metadata::v15::RuntimeMetadataV15;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
|
||||
use syn::__private::str;
|
||||
|
||||
use crate::commands::explore::calls::{explore_calls, CallsSubcommand};
|
||||
use crate::commands::explore::constants::{explore_constants, ConstantsSubcommand};
|
||||
@@ -89,24 +85,23 @@ pub enum PalletSubcommand {
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
// get the metadata
|
||||
let bytes = opts.file_or_url.fetch().await?;
|
||||
let metadata_prefixed = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
let metadata = Metadata::try_from(metadata_prefixed)?;
|
||||
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.runtime_metadata());
|
||||
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.runtime_metadata().pallets.iter().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.runtime_metadata())));
|
||||
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 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}")?;
|
||||
@@ -132,12 +127,12 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_available_pallets(metadata_v15: &RuntimeMetadataV15) -> String {
|
||||
if metadata_v15.pallets.is_empty() {
|
||||
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_v15.pallets.iter().map(|p| &p.name).collect();
|
||||
let mut strings: Vec<_> = metadata.pallets().map(|p| p.name()).collect();
|
||||
strings.sort();
|
||||
for pallet in strings {
|
||||
write!(output, "\n {}", pallet).unwrap();
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
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};
|
||||
use clap::Args;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use std::fmt::Write;
|
||||
use std::write;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use frame_metadata::v15::{PalletMetadata, PalletStorageMetadata, StorageEntryType};
|
||||
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
use subxt::OnlineClient;
|
||||
use subxt::{config::SubstrateConfig, Metadata};
|
||||
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 {
|
||||
@@ -24,14 +26,14 @@ pub struct StorageSubcommand {
|
||||
pub(crate) async fn explore_storage(
|
||||
command: StorageSubcommand,
|
||||
metadata: &Metadata,
|
||||
pallet_metadata: &PalletMetadata<PortableForm>,
|
||||
pallet_metadata: PalletMetadata<'_>,
|
||||
custom_online_client_url: Option<String>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let pallet_name = pallet_metadata.name.as_str();
|
||||
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 {
|
||||
let Some(storage_metadata) = pallet_metadata.storage() else {
|
||||
println!("The \"{pallet_name}\" pallet has no storage entries.");
|
||||
return Ok(());
|
||||
};
|
||||
@@ -44,15 +46,17 @@ pub(crate) async fn explore_storage(
|
||||
};
|
||||
|
||||
// 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.iter().find(|entry| entry.name.to_lowercase() == entry_name.to_lowercase()) else {
|
||||
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.ty {
|
||||
StorageEntryType::Plain(value) => (value.id, None),
|
||||
StorageEntryType::Map { value, key, .. } => (value.id, Some(key.id)),
|
||||
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:
|
||||
@@ -66,7 +70,7 @@ pub(crate) async fn explore_storage(
|
||||
)?;
|
||||
}
|
||||
|
||||
let docs_string = print_docs_with_indent(&storage.docs, 4);
|
||||
let docs_string = print_docs_with_indent(storage.docs(), 4);
|
||||
if !docs_string.is_empty() {
|
||||
write!(output, "Description:\n{docs_string}")?;
|
||||
}
|
||||
@@ -156,17 +160,17 @@ pub(crate) async fn explore_storage(
|
||||
}
|
||||
|
||||
fn print_available_storage_entries(
|
||||
storage_metadata: &PalletStorageMetadata<PortableForm>,
|
||||
storage_metadata: &StorageMetadata,
|
||||
pallet_name: &str,
|
||||
) -> String {
|
||||
if storage_metadata.entries.is_empty() {
|
||||
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.iter().map(|s| &s.name).collect();
|
||||
let mut strings: Vec<_> = storage_metadata.entries().map(|s| s.name()).collect();
|
||||
strings.sort();
|
||||
for entry in strings {
|
||||
write!(output, "\n {}", entry).unwrap();
|
||||
|
||||
@@ -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};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
/// Download metadata from a substrate node, for use with `subxt` codegen.
|
||||
#[derive(Debug, ClapParser)]
|
||||
@@ -36,19 +36,19 @@ pub struct Opts {
|
||||
|
||||
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[..])?;
|
||||
|
||||
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() {
|
||||
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
|
||||
));
|
||||
}
|
||||
};
|
||||
// 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),
|
||||
@@ -57,8 +57,16 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
Some(apis) => Box::new(|name| apis.iter().any(|p| &**p == name)),
|
||||
None => Box::new(|_| true),
|
||||
};
|
||||
retain_metadata(&mut metadata_v15, retain_pallets_fn, retain_runtime_apis_fn);
|
||||
metadata = metadata_v15.into();
|
||||
md.retain(retain_pallets_fn, retain_runtime_apis_fn);
|
||||
|
||||
// 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() {
|
||||
@@ -82,3 +90,9 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
enum Version {
|
||||
V14,
|
||||
V15,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
+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 ] )* })
|
||||
|
||||
+53
-64
@@ -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,7 +22,6 @@ 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};
|
||||
@@ -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,15 +172,8 @@ impl RuntimeGenerator {
|
||||
/// Panics if the runtime metadata version is not supported.
|
||||
///
|
||||
/// Supported versions: v14 and v15.
|
||||
pub fn new(metadata: RuntimeMetadataPrefixed) -> Self {
|
||||
let mut 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 }
|
||||
}
|
||||
|
||||
@@ -190,9 +181,9 @@ impl RuntimeGenerator {
|
||||
/// 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 RuntimeMetadataV15) {
|
||||
fn ensure_unique_type_paths(metadata: &mut Metadata) {
|
||||
let mut visited_path_counts = HashMap::<Vec<String>, usize>::new();
|
||||
for ty in &mut metadata.types.types {
|
||||
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;
|
||||
@@ -249,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,
|
||||
@@ -300,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(),
|
||||
@@ -311,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<_>>();
|
||||
@@ -326,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,
|
||||
@@ -357,7 +347,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -366,7 +355,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -390,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),
|
||||
@@ -410,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),
|
||||
@@ -430,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 {
|
||||
@@ -448,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 {
|
||||
@@ -466,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),
|
||||
@@ -486,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();
|
||||
@@ -632,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.7.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;
|
||||
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,128 +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(api: &RuntimeApiMetadata<PortableForm>, type_ids: &mut HashSet<u32>) {
|
||||
for method in &api.methods {
|
||||
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.id);
|
||||
type_ids.insert(input.ty);
|
||||
}
|
||||
type_ids.insert(method.output.id);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,26 +127,26 @@ 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
|
||||
@@ -189,7 +181,7 @@ 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<F, G>(
|
||||
metadata: &mut RuntimeMetadataV15,
|
||||
metadata: &mut Metadata,
|
||||
mut pallets_filter: F,
|
||||
mut runtime_apis_filter: G,
|
||||
) where
|
||||
@@ -213,11 +205,20 @@ pub fn retain_metadata<F, G>(
|
||||
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 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.
|
||||
@@ -244,31 +245,31 @@ pub fn retain_metadata<F, G>(
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -278,16 +279,19 @@ 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(
|
||||
&mut metadata,
|
||||
|pallet_name| pallet_name == pallet.name,
|
||||
|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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,16 +300,19 @@ mod tests {
|
||||
let metadata_cache = load_metadata();
|
||||
|
||||
// Retain one runtime API at a time ensuring the test does not panic.
|
||||
for runtime_api in &metadata_cache.apis {
|
||||
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,
|
||||
|runtime_api_name| runtime_api_name == runtime_api.name(),
|
||||
);
|
||||
|
||||
assert_eq!(metadata.apis.len(), 1);
|
||||
assert_eq!(metadata.apis.get(0).unwrap().name, runtime_api.name);
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,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
@@ -14,9 +14,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
|
||||
@@ -36,12 +37,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),
|
||||
@@ -160,9 +161,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 {
|
||||
@@ -173,6 +171,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]
|
||||
|
||||
@@ -11,6 +11,9 @@ use subxt_codegen::{CratePath, DerivesRegistry, RuntimeGenerator, TypeSubstitute
|
||||
|
||||
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 {}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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};
|
||||
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,27 +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(
|
||||
&mut metadata,
|
||||
|pallet_filter| pallet_filter == pallet.name,
|
||||
|_| true,
|
||||
);
|
||||
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}"));
|
||||
@@ -79,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;
|
||||
@@ -106,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