diff --git a/Cargo.lock b/Cargo.lock index bef0e21..c5b8148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,35 +76,11 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -117,36 +93,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -612,9 +588,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", @@ -770,9 +746,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d077619e9c237a5d1875166f5e8033e8f6bff0c96f8caf81e1c2d7738c431bf" +checksum = "32ed0a820ed50891d36358e997d27741a6142e382242df40ff01c89bcdcc7a2b" dependencies = [ "log", "parity-scale-codec", @@ -798,12 +774,6 @@ dependencies = [ "semver 0.6.0", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byte-slice-cast" version = "1.2.2" @@ -812,9 +782,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder" @@ -830,18 +800,18 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -886,23 +856,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-targets", -] - [[package]] name = "clap" -version = "4.5.7" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -910,9 +868,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -922,9 +880,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -934,9 +892,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codespan-reporting" @@ -945,14 +903,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "common" @@ -978,15 +936,15 @@ checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] name = "console" -version = "0.15.8" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width", - "windows-sys", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -1015,6 +973,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1027,12 +1005,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" version = "0.2.12" @@ -1149,45 +1121,60 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.123" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8194f089b6da4751d6c1da1ef37c17255df51f9346cdb160f8b096562ae4a85c" +checksum = "050906babad73f9b32a91cecc3063ff1e2235226dd2367dd839fd6fbc941c68a" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.123" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8df9a089caae66634d754672d5f909395f30f38af6ff19366980d8a8b57501" +checksum = "875d58f2ac56025a775b91a424515b5adf1e68205765f2c90e6dd81e269ae004" dependencies = [ "cc", "codespan-reporting", - "once_cell", "proc-macro2", "quote", "scratch", "syn 2.0.96", ] +[[package]] +name = "cxxbridge-cmd" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c3062da294104183e1c34ea9887941d4d8c74f6195ce9fbb430ac4b5290ede" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "cxxbridge-flags" -version = "1.0.123" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25290be4751803672a70b98c68b51c1e7d0a640ab5a4377f240f9d2e70054cd1" +checksum = "f4b358173a166833ddef75fe468579f71727c789b8082d4cc77c38d08f656c59" [[package]] name = "cxxbridge-macro" -version = "1.0.123" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cb317cb13604b4752416783bb25070381c36e844743e4146b7f8e55de7d140" +checksum = "b9531217f3b5f7728244d2b7312bc6660f6b3e4cdbc118f4f1fbce48cb401a0f" dependencies = [ "proc-macro2", "quote", + "rustversion", "syn 2.0.96", ] @@ -1221,17 +1208,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive-syn-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive-syn-parse" version = "0.2.0" @@ -1316,7 +1292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7" dependencies = [ "common-path", - "derive-syn-parse 0.2.0", + "derive-syn-parse", "once_cell", "proc-macro2", "quote", @@ -1462,9 +1438,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "enum-ordinalize" @@ -1500,12 +1476,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1538,9 +1514,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -1573,21 +1549,21 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys", + "libredox", + "windows-sys 0.59.0", ] [[package]] name = "finality-grandpa" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +checksum = "b4f8f43dc520133541781ec03a8cab158ae8b7f7169cdf22e9050aa6cf0fbdfc" dependencies = [ "either", "futures", @@ -1617,6 +1593,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "fortuples" version = "0.9.1" @@ -1671,7 +1653,7 @@ dependencies = [ "sp-io 35.0.0", "sp-runtime 36.0.0", "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-tracing 17.0.0", + "sp-tracing 17.0.1", ] [[package]] @@ -1688,9 +1670,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "18.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daaf440c68eb2c3d88e5760fe8c7af3f9fee9181fab6c2f2c4e7cc48dcc40bb8" +checksum = "835a2e736d544b98dab966b4b9541f15af416288a86c3738fdd67bd9fbc4696e" dependencies = [ "cfg-if", "parity-scale-codec", @@ -1734,7 +1716,7 @@ dependencies = [ "sp-staking", "sp-state-machine 0.40.0", "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-tracing 17.0.0", + "sp-tracing 17.0.1", "sp-weights 31.0.0", "static_assertions", "tt-call", @@ -1748,7 +1730,7 @@ checksum = "3d8eaf3bb331b98427158733e221bd6fb79e9f213da55b305e159dc023d41fd2" dependencies = [ "Inflector", "cfg-expr", - "derive-syn-parse 0.2.0", + "derive-syn-parse", "expander", "frame-support-procedural-tools", "itertools 0.10.5", @@ -1987,6 +1969,12 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "group" version = "0.13.0" @@ -2110,29 +2098,6 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2182,29 +2147,29 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -2231,9 +2196,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -2270,22 +2235,13 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "k256" version = "0.13.4" @@ -2321,6 +2277,17 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall", +] + [[package]] name = "libsecp256k1" version = "0.7.1" @@ -2371,27 +2338,27 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" dependencies = [ "cc", ] [[package]] name = "linregress" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" +checksum = "a9eda9dcf4f2a99787827661f312ac3219292549c2ee992bf9a6248ffb066bf7" dependencies = [ "nalgebra", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -2411,9 +2378,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "macro_magic" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e03844fc635e92f3a0067e25fa4bf3e3dbf3f2927bf3aa01bb7bc8f1c428949d" +checksum = "cc33f9f0351468d26fbc53d9ce00a096c8522ecb42f19b50f34f2c422f76d21d" dependencies = [ "macro_magic_core", "macro_magic_macros", @@ -2423,12 +2390,12 @@ dependencies = [ [[package]] name = "macro_magic_core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468155613a44cfd825f1fb0ffa532b018253920d404e6fca1e8d43155198a46d" +checksum = "1687dc887e42f352865a393acae7cf79d98fab6351cde1f58e9e057da89bf150" dependencies = [ "const-random", - "derive-syn-parse 0.1.5", + "derive-syn-parse", "macro_magic_core_macros", "proc-macro2", "quote", @@ -2437,9 +2404,9 @@ dependencies = [ [[package]] name = "macro_magic_core_macros" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea73aa640dc01d62a590d48c0c3521ed739d53b27f919b25c3551e233481654" +checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", @@ -2448,24 +2415,15 @@ dependencies = [ [[package]] name = "macro_magic_macros" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef9d79ae96aaba821963320eb2b6e34d17df1e5a83d8a1985c29cc5be59577b3" +checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", "syn 2.0.96", ] -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matchers" version = "0.1.0" @@ -2477,9 +2435,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" dependencies = [ "autocfg", "rawpointer", @@ -2523,13 +2481,12 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.6" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" dependencies = [ "approx", "matrixmultiply", - "nalgebra-macros", "num-complex", "num-rational", "num-traits", @@ -2537,17 +2494,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "nalgebra-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2614,6 +2560,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ + "num-bigint", "num-integer", "num-traits", ] @@ -2790,29 +2737,31 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "c9fde3d0718baf5bc92f577d652001da0f8d54cd03a7974e118d04fc888dc23d" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", "bytes", + "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "581c837bb6b9541ce7faa9377c20616e4fb7650f6b0f68bc93c827ee504fb7b3" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -2839,7 +2788,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", "windows-targets", ] @@ -2895,9 +2844,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "platforms" @@ -3221,6 +3170,7 @@ dependencies = [ "pvq-primitives", "scale-info", "tracing", + "tracing-subscriber", ] [[package]] @@ -3245,11 +3195,14 @@ dependencies = [ name = "pvq-extension-procedural" version = "0.1.0" dependencies = [ - "Inflector", + "parity-scale-codec", "proc-macro-crate", "proc-macro2", + "pvq-extension", "quote", + "scale-info", "syn 2.0.96", + "trybuild", "twox-hash", ] @@ -3283,7 +3236,7 @@ dependencies = [ "pvq-primitives", "scale-info", "tracing", - "tracing-subscriber 0.3.18", + "tracing-subscriber", ] [[package]] @@ -3363,15 +3316,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.1" @@ -3522,7 +3466,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3539,9 +3483,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "safe_arch" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" dependencies = [ "bytemuck", ] @@ -3619,9 +3563,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" [[package]] name = "sec1" @@ -3803,9 +3747,9 @@ dependencies = [ [[package]] name = "simba" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" dependencies = [ "approx", "num-complex", @@ -4335,7 +4279,7 @@ dependencies = [ "sp-runtime-interface 27.0.0", "sp-state-machine 0.40.0", "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-tracing 17.0.0", + "sp-tracing 17.0.1", "sp-trie 34.0.0", "tracing", "tracing-core", @@ -4377,7 +4321,7 @@ dependencies = [ name = "sp-metadata-ir" version = "0.6.0" dependencies = [ - "frame-metadata 18.0.0", + "frame-metadata 19.0.0", "parity-scale-codec", "scale-info", ] @@ -4414,12 +4358,11 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "13.0.0" +version = "13.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f5a17a0a11de029a8b811cb6e8b32ce7e02183cc04a3e965c383246798c416" +checksum = "81478b3740b357fa0ea10fcdc1ee02ebae7734e50f80342c4743476d9f78eeea" dependencies = [ "backtrace", - "lazy_static", "regex", ] @@ -4528,8 +4471,8 @@ dependencies = [ "sp-runtime-interface-proc-macro 18.0.0", "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-storage 21.0.0", - "sp-tracing 17.0.0", - "sp-wasm-interface 21.0.0", + "sp-tracing 17.0.1", + "sp-wasm-interface 21.0.1", "static_assertions", ] @@ -4634,7 +4577,7 @@ dependencies = [ "smallvec", "sp-core 32.0.0", "sp-externalities 0.28.0", - "sp-panic-handler 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-panic-handler 13.0.1", "sp-trie 34.0.0", "thiserror", "tracing", @@ -4712,7 +4655,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.18", + "tracing-subscriber", ] [[package]] @@ -4723,19 +4666,19 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.18", + "tracing-subscriber", ] [[package]] name = "sp-tracing" -version = "17.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b3decf116db9f1dfaf1f1597096b043d0e12c952d3bcdc018c6d6b77deec7e" +checksum = "cf641a1d17268c8fcfdb8e0fa51a79c2d4222f4cfda5f3944dbdbc384dced8d5" dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -4873,10 +4816,11 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "21.0.0" +version = "21.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b04b919e150b4736d85089d49327eab65507deb1485eec929af69daa2278eb3" +checksum = "b066baa6d57951600b14ffe1243f54c47f9c23dd89c262e17ca00ae8dca58be9" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -4961,9 +4905,9 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros 0.26.4", ] @@ -5031,7 +4975,7 @@ dependencies = [ "parity-wasm", "polkavm-linker", "sp-maybe-compressed-blob", - "strum 0.26.2", + "strum 0.26.3", "tempfile", "toml", "walkdir", @@ -5072,16 +5016,23 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-triple" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" + [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -5256,17 +5207,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -5278,45 +5218,13 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers 0.0.1", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-serde", -] - [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "matchers 0.1.0", + "matchers", "nu-ansi-term", "once_cell", "regex", @@ -5326,7 +5234,7 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] @@ -5350,6 +5258,21 @@ dependencies = [ "hash-db", ] +[[package]] +name = "trybuild" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ae08be68c056db96f0e6c6dd820727cca756ced9e1f4cc7fdd20e2a55e23898" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + [[package]] name = "tt-call" version = "1.0.9" @@ -5421,9 +5344,15 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode-xid" @@ -5489,60 +5418,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.96", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.96", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - [[package]] name = "wasm-opt" version = "0.116.1" @@ -5585,9 +5460,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.24" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a040b111774ab63a19ef46bbc149398ab372b4ccdcfd719e9814dbd7dfd76c8" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" dependencies = [ "bytemuck", "safe_arch", @@ -5615,7 +5490,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -5625,28 +5500,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-core" +name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -5660,51 +5535,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -5794,9 +5669,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/poc/guests/Cargo.lock b/poc/guests/Cargo.lock index c67c570..b3242a2 100644 --- a/poc/guests/Cargo.lock +++ b/poc/guests/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -29,9 +29,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "cfg-if" @@ -39,6 +39,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -56,37 +76,37 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "indexmap" -version = "2.3.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -106,26 +126,28 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "c9fde3d0718baf5bc92f577d652001da0f8d54cd03a7974e118d04fc888dc23d" dependencies = [ "arrayvec", "byte-slice-cast", + "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "581c837bb6b9541ce7faa9377c20616e4fb7650f6b0f68bc93c827ee504fb7b3" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -178,7 +200,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.63", + "syn", ] [[package]] @@ -186,23 +208,23 @@ name = "polkavm-derive-impl-macro" version = "0.21.0" dependencies = [ "polkavm-derive-impl", - "syn 2.0.63", + "syn", ] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -224,23 +246,23 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.63", + "syn", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -250,9 +272,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -261,9 +283,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "scale-info" @@ -286,25 +314,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.63", + "syn", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -319,9 +336,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", @@ -330,15 +347,21 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/poc/runtime/src/pvq.rs b/poc/runtime/src/pvq.rs index ad16b97..bff69ae 100644 --- a/poc/runtime/src/pvq.rs +++ b/poc/runtime/src/pvq.rs @@ -6,7 +6,7 @@ use frame::prelude::*; use pvq_extension::metadata::Metadata; pub use pvq_primitives::PvqResult; -use pvq_extension::{impl_extensions, ExtensionsExecutor, InvokeSource}; +use pvq_extension::{extensions_impl, ExtensionsExecutor, InvokeSource}; decl_runtime_apis! { pub trait PvqApi { fn execute_query(query: Vec, input: Vec) -> PvqResult; @@ -14,48 +14,41 @@ decl_runtime_apis! { } } -// extension_core impls -pub struct ExtensionImpl; - -impl pvq_extension_core::Config for ExtensionImpl { - type ExtensionId = u64; -} - -// extension_fungibles impls -impl pvq_extension_fungibles::Config for ExtensionImpl { - type AccountId = [u8; 32]; - type Balance = crate::interface::Balance; - type AssetId = crate::interface::AssetId; -} -impl_extensions! { - impl pvq_extension_core::ExtensionCore for ExtensionImpl { - type Config = ExtensionImpl; - fn has_extension(id: ::ExtensionId) -> bool { - matches!(id, pvq_extension_core::EXTENSION_ID | pvq_extension_fungibles::EXTENSION_ID) +#[extensions_impl] +pub mod extensions { + #[extensions_impl::impl_struct] + pub struct ExtensionImpl; + + #[extensions_impl::extension] + impl pvq_extension_core::extension::ExtensionCore for ExtensionImpl { + type ExtensionId = u64; + fn has_extension(id: Self::ExtensionId) -> bool { + id == pvq_extension_core::extension::extension_id() + || id == pvq_extension_fungibles::extension::extension_id() } } - impl pvq_extension_fungibles::ExtensionFungibles for ExtensionImpl { - type Config = ExtensionImpl; - fn balance( - asset: ::AssetId, - who: ::AccountId, - ) -> ::Balance { + #[extensions_impl::extension] + impl pvq_extension_fungibles::extension::ExtensionFungibles for ExtensionImpl { + type AccountId = [u8; 32]; + type Balance = crate::interface::Balance; + type AssetId = crate::interface::AssetId; + fn balance(asset: Self::AssetId, who: Self::AccountId) -> Self::Balance { crate::Assets::balance(asset, crate::interface::AccountId::from(who)) } - fn total_supply(asset: ::AssetId) -> ::Balance { + fn total_supply(asset: Self::AssetId) -> Self::Balance { crate::Assets::total_supply(asset) } } } pub fn execute_query(query: &[u8], input: &[u8]) -> PvqResult { - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - executor.execute_method(query, input) + let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); + executor.execute_method(query, input, 0) } pub fn metadata() -> Metadata { - ExtensionImpl::metadata() + extensions::metadata() } #[cfg(test)] diff --git a/pvq-executor/src/lib.rs b/pvq-executor/src/lib.rs index 1a31711..565c646 100644 --- a/pvq-executor/src/lib.rs +++ b/pvq-executor/src/lib.rs @@ -3,6 +3,7 @@ extern crate alloc; pub use alloc::vec::Vec; +use polkavm::ModuleConfig; pub use polkavm::{Caller, Config, Engine, Linker, Module, ProgramBlob}; pub trait PvqExecutorContext { @@ -57,35 +58,28 @@ impl PvqExecutor { } } - pub fn execute(&mut self, raw_blob: &[u8], input: &[u8]) -> Result, PvqExecutorError> { - let blob = ProgramBlob::parse(raw_blob.into()).map_err(polkavm::Error::from)?; - let module = Module::from_blob(&self.engine, &Default::default(), blob)?; + pub fn execute( + &mut self, + program: &[u8], + args: &[u8], + _gas_limit: u64, + ) -> Result, PvqExecutorError> { + let blob = ProgramBlob::parse(program.into()).map_err(polkavm::Error::from)?; + + // TODO: make this configurable + let mut module_config = ModuleConfig::new(); + module_config.set_aux_data_size(args.len() as u32); + + let module = Module::from_blob(&self.engine, &module_config, blob)?; let instance_pre = self.linker.instantiate_pre(&module)?; let mut instance = instance_pre.instantiate()?; - let input_ptr = if !input.is_empty() { - // First sbrk call to get the start of the heap - let start_ptr = instance - .sbrk(0) - .expect("should not fail because we don't allocate") - .expect("should not fail because we don't allocate"); - // Second sbrk call to check the allocation doesn't exceed the heap limit - instance - .sbrk(input.len() as u32) - .map_err(|_| PvqExecutorError::MemoryAllocationError)? - .ok_or(PvqExecutorError::MemoryAllocationError)?; - // Args are passed via guest's heap - instance.write_memory(start_ptr, input)?; - start_ptr - } else { - 0 - }; - tracing::info!("(passing args): input_ptr: {}, input_len: {:?}", input_ptr, input.len()); + instance.write_memory(module.memory_map().aux_data_address(), args)?; let res = instance.call_typed_and_get_result::( self.context.data(), "main", - (input_ptr, input.len() as u32), + (module.memory_map().aux_data_address(), args.len() as u32), )?; let res_size = (res >> 32) as u32; let res_ptr = (res & 0xffffffff) as u32; diff --git a/pvq-extension-core/src/lib.rs b/pvq-extension-core/src/lib.rs index 908fc39..4a5c039 100644 --- a/pvq-extension-core/src/lib.rs +++ b/pvq-extension-core/src/lib.rs @@ -1,15 +1,13 @@ #![cfg_attr(not(feature = "std"), no_std)] -use parity_scale_codec::Encode; -use pvq_extension::decl_extensions; -pub trait Config { - type ExtensionId: Encode; -} +use pvq_extension::extension_decl; -decl_extensions! { +#[extension_decl] +pub mod extension { + #[extension_decl::extension] pub trait ExtensionCore { - type Config: Config; - fn has_extension(id: ::ExtensionId) -> bool; + type ExtensionId; + fn has_extension(id: Self::ExtensionId) -> bool; // crypto functions // fn blake2_64(data: Vec) -> [u8; 8]; // fn blake2_128(data: Vec) -> [u8; 16]; diff --git a/pvq-extension-fungibles/src/lib.rs b/pvq-extension-fungibles/src/lib.rs index bb10148..2936bd9 100644 --- a/pvq-extension-fungibles/src/lib.rs +++ b/pvq-extension-fungibles/src/lib.rs @@ -1,22 +1,14 @@ #![cfg_attr(not(feature = "std"), no_std)] -use parity_scale_codec::{Codec, Encode}; -use pvq_extension::decl_extensions; +use pvq_extension::extension_decl; -pub trait Config { - type AssetId: Codec; - type AccountId: Codec; - type Balance: Codec; -} -decl_extensions! { +#[extension_decl] +pub mod extension { + #[extension_decl::extension] pub trait ExtensionFungibles { - // fungibles::Inspect (not extensive) - // fn total_inssuance(asset: AssetIdFor) -> BalanceFor; - // fn minimum_balance(asset: AssetIdFor) -> BalanceFor; - type Config: Config; - fn total_supply(asset: ::AssetId) -> ::Balance; - fn balance(asset: ::AssetId, who: ::AccountId) -> ::Balance; - // fungibles::InspectEnumerable - // fn asset_ids() -> Vec>; - // fn account_balances(who: AccountIdFor) -> Vec<(AssetIdFor, BalanceFor)>; + type AssetId; + type Balance; + type AccountId; + fn total_supply(asset: Self::AssetId) -> Self::Balance; + fn balance(asset: Self::AssetId, who: Self::AccountId) -> Self::Balance; } } diff --git a/pvq-extension/Cargo.toml b/pvq-extension/Cargo.toml index d87f9a0..f441f89 100644 --- a/pvq-extension/Cargo.toml +++ b/pvq-extension/Cargo.toml @@ -17,6 +17,9 @@ parity-scale-codec = { workspace = true } scale-info = { workspace = true } pvq-primitives = { workspace = true } +[dev-dependencies] +tracing-subscriber = { workspace = true } + [features] default = ["std"] std = [ diff --git a/pvq-extension/procedural/Cargo.toml b/pvq-extension/procedural/Cargo.toml index db0e6b2..651eee9 100644 --- a/pvq-extension/procedural/Cargo.toml +++ b/pvq-extension/procedural/Cargo.toml @@ -14,5 +14,10 @@ quote = { workspace = true } syn = { workspace = true } proc-macro2 = { workspace = true } proc-macro-crate = { workspace = true } -Inflector = { workspace = true } twox-hash = "1.6.3" + +[dev-dependencies] +pvq-extension = { workspace = true } +scale-info = { workspace = true } +parity-scale-codec = { workspace = true } +trybuild = "1.0" diff --git a/pvq-extension/procedural/examples/proc_main/extension_decl.rs b/pvq-extension/procedural/examples/proc_main/extension_decl.rs new file mode 100644 index 0000000..9e23136 --- /dev/null +++ b/pvq-extension/procedural/examples/proc_main/extension_decl.rs @@ -0,0 +1,30 @@ +use pvq_extension_procedural::extension_decl; + +#[extension_decl] +pub mod extension_without_associated_type { + #[extension_decl::extension] + pub trait ExtensionWithoutAssociatedType { + fn test_fn(); + } +} + +#[extension_decl] +pub mod extension_core { + #[extension_decl::extension] + pub trait ExtensionCore { + type ExtensionId; + fn has_extension(id: Self::ExtensionId) -> bool; + } +} + +#[extension_decl] +pub mod extension_fungibles { + #[extension_decl::extension] + pub trait ExtensionFungibles { + type AssetId; + type AccountId; + type Balance; + fn total_supply(asset: Self::AssetId) -> Self::Balance; + fn balance(asset: Self::AssetId, who: Self::AccountId) -> Self::Balance; + } +} diff --git a/pvq-extension/procedural/examples/proc_main/extension_impl.rs b/pvq-extension/procedural/examples/proc_main/extension_impl.rs new file mode 100644 index 0000000..56afc12 --- /dev/null +++ b/pvq-extension/procedural/examples/proc_main/extension_impl.rs @@ -0,0 +1,30 @@ +use pvq_extension_procedural::extensions_impl; + +#[extensions_impl] +mod extensions_impl { + use crate::extension_decl::{extension_core, extension_fungibles}; + + #[extensions_impl::impl_struct] + pub struct ExtensionsImpl; + + #[extensions_impl::extension] + impl extension_core::ExtensionCore for ExtensionsImpl { + type ExtensionId = u64; + fn has_extension(id: u64) -> bool { + matches!(id, 0 | 1) + } + } + + #[extensions_impl::extension] + impl extension_fungibles::ExtensionFungibles for ExtensionsImpl { + type AssetId = u32; + type AccountId = [u8; 32]; + type Balance = u64; + fn total_supply(asset: u32) -> u64 { + 200 + } + fn balance(asset: u32, who: [u8; 32]) -> u64 { + 100 + } + } +} diff --git a/pvq-extension/procedural/examples/proc_main/main.rs b/pvq-extension/procedural/examples/proc_main/main.rs new file mode 100644 index 0000000..fd26d34 --- /dev/null +++ b/pvq-extension/procedural/examples/proc_main/main.rs @@ -0,0 +1,3 @@ +pub(crate) mod extension_decl; +pub(crate) mod extension_impl; +fn main() {} diff --git a/pvq-extension/procedural/src/decl_extensions.rs b/pvq-extension/procedural/src/decl_extensions.rs deleted file mode 100644 index 4034c6a..0000000 --- a/pvq-extension/procedural/src/decl_extensions.rs +++ /dev/null @@ -1,234 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use std::hash::{Hash, Hasher}; -use syn::{ - parse_macro_input, parse_quote, parse_str, punctuated::Punctuated, spanned::Spanned, token::Comma, Error, ExprCall, - Field, FnArg, Ident, ItemEnum, ItemImpl, ItemTrait, Pat, Result, TraitItem, Type, Variant, -}; - -use crate::utils::{generate_crate_access, generate_mod_name_for_trait}; - -pub fn decl_extensions_impl(input: TokenStream) -> TokenStream { - let item_trait = parse_macro_input!(input as ItemTrait); - decl_extension_inner(&item_trait) - .unwrap_or_else(|e| e.to_compile_error()) - .into() -} - -pub fn decl_extension_inner(item_trait: &ItemTrait) -> Result { - let mut item_trait = item_trait.clone(); - // Generate a separate module for the extension - let mod_name = generate_mod_name_for_trait(&item_trait.ident); - - // Add super trait ExtensionId and ExtensionMetadata to the trait's where clause - add_super_trait(&mut item_trait)?; - - // TODO: If trait has associated type, we assume it has a single associated type called `Config` - let has_config = item_trait - .items - .iter() - .any(|item| matches!(item, syn::TraitItem::Type(_))); - - // Extract methods from the trait - let methods = methods(&item_trait.items)?; - - let call_enum_def = call_enum_def(&item_trait.ident, &methods)?; - let call_data_dispatchable_impl = impl_dispatchable(&item_trait.ident, &methods)?; - let call_data_extension_id_impl = impl_extension_id(&item_trait.ident, &item_trait.items)?; - let call_data_metadata_impl = impl_call_metadata(&item_trait.ident)?; - let extension_runtime_metadata = crate::runtime_metadata::generate_decl_metadata(&item_trait, has_config)?; - - let expanded = quote! { - #item_trait - #[doc(hidden)] - #[allow(dead_code)] - pub mod #mod_name { - pub use super::*; - #call_enum_def - #call_data_dispatchable_impl - #call_data_extension_id_impl - #call_data_metadata_impl - #extension_runtime_metadata - } - pub use #mod_name::*; - }; - Ok(expanded) -} - -fn add_super_trait(item_trait: &mut ItemTrait) -> Result<()> { - let pvq_extension = generate_crate_access("pvq-extension")?; - // item_trait.supertraits.push(parse_quote!(#pvq_extension::ExtensionId)); - item_trait - .supertraits - .push(parse_quote!(#pvq_extension::ExtensionImplMetadata)); - Ok(()) -} - -#[derive(Clone)] -struct Method { - /// Function name - pub name: Ident, - /// Information on args: `(name, type)` - pub args: Vec<(Ident, Box)>, -} - -fn call_enum_def(trait_ident: &Ident, methods: &[Method]) -> Result { - let mut variants = Punctuated::::new(); - for method in methods { - let name = &method.name; - let mut args = Punctuated::::new(); - for (name, ty) in &method.args { - let ty = replace_self_to_impl(ty)?; - args.push(parse_quote! { - #name: #ty - }); - } - variants.push(parse_quote! { - #[allow(non_camel_case_types)] - #name { - #args - } - }); - } - // Add phantom data - variants.push(parse_quote!( - #[doc(hidden)] - __Phantom(core::marker::PhantomData) - )); - Ok(parse_quote!( - #[derive(parity_scale_codec::Decode)] - pub enum Call { - #variants - } - )) -} - -fn impl_dispatchable(trait_ident: &Ident, methods: &[Method]) -> Result { - let pvq_extension = generate_crate_access("pvq-extension")?; - let mut pats = Vec::::new(); - for method in methods { - let name = &method.name; - let mut args = Punctuated::::new(); - for (ident, _ty) in &method.args { - args.push(parse_quote! { - #ident - }); - } - pats.push(parse_quote! { - Self::#name { - #args - } - }); - } - - let mut method_calls = Vec::::new(); - for method in methods { - let name = &method.name; - let mut args = Punctuated::::new(); - for (ident, _ty) in &method.args { - args.push(parse_quote! { - #ident - }); - } - method_calls.push({ - parse_quote! { - Impl::#name(#args) - } - }); - } - - Ok(parse_quote! { - impl #pvq_extension::Dispatchable for Call { - fn dispatch(self) -> Result, #pvq_extension::DispatchError> { - match self { - #( #pats => Ok(#method_calls.encode()),)* - Self::__Phantom(_) => unreachable!(), - } - } - } - }) -} - -fn impl_extension_id(trait_ident: &Ident, trait_items: &[TraitItem]) -> Result { - let pvq_extension = generate_crate_access("pvq-extension")?; - - let extension_id = calculate_hash(trait_ident, trait_items); - Ok(quote! { - // Defining an trait for extension_id is useful for generic usage - impl #pvq_extension::ExtensionId for Call { - const EXTENSION_ID: #pvq_extension::ExtensionIdTy = #extension_id; - } - // This one is for easier access, since impl doesn't contribute to the extension_id calculation - pub const EXTENSION_ID: #pvq_extension::ExtensionIdTy = #extension_id; - }) -} - -// Delegate the metadata generation to the trait implementation -fn impl_call_metadata(trait_ident: &Ident) -> Result { - let pvq_extension = generate_crate_access("pvq-extension")?; - Ok(parse_quote! { - impl #pvq_extension::CallMetadata for Call { - fn call_metadata() -> #pvq_extension::metadata::ExtensionMetadata { - Impl::extension_metadata(::EXTENSION_ID) - } - } - }) -} - -// helper functions -fn methods(trait_items: &[TraitItem]) -> Result> { - let mut methods = vec![]; - for item in trait_items { - if let TraitItem::Fn(method) = item { - let method_name = &method.sig.ident; - let mut method_args = vec![]; - for arg in method.sig.inputs.iter() { - let arg = if let FnArg::Typed(arg) = arg { - arg - } else { - unreachable!("every argument should be typed instead of receiver(self)") - }; - let arg_ident = if let Pat::Ident(pat) = &*arg.pat { - pat.ident.clone() - } else { - let msg = "Invalid call, argument must be ident"; - return Err(Error::new(arg.pat.span(), msg)); - }; - method_args.push((arg_ident, arg.ty.clone())) - } - methods.push(Method { - name: method_name.clone(), - args: method_args, - }); - } - } - Ok(methods) -} - -// TODO: refine this to make it more stable -fn replace_self_to_impl(ty: &Type) -> Result> { - let ty_str = quote!(#ty).to_string(); - - let modified_ty_str = ty_str.replace("Self", "Impl"); - - let modified_ty = parse_str(&modified_ty_str)?; - - Ok(Box::new(modified_ty)) -} - -// TODO: currently we only hash on trait ident and function names, -fn calculate_hash(trait_ident: &Ident, trait_items: &[TraitItem]) -> u64 { - let mut hasher = twox_hash::XxHash64::default(); - // reduce the chance of hash collision - "pvq-ext$".hash(&mut hasher); - trait_ident.hash(&mut hasher); - for trait_item in trait_items { - if let TraitItem::Fn(method) = trait_item { - // reduce the chance of hash collision - "@".hash(&mut hasher); - method.sig.ident.hash(&mut hasher); - } - } - hasher.finish() -} diff --git a/pvq-extension/procedural/src/extension_decl/expand/extension.rs b/pvq-extension/procedural/src/extension_decl/expand/extension.rs new file mode 100644 index 0000000..ae44dcf --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/expand/extension.rs @@ -0,0 +1,154 @@ +use super::helper; +use crate::extension_decl::parse::extension::ExtensionFunction; +use crate::extension_decl::Def; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +/// Replace Self::SomeType with Impl::SomeType +fn replace_self_to_impl(ty: &syn::Type) -> Box { + let ty_str = quote!(#ty).to_string(); + + let modified_ty_str = ty_str.replace("Self", "Impl"); + + let modified_ty = + syn::parse_str(&modified_ty_str).expect("The replace with Impl::SomeType should not break the syntax"); + + Box::new(modified_ty) +} + +pub fn expand_extension(def: &mut Def) -> TokenStream2 { + let pvq_extension = &def.pvq_extension; + let parity_scale_codec = &def.parity_scale_codec; + // Set the trait name based on module_name + let trait_ident = &def.extension.name; + + // Add super trait ExtensionId and ExtensionMetadata to the trait's where clause + // helper::add_super_trait(&mut item_trait)?; + + // Generate the functions enum definition + let functions_enum = expand_functions_enum(parity_scale_codec, trait_ident, &def.extension.functions); + + // Generate the dispatchable implementation + let functions_impl_dispatchable = + impl_dispatchable_for_functions(pvq_extension, parity_scale_codec, trait_ident, &def.extension.functions); + + // Generate the extension ID implementation + let extension_id_expanded = expand_extension_id(pvq_extension, trait_ident, &def.extension.functions); + + // let extension_runtime_metadata = crate::runtime_metadata::generate_decl_metadata(&item_trait, view_fns.has_config)?; + + // Combine all the generated code + let expanded = quote! { + #functions_enum + #functions_impl_dispatchable + #extension_id_expanded + // #extension_runtime_metadata + }; + + expanded +} + +fn expand_functions_enum( + parity_scale_codec: &syn::Path, + trait_ident: &syn::Ident, + functions: &[ExtensionFunction], +) -> syn::ItemEnum { + let mut variants = syn::punctuated::Punctuated::::new(); + + for function in functions { + let name = &function.name; + let mut inputs = syn::punctuated::Punctuated::::new(); + + for (name, ty) in &function.inputs { + let ty = replace_self_to_impl(ty); + inputs.push(syn::parse_quote! { + #name: #ty + }); + } + + variants.push(syn::parse_quote! { + #name { + #inputs + } + }); + } + + // Add phantom data + variants.push(syn::parse_quote!( + #[doc(hidden)] + __marker(core::marker::PhantomData) + )); + syn::parse_quote!( + #[derive(#parity_scale_codec::Encode, #parity_scale_codec::Decode)] + #[allow(non_camel_case_types)] + pub enum Functions { + #variants + } + ) +} + +fn impl_dispatchable_for_functions( + pvq_extension: &syn::Path, + parity_scale_codec: &syn::Path, + trait_ident: &syn::Ident, + functions: &[ExtensionFunction], +) -> syn::ItemImpl { + let mut pats = Vec::::new(); + + for function in functions { + let name = &function.name; + let mut inputs = syn::punctuated::Punctuated::::new(); + + for (ident, _ty) in &function.inputs { + inputs.push(ident.clone()); + } + + pats.push(syn::parse_quote! { + Self::#name { + #inputs + } + }); + } + + let mut method_calls = Vec::::new(); + + for function in functions { + let name = &function.name; + let mut inputs = syn::punctuated::Punctuated::::new(); + + for (ident, _ty) in &function.inputs { + inputs.push(ident.clone()); + } + + method_calls.push(syn::parse_quote! { + Impl::#name(#inputs) + }); + } + + syn::parse_quote! { + impl #pvq_extension::Dispatchable for Functions { + fn dispatch(self) -> Result, #pvq_extension::DispatchError> { + match self { + #( #pats => Ok(#parity_scale_codec::Encode::encode(&#method_calls)),)* + Self::__marker(_) => Err(#pvq_extension::DispatchError::PhantomData), + } + } + } + } +} + +fn expand_extension_id( + pvq_extension: &syn::Path, + trait_ident: &syn::Ident, + functions: &[ExtensionFunction], +) -> TokenStream2 { + let extension_id = helper::calculate_hash(trait_ident, functions); + quote::quote! { + impl #pvq_extension::ExtensionId for Functions { + const EXTENSION_ID: #pvq_extension::ExtensionIdTy = #extension_id; + } + pub fn extension_id() -> #pvq_extension::ExtensionIdTy { + #extension_id + } + } +} diff --git a/pvq-extension/procedural/src/extension_decl/expand/helper.rs b/pvq-extension/procedural/src/extension_decl/expand/helper.rs new file mode 100644 index 0000000..38e9cd5 --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/expand/helper.rs @@ -0,0 +1,17 @@ +use crate::extension_decl::parse::extension::ExtensionFunction; +use std::hash::{Hash, Hasher}; +use twox_hash::XxHash64; + +// Calculate hash for extension ID +pub fn calculate_hash(trait_ident: &syn::Ident, functions: &[ExtensionFunction]) -> u64 { + let mut hasher = XxHash64::default(); + // reduce the chance of hash collision + "pvq-ext$".hash(&mut hasher); + trait_ident.hash(&mut hasher); + for function in functions { + // reduce the chance of hash collision + "@".hash(&mut hasher); + function.name.hash(&mut hasher); + } + hasher.finish() +} diff --git a/pvq-extension/procedural/src/extension_decl/expand/metadata.rs b/pvq-extension/procedural/src/extension_decl/expand/metadata.rs new file mode 100644 index 0000000..ae9c1c7 --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/expand/metadata.rs @@ -0,0 +1,84 @@ +use crate::extension_decl::Def; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::visit_mut::VisitMut; + +/// Generate the runtime metadata of the provided extension trait. +pub fn expand_metadata(def: &Def) -> TokenStream2 { + let pvq_extension = &def.pvq_extension; + let scale_info = &def.scale_info; + + let mut functions = Vec::new(); + + let mut replacer = AssociatedTypeReplacer; + + for function in &def.extension.functions { + let mut inputs = Vec::new(); + for (name, ty) in &function.inputs { + let name = name.to_string(); + let mut ty = ty.clone(); + + // Replace Self::AssociatedType with Impl::AssociatedType + replacer.visit_type_mut(&mut ty); + + inputs.push(quote!( + #pvq_extension::metadata::FunctionParamMetadata { + name: #name, + ty: #scale_info::meta_type::<#ty>(), + } + )); + } + + let output = match &function.output { + syn::ReturnType::Default => quote!(#scale_info::meta_type::<()>()), + syn::ReturnType::Type(_, ty) => { + let mut ty = ty.clone(); + replacer.visit_type_mut(&mut ty); + quote!(#scale_info::meta_type::<#ty>()) + } + }; + + let function_name = function.name.to_string(); + + functions.push(quote!( + #pvq_extension::metadata::FunctionMetadata { + name: #function_name, + inputs: #scale_info::prelude::vec![ #( #inputs, )* ], + output: #output, + } + )); + } + + let trait_ident = &def.extension.name; + let trait_name = trait_ident.to_string(); + if def.extension.types.is_empty() { + quote!( + pub fn metadata () -> #pvq_extension::metadata::ExtensionMetadata { + #pvq_extension::metadata::ExtensionMetadata { + name: #trait_name, + functions: #scale_info::prelude::vec![ #( #functions, )* ], + } + } + ) + } else { + let impl_generics = quote!(Impl: #trait_ident); + quote!( + pub fn metadata <#impl_generics> () -> #pvq_extension::metadata::ExtensionMetadata { + #pvq_extension::metadata::ExtensionMetadata { + name: #trait_name, + functions: #scale_info::prelude::vec![ #( #functions, )* ], + } + } + ) + } +} + +// Convert `Self::AssociatedType` to `Impl::AssociatedType` +struct AssociatedTypeReplacer; +impl syn::visit_mut::VisitMut for AssociatedTypeReplacer { + fn visit_path_mut(&mut self, path: &mut syn::Path) { + if path.segments.len() == 2 && path.segments[0].ident == "Self" { + path.segments[0].ident = syn::Ident::new("Impl", path.segments[0].ident.span()); + } + } +} diff --git a/pvq-extension/procedural/src/extension_decl/expand/mod.rs b/pvq-extension/procedural/src/extension_decl/expand/mod.rs new file mode 100644 index 0000000..363b570 --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/expand/mod.rs @@ -0,0 +1,24 @@ +mod extension; +mod helper; +mod metadata; + +use crate::extension_decl::Def; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; + +pub fn expand(mut def: Def) -> TokenStream2 { + let extension_expanded = extension::expand_extension(&mut def); + let metadata_expanded = metadata::expand_metadata(&def); + let new_items = quote::quote! { + #extension_expanded + #metadata_expanded + }; + + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); + def.item.into_token_stream() +} diff --git a/pvq-extension/procedural/src/extension_decl/mod.rs b/pvq-extension/procedural/src/extension_decl/mod.rs new file mode 100644 index 0000000..0998a2f --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/mod.rs @@ -0,0 +1,24 @@ +mod expand; +pub(crate) mod parse; +pub use parse::Def; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use syn::spanned::Spanned; + +mod keyword { + syn::custom_keyword!(dev_mode); +} + +pub fn extension_decl(attr: TokenStream, item: TokenStream) -> TokenStream { + if !attr.is_empty() { + let msg = "Invalid #[extension_decl] macro call: unexpected attribute. Macro call must be bare, such as `#[extension_decl]`."; + let span = TokenStream2::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/pvq-extension/procedural/src/extension_decl/parse/extension.rs b/pvq-extension/procedural/src/extension_decl/parse/extension.rs new file mode 100644 index 0000000..3fbe32e --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/parse/extension.rs @@ -0,0 +1,202 @@ +use super::helper; +use crate::utils::generate_crate_access; +use std::collections::HashMap; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing +mod keyword { + syn::custom_keyword!(extension_decl); + syn::custom_keyword!(fn_index); +} + +/// Definition of a ViewFns trait +/// #[extension_decl::extension] +/// pub trait ExtensionCore { +/// type ExtensionId: Codec; +/// #[extension_decl::fn_index(expr)] +/// fn has_extension(id: Self::ExtensionId) -> bool; +/// } +pub struct Extension { + /// The name of the trait + pub name: syn::Ident, + /// The associated type of the trait + pub types: Vec, + /// Information on functions + pub functions: Vec, +} + +#[derive(Debug)] +pub struct ExtensionType { + #[allow(dead_code)] + pub name: syn::Ident, +} + +/// Definition of a function variant +pub struct ExtensionFunction { + /// Function name + pub name: syn::Ident, + /// Information on inputs: `(name, type)` + pub inputs: Vec<(syn::Ident, Box)>, + /// The return type of the function + pub output: syn::ReturnType, +} + +/// Attributes for functions +pub enum FunctionAttr { + /// Parse for #[extension::fn_index(expr)] + FnIndex(u8), +} + +impl syn::parse::Parse for FunctionAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::fn_index) { + content.parse::()?; + let fn_index_content; + syn::parenthesized!(fn_index_content in content); + let index = fn_index_content.parse::()?; + if !index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(index.span(), msg)); + } + Ok(Self::FnIndex(index.base10_parse()?)) + } else { + Err(lookahead.error()) + } + } +} + +impl Extension { + pub fn try_from(item: &mut syn::Item) -> syn::Result { + let parity_scale_codec = generate_crate_access("parity-scale-codec")?; + let scale_info = generate_crate_access("scale-info")?; + let syn::Item::Trait(item) = item else { + let msg = "Invalid extension_decl::extension, expected trait definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid extension_decl::extension, expected public trait definition"; + return Err(syn::Error::new(item.span(), msg)); + } + + if !item.generics.params.is_empty() { + let msg = "Invalid extension_decl::extension, expected no generics"; + return Err(syn::Error::new(item.generics.params[0].span(), msg)); + } + + let mut types = vec![]; + let mut functions = vec![]; + let mut indices = HashMap::new(); + let mut last_index: Option = None; + for item in &mut item.items { + match item { + syn::TraitItem::Type(item_type) => { + // Add `Codec + TypeInfo + 'static` bounds if not present + if item_type + .bounds + .iter() + .all(|bound| bound != &syn::parse_quote!(#parity_scale_codec::Codec)) + { + item_type.bounds.push(syn::parse_quote!(#parity_scale_codec::Codec)); + } + if item_type + .bounds + .iter() + .all(|bound| bound != &syn::parse_quote!(#scale_info::TypeInfo)) + { + item_type.bounds.push(syn::parse_quote!(#scale_info::TypeInfo)); + } + if item_type + .bounds + .iter() + .all(|bound| bound != &syn::parse_quote!('static)) + { + item_type.bounds.push(syn::parse_quote!('static)); + } + types.push(ExtensionType { + name: item_type.ident.clone(), + }); + } + syn::TraitItem::Fn(function) => { + let mut function_index_attrs = vec![]; + for attr in helper::take_item_extension_decl_attrs(&mut function.attrs)?.into_iter() { + match attr { + FunctionAttr::FnIndex(_) => { + function_index_attrs.push(attr); + } + } + } + + if function_index_attrs.len() > 1 { + let msg = "Invalid extension_decl::extension, too many fn_index attributes given"; + return Err(syn::Error::new(function.sig.span(), msg)); + } + + let function_index = function_index_attrs.pop().map(|attr| match attr { + FunctionAttr::FnIndex(index) => index, + }); + + let final_index = match function_index { + Some(i) => i, + None => last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| { + let msg = "Function index doesn't fit into u8, index is 256"; + syn::Error::new(function.sig.span(), msg) + })?, + }; + last_index = Some(final_index); + + if let Some(used_fn) = indices.insert(final_index, function.sig.ident.clone()) { + let error_msg = format!( + "Invalid extension_decl::extension; Both functions {} and {} are at index {}", + used_fn, function.sig.ident, final_index + ); + let mut err = syn::Error::new(used_fn.span(), &error_msg); + err.combine(syn::Error::new(function.sig.ident.span(), &error_msg)); + return Err(err); + } + + let mut inputs = vec![]; + for input in function.sig.inputs.iter() { + let input = if let syn::FnArg::Typed(input) = input { + input + } else { + let msg = "Invalid extension_decl::extension, every input argument should be typed instead of receiver(self)"; + return Err(syn::Error::new(input.span(), msg)); + }; + let input_ident = if let syn::Pat::Ident(pat) = &*input.pat { + pat.ident.clone() + } else { + let msg = "Invalid extension_decl::extension, input argument must be ident"; + return Err(syn::Error::new(input.pat.span(), msg)); + }; + inputs.push((input_ident, input.ty.clone())) + } + + functions.push(ExtensionFunction { + name: function.sig.ident.clone(), + inputs, + output: function.sig.output.clone(), + }); + } + _ => {} + } + } + + if functions.is_empty() { + let msg = "Invalid extension_decl::extension, expected at least one function"; + return Err(syn::Error::new(item.span(), msg)); + } + + Ok(Self { + name: item.ident.clone(), + types, + functions, + }) + } +} diff --git a/pvq-extension/procedural/src/extension_decl/parse/helper.rs b/pvq-extension/procedural/src/extension_decl/parse/helper.rs new file mode 100644 index 0000000..031a1b5 --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/parse/helper.rs @@ -0,0 +1,99 @@ +use quote::ToTokens; +pub trait MutItemAttrs { + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; +} + +/// Take the first extension_decl attribute (e.g. attribute like `#[extension_decl..]`) and decode it to `Attr` +pub(crate) fn take_first_item_extension_decl_attr(item: &mut impl MutItemAttrs) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let Some(attrs) = item.mut_item_attrs() else { + return Ok(None); + }; + + let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .is_some_and(|segment| segment.ident == "extension_decl") + }) else { + return Ok(None); + }; + + let extension_attr = attrs.remove(index); + Ok(Some(syn::parse2(extension_attr.into_token_stream())?)) +} + +/// Take all the extension_decl attributes (e.g. attribute like `#[extension_decl..]`) and decode them to `Attr` +pub(crate) fn take_item_extension_decl_attrs(item: &mut impl MutItemAttrs) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let mut extension_attrs = Vec::new(); + + while let Some(attr) = take_first_item_extension_decl_attr(item)? { + extension_attrs.push(attr) + } + + Ok(extension_attrs) +} + +impl MutItemAttrs for syn::Item { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for syn::TraitItem { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for Vec { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } +} + +impl MutItemAttrs for syn::ItemMod { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ImplItemFn { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ItemType { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} diff --git a/pvq-extension/procedural/src/extension_decl/parse/mod.rs b/pvq-extension/procedural/src/extension_decl/parse/mod.rs new file mode 100644 index 0000000..956a2e6 --- /dev/null +++ b/pvq-extension/procedural/src/extension_decl/parse/mod.rs @@ -0,0 +1,96 @@ +pub mod extension; +mod helper; + +use crate::utils::generate_crate_access; +use syn::spanned::Spanned; + +mod keyword { + syn::custom_keyword!(extension_decl); + syn::custom_keyword!(extension); +} +pub struct Def { + pub item: syn::ItemMod, + pub extension: extension::Extension, + pub pvq_extension: syn::Path, + pub scale_info: syn::Path, + pub parity_scale_codec: syn::Path, +} + +impl Def { + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let pvq_extension = generate_crate_access("pvq-extension")?; + let scale_info = generate_crate_access("scale-info")?; + let parity_scale_codec = generate_crate_access("parity-scale-codec")?; + let mod_span = item.span(); + // Check if the module is public + if !matches!(item.vis, syn::Visibility::Public(_)) { + return Err(syn::Error::new( + mod_span, + "Invalid #[extension_decl] definition, expected public module.", + )); + } + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid #[extension_decl] definition, expected mod to be inline."; + syn::Error::new(mod_span, msg) + })? + .1; + let mut extension = None; + for item in items.iter_mut() { + let extension_attr: Option = helper::take_first_item_extension_decl_attr(item)?; + + match extension_attr { + Some(ExtensionDeclAttr::Extension(_)) if extension.is_none() => { + extension = Some(extension::Extension::try_from(item)?); + } + Some(attr) => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + None => (), + } + } + + Ok(Self { + item, + extension: extension.ok_or_else(|| syn::Error::new(mod_span, "Missing `#[extension_decl::extension]`"))?, + pvq_extension, + scale_info, + parity_scale_codec, + }) + } +} + +/// Parse attributes for item in extension module +/// syntax must be `extension_decl::` (e.g. `#[extension_decl::extension]`) +enum ExtensionDeclAttr { + Extension(proc_macro2::Span), +} + +impl syn::parse::Parse for ExtensionDeclAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::extension) { + let span = content.parse::()?.span(); + Ok(Self::Extension(span)) + } else { + Err(lookahead.error()) + } + } +} + +impl ExtensionDeclAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::Extension(span) => *span, + } + } +} diff --git a/pvq-extension/procedural/src/extensions_impl/expand/metadata.rs b/pvq-extension/procedural/src/extensions_impl/expand/metadata.rs new file mode 100644 index 0000000..4fa8f00 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/expand/metadata.rs @@ -0,0 +1,45 @@ +use crate::extensions_impl::Def; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +/// generate the `metadata` function in the #[extensions_impl] module +pub fn expand_metadata(def: &Def) -> TokenStream2 { + let pvq_extension = &def.pvq_extension; + let mut extension_metadata_call_list = Vec::new(); + + for impl_ in &def.extension_impls { + let mut trait_path = impl_.trait_path.clone(); + trait_path.segments.pop(); + + // Replace trait_path with a call to the metadata function with the impl struct as generic parameter + let impl_struct_ident = &def.impl_struct.ident; + + // Create a method call expression instead of a path + let method_call = quote!( + #trait_path metadata::<#impl_struct_ident>() + ); + + extension_metadata_call_list.push(method_call); + } + + // let query_metadata_by_extension_id = quote! { + // impl #pvq_extension::ExtensionImplMetadata for #extension_impl_name { + // fn extension_metadata(extension_id: #pvq_extension::ExtensionIdTy) -> #pvq_extension::metadata::ExtensionMetadata { + // let extension_metadata = match extension_id { + // #(#extension_ids => #extension_metadata_list,)* + // _ => panic!("Unknown extension id"), + // }; + // extension_metadata + // } + // } + // }; + + let metadata = quote! { + pub fn metadata() -> #pvq_extension::metadata::Metadata { + #pvq_extension::metadata::Metadata::new( + scale_info::prelude::vec![ #( #extension_metadata_call_list, )* ], + ) + } + }; + metadata +} diff --git a/pvq-extension/procedural/src/extensions_impl/expand/mod.rs b/pvq-extension/procedural/src/extensions_impl/expand/mod.rs new file mode 100644 index 0000000..23c3e88 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/expand/mod.rs @@ -0,0 +1,42 @@ +use crate::extensions_impl::Def; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +mod metadata; + +fn expand_extensions_tuple(def: &Def) -> TokenStream2 { + let mut extensions = Vec::new(); + let impl_struct_ident = &def.impl_struct.ident; + for impl_ in &def.extension_impls { + let mut trait_path = impl_.trait_path.clone(); + + // Replace the last segment of the trait path with Functions + if let Some(segment) = trait_path.segments.last_mut() { + *segment = syn::parse_quote!(Functions<#impl_struct_ident>); + } + + extensions.push(trait_path); + } + + quote::quote! { + pub type Extensions = ( + #(#extensions),* + ); + } +} + +pub fn expand(mut def: Def) -> TokenStream2 { + let extensions_tuple_expanded = expand_extensions_tuple(&def); + let metadata_expanded = metadata::expand_metadata(&def); + let new_items = quote::quote! { + #extensions_tuple_expanded + #metadata_expanded + }; + + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); + def.item.into_token_stream() +} diff --git a/pvq-extension/procedural/src/extensions_impl/mod.rs b/pvq-extension/procedural/src/extensions_impl/mod.rs new file mode 100644 index 0000000..4aefcd7 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/mod.rs @@ -0,0 +1,20 @@ +mod expand; +mod parse; +pub use parse::Def; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use syn::spanned::Spanned; + +pub fn extensions_impl(attr: TokenStream, item: TokenStream) -> TokenStream { + if !attr.is_empty() { + let msg = "Invalid extensions_impl macro call: unexpected attribute. Macro call must be bare, such as `#[extensions_impl]`."; + let span = TokenStream2::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/pvq-extension/procedural/src/extensions_impl/parse/extension.rs b/pvq-extension/procedural/src/extensions_impl/parse/extension.rs new file mode 100644 index 0000000..1ffb748 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/parse/extension.rs @@ -0,0 +1,35 @@ +use syn::spanned::Spanned; +use syn::Error; +pub struct ExtensionImpl { + pub trait_path: syn::Path, +} + +impl ExtensionImpl { + pub fn try_from(item: &mut syn::Item) -> syn::Result { + let syn::Item::Impl(item) = item else { + let msg = "Invalid extensions_impl::extension, expected impl definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + // Check if's a impl trait and the trait is qualified + let path = item + .trait_ + .as_ref() + .map(|v| &v.1) + .ok_or_else(|| Error::new(item.span(), "Only implementation of traits are supported!")) + .and_then(|p| { + // The implemented trait has to be referenced with a fully qualified path, + if p.segments.len() > 1 { + Ok(p) + } else { + Err(Error::new( + p.span(), + "The implemented trait has to be referenced with a fully qualified path, \ + e.g. `impl pvq_extension_core::ExtensionCore for ExtensionsImpl`.", + )) + } + })?; + Ok(Self { + trait_path: path.clone(), + }) + } +} diff --git a/pvq-extension/procedural/src/extensions_impl/parse/helper.rs b/pvq-extension/procedural/src/extensions_impl/parse/helper.rs new file mode 100644 index 0000000..bd6c8ed --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/parse/helper.rs @@ -0,0 +1,100 @@ +use quote::ToTokens; +pub trait MutItemAttrs { + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; +} + +/// Take the first extensions_impl attribute (e.g. attribute like `#[extensions_impl..]`) and decode it to `Attr` +pub(crate) fn take_first_item_extensions_impl_attr(item: &mut impl MutItemAttrs) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let Some(attrs) = item.mut_item_attrs() else { + return Ok(None); + }; + + let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .is_some_and(|segment| segment.ident == "extensions_impl") + }) else { + return Ok(None); + }; + + let extension_attr = attrs.remove(index); + Ok(Some(syn::parse2(extension_attr.into_token_stream())?)) +} + +/// Take all the extensions_impl attributes (e.g. attribute like `#[extensions_impl..]`) and decode them to `Attr` +#[allow(dead_code)] +pub(crate) fn take_item_extensions_impl_attrs(item: &mut impl MutItemAttrs) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let mut extension_attrs = Vec::new(); + + while let Some(attr) = take_first_item_extensions_impl_attr(item)? { + extension_attrs.push(attr) + } + + Ok(extension_attrs) +} + +impl MutItemAttrs for syn::Item { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for syn::TraitItem { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for Vec { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } +} + +impl MutItemAttrs for syn::ItemMod { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ImplItemFn { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ItemType { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} diff --git a/pvq-extension/procedural/src/extensions_impl/parse/impl_struct.rs b/pvq-extension/procedural/src/extensions_impl/parse/impl_struct.rs new file mode 100644 index 0000000..f686ee5 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/parse/impl_struct.rs @@ -0,0 +1,16 @@ +use syn::spanned::Spanned; +pub struct ImplStruct { + pub ident: syn::Ident, +} +impl ImplStruct { + pub fn try_from(item: &mut syn::Item) -> syn::Result { + let syn::Item::Struct(item) = item else { + let msg = "Invalid extensions_impl::impl_struct, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + + Ok(Self { + ident: item.ident.clone(), + }) + } +} diff --git a/pvq-extension/procedural/src/extensions_impl/parse/mod.rs b/pvq-extension/procedural/src/extensions_impl/parse/mod.rs new file mode 100644 index 0000000..d45c4a7 --- /dev/null +++ b/pvq-extension/procedural/src/extensions_impl/parse/mod.rs @@ -0,0 +1,98 @@ +mod extension; +mod helper; +mod impl_struct; +use crate::utils::generate_crate_access; +use syn::spanned::Spanned; + +mod keyword { + syn::custom_keyword!(extensions_impl); + syn::custom_keyword!(impl_struct); + syn::custom_keyword!(extension); +} + +pub struct Def { + pub item: syn::ItemMod, + pub impl_struct: impl_struct::ImplStruct, + pub extension_impls: Vec, + pub pvq_extension: syn::Path, +} + +impl Def { + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let pvq_extension = generate_crate_access("pvq-extension")?; + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid extensions_impl definition, expected mod to be inline."; + syn::Error::new(item_span, msg) + })? + .1; + let mut impl_struct = None; + let mut extension_impls = Vec::new(); + for item in items.iter_mut() { + let extensions_impl_attr: Option = helper::take_first_item_extensions_impl_attr(item)?; + match extensions_impl_attr { + Some(ExtensionsImplAttr::ImplStruct(_)) if impl_struct.is_none() => { + impl_struct = Some(impl_struct::ImplStruct::try_from(item)?); + } + Some(ExtensionsImplAttr::Extension(_)) => { + extension_impls.push(extension::ExtensionImpl::try_from(item)?); + } + Some(attr) => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + None => (), + } + } + + if extension_impls.is_empty() { + let msg = "At least one `#[extensions_impl::extension]` is required"; + return Err(syn::Error::new(item_span, msg)); + } + Ok(Self { + item, + impl_struct: impl_struct + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[extensions_impl::impl_struct]`"))?, + extension_impls, + pvq_extension, + }) + } +} + +enum ExtensionsImplAttr { + ImplStruct(proc_macro2::Span), + Extension(proc_macro2::Span), +} + +impl syn::parse::Parse for ExtensionsImplAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::impl_struct) { + let span = content.parse::()?.span(); + Ok(Self::ImplStruct(span)) + } else if lookahead.peek(keyword::extension) { + let span = content.parse::()?.span(); + Ok(Self::Extension(span)) + } else { + Err(lookahead.error()) + } + } +} + +impl ExtensionsImplAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::ImplStruct(span) => *span, + Self::Extension(span) => *span, + } + } +} diff --git a/pvq-extension/procedural/src/impl_extensions.rs b/pvq-extension/procedural/src/impl_extensions.rs deleted file mode 100644 index 40a86b4..0000000 --- a/pvq-extension/procedural/src/impl_extensions.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::utils::{extract_impl_trait, generate_mod_name_for_trait, RequireQualifiedTraitPath}; -use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use syn::{ - parse::{Parse, ParseStream}, - parse_macro_input, parse_quote, ItemImpl, Result, -}; -pub fn impl_extensions_impl(input: TokenStream) -> TokenStream { - let PvqExtensionImpls { impls } = parse_macro_input!(input as PvqExtensionImpls); - impl_extensions_inner(&impls) - .unwrap_or_else(|err| err.to_compile_error()) - .into() -} - -struct PvqExtensionImpls { - impls: Vec, -} - -impl Parse for PvqExtensionImpls { - fn parse(input: ParseStream) -> Result { - let mut impls = Vec::new(); - while !input.is_empty() { - impls.push(input.parse()?); - } - Ok(Self { impls }) - } -} - -fn impl_extensions_inner(impls: &[ItemImpl]) -> Result { - let runtime_metadata = crate::runtime_metadata::generate_impl_metadata(impls)?; - - let extension_tuple = generate_extension_tuple(impls)?; - - let expanded = quote! { - #(#impls)* - - #runtime_metadata - - #extension_tuple - }; - Ok(expanded) -} - -fn generate_extension_tuple(impls: &[ItemImpl]) -> Result { - let extension_impl_name = &impls - .first() - .expect("Traits should contain at least one implementation; qed") - .self_ty; - let mut extensions = Vec::new(); - for impl_ in impls { - let mut trait_ = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(); - - let trait_name_ident = &trait_ - .segments - .last() - .as_ref() - .expect("Trait path should always contain at least one item; qed") - .ident; - let mod_name = generate_mod_name_for_trait(trait_name_ident); - // Get absolute path to the `runtime_decl_for_` module by replacing the last segment. - if let Some(segment) = trait_.segments.last_mut() { - *segment = parse_quote!(#mod_name); - } - trait_.segments.push(parse_quote!(Call<#extension_impl_name>)); - extensions.push(trait_); - } - - Ok(quote! { - type Extensions = ( - #(#extensions),* - ); - }) -} diff --git a/pvq-extension/procedural/src/lib.rs b/pvq-extension/procedural/src/lib.rs index 6e3f753..9bc82dd 100644 --- a/pvq-extension/procedural/src/lib.rs +++ b/pvq-extension/procedural/src/lib.rs @@ -1,15 +1,14 @@ use proc_macro::TokenStream; -mod decl_extensions; -mod impl_extensions; -mod runtime_metadata; -mod utils; +mod extension_decl; +mod extensions_impl; +pub(crate) mod utils; -#[proc_macro] -pub fn decl_extensions(input: TokenStream) -> TokenStream { - decl_extensions::decl_extensions_impl(input) +#[proc_macro_attribute] +pub fn extension_decl(attr: TokenStream, item: TokenStream) -> TokenStream { + extension_decl::extension_decl(attr, item) } -#[proc_macro] -pub fn impl_extensions(input: TokenStream) -> TokenStream { - impl_extensions::impl_extensions_impl(input) +#[proc_macro_attribute] +pub fn extensions_impl(attr: TokenStream, item: TokenStream) -> TokenStream { + extensions_impl::extensions_impl(attr, item) } diff --git a/pvq-extension/procedural/src/runtime_metadata.rs b/pvq-extension/procedural/src/runtime_metadata.rs deleted file mode 100644 index 5774181..0000000 --- a/pvq-extension/procedural/src/runtime_metadata.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::collections::HashSet; - -use crate::utils::{extract_impl_trait, generate_crate_access, generate_mod_name_for_trait, RequireQualifiedTraitPath}; -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use syn::{ - parse_quote, punctuated::Punctuated, visit_mut::VisitMut, GenericParam, Generics, ItemImpl, ItemTrait, Result, - Token, -}; - -/// Generate the runtime metadata of the provided extension trait. -/// -/// The metadata is exposed as a generic function on the hidden module -/// of the trait generated by the `decl_extensions`. -pub fn generate_decl_metadata(decl: &ItemTrait, has_config: bool) -> Result { - let pvq_extension = generate_crate_access("pvq-extension")?; - let mut methods = Vec::new(); - - // Convert `::Associated` to `T::Associated` with `T:Config` bound - let mut replacer = AssociatedTypeReplacer { - generic_params: HashSet::new(), - where_predicates: HashSet::new(), - }; - // Adding bounds: `TypeInfo + 'static` for any type parameter in method sigs (generic parameter and associated type). - let mut where_clause = Vec::new(); - - for item in &decl.items { - // Collect metadata for methods only. - let syn::TraitItem::Fn(method) = item else { continue }; - - let mut inputs = Vec::new(); - let signature = &method.sig; - for input in &signature.inputs { - // Exclude `self` from metadata collection. - let syn::FnArg::Typed(typed) = input else { continue }; - - let pat = &typed.pat; - let name = quote!(#pat).to_string(); - let mut ty = typed.ty.clone(); - - if has_config { - replacer.visit_type_mut(&mut ty); - } - - where_clause.push(get_type_param(&ty)); - - inputs.push(quote!( - #pvq_extension::metadata::MethodParamMetadata { - name: #name, - ty: scale_info::meta_type::<#ty>(), - } - )); - } - - let output = match &signature.output { - syn::ReturnType::Default => quote!(scale_info::meta_type::<()>()), - syn::ReturnType::Type(_, ty) => { - let mut ty = ty.clone(); - if has_config { - replacer.visit_type_mut(&mut ty); - } - where_clause.push(get_type_param(&ty)); - quote!(scale_info::meta_type::<#ty>()) - } - }; - - let method_name = signature.ident.to_string(); - - let attrs = &method.attrs; - methods.push(quote!( - #( #attrs )* - #pvq_extension::metadata::MethodMetadata { - name: #method_name, - inputs: scale_info::prelude::vec![ #( #inputs, )* ], - output: #output, - } - )); - } - - let trait_name_ident = &decl.ident; - let trait_name = trait_name_ident.to_string(); - let attrs = &decl.attrs; - - // Assume no generics - // extract associated types - let mut generics = Generics { - lt_token: Some(syn::token::Lt::default()), - params: replacer.generic_params.into_iter().collect(), - gt_token: Some(syn::token::Gt::default()), - where_clause: None, - }; - - for where_predicate in replacer.where_predicates { - generics.make_where_clause().predicates.push(where_predicate); - } - where_clause - .into_iter() - .map(|ty| parse_quote!(#ty: scale_info::TypeInfo + 'static)) - .for_each(|w| generics.make_where_clause().predicates.push(w)); - - let (impl_generics, _, where_clause) = generics.split_for_impl(); - if has_config { - Ok(quote!( - #(#attrs)* - #[inline(always)] - pub fn extension_metadata #impl_generics () -> #pvq_extension::metadata::ExtensionMetadata - #where_clause - { - #pvq_extension::metadata::ExtensionMetadata { - name: #trait_name, - methods: scale_info::prelude::vec![ #( #methods, )* ], - } - } - )) - } else { - Ok(quote!( - #(#attrs)* - #[inline(always)] - pub fn extension_metadata() -> #pvq_extension::metadata::ExtensionMetadata { - #pvq_extension::metadata::ExtensionMetadata { - name: #trait_name, - methods: scale_info::prelude::vec![ #( #methods, )* ], - } - } - )) - } -} - -/// Implement the `runtime_metadata` function on the extensions impl that -/// generates the metadata for the given traits. -/// The metadata of each extension trait is extracted from the generic function -/// exposed by `generate_decl_metadata`. -pub fn generate_impl_metadata(impls: &[ItemImpl]) -> Result { - if impls.is_empty() { - return Ok(quote!()); - } - - let pvq_extension = generate_crate_access("pvq-extension")?; - - // Get the name of the runtime for which the traits are implemented. - let extension_impl_name = &impls - .first() - .expect("Traits should contain at least one implementation; qed") - .self_ty; - - let mut extension_metadata_list = Vec::new(); - let mut extension_ids = Vec::new(); - - for impl_ in impls { - let mut trait_ = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(); - - // Implementation traits are always references with a path `impl pvq_extension::ExtensionCore ...` - // The trait name is the last segment of this path. - let trait_name_ident = trait_ - .segments - .last() - .as_ref() - .expect("Trait path should always contain at least one item; qed") - .ident - .clone(); - - // Convert associated types to generics - let mut generic_params = HashSet::::new(); - for item in &impl_.items { - if let syn::ImplItem::Type(associated_type) = item { - let ty = &associated_type.ty; - generic_params.insert(parse_quote!(#ty)); - } - } - let generics = Generics { - lt_token: Some(syn::token::Lt::default()), - params: generic_params.into_iter().collect(), - gt_token: Some(syn::token::Gt::default()), - where_clause: None, - }; - - let mod_name = generate_mod_name_for_trait(&trait_name_ident); - // Get absolute path to the `runtime_decl_for_` module by replacing the last segment. - let mut extension_trait_full_path = trait_.clone(); - if let Some(segment) = extension_trait_full_path.segments.last_mut() { - *segment = parse_quote!(EXTENSION_ID); - } - if let Some(segment) = trait_.segments.last_mut() { - *segment = parse_quote!(#mod_name); - } - - let attrs = &impl_.attrs; - extension_metadata_list.push(quote!( - #( #attrs )* - #trait_::extension_metadata::#generics() - )); - extension_ids.push(quote!(#extension_trait_full_path)); - } - - let query_metadata_by_extension_id = quote! { - impl #pvq_extension::ExtensionImplMetadata for #extension_impl_name { - fn extension_metadata(extension_id: #pvq_extension::ExtensionIdTy) -> #pvq_extension::metadata::ExtensionMetadata { - let extension_metadata = match extension_id { - #(#extension_ids => #extension_metadata_list,)* - _ => panic!("Unknown extension id"), - }; - extension_metadata - } - } - }; - - let aggregate_metadata = quote! { - impl #extension_impl_name { - pub fn metadata() -> #pvq_extension::metadata::Metadata { - #pvq_extension::metadata::Metadata::new( - scale_info::prelude::vec![ #( #extension_metadata_list, )* ], - ) - } - } - }; - - Ok(quote!( - #query_metadata_by_extension_id - #aggregate_metadata - )) -} - -// Convert associated type to generic type -// i.e `::Associated` to `::Associated` with `Config::Config` bound -struct AssociatedTypeReplacer { - generic_params: HashSet, - where_predicates: HashSet, -} -impl VisitMut for AssociatedTypeReplacer { - fn visit_path_mut(&mut self, path: &mut syn::Path) { - if path.segments.len() == 2 && path.segments[0].ident == "Self" { - let mut new_segments: Punctuated = Punctuated::new(); - let segment = &path.segments[1]; - let generic_param = format_ident!("GenericFor{}", segment.ident); - let new_segment = syn::PathSegment { - ident: generic_param, - arguments: segment.arguments.clone(), - }; - self.generic_params.insert(parse_quote!(#new_segment)); - self.where_predicates.insert(parse_quote!(#new_segment: #segment)); - new_segments.push(new_segment); - path.segments = new_segments; - } - } -} - -/// Instead of returning `&'a AccountId` for the first parameter, this function -/// returns `AccountId` to place bounds around it. -fn get_type_param(ty: &syn::Type) -> syn::Type { - // Remove the lifetime and mutability of the type T to - // place bounds around it. - let ty_elem = match &ty { - syn::Type::Reference(reference) => &reference.elem, - syn::Type::Ptr(ptr) => &ptr.elem, - syn::Type::Slice(slice) => &slice.elem, - syn::Type::Array(arr) => &arr.elem, - _ => ty, - }; - - ty_elem.clone() -} diff --git a/pvq-extension/procedural/src/utils.rs b/pvq-extension/procedural/src/utils.rs index 5a1a694..7f48f87 100644 --- a/pvq-extension/procedural/src/utils.rs +++ b/pvq-extension/procedural/src/utils.rs @@ -1,52 +1,13 @@ -use inflector::Inflector; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::Span; use proc_macro_crate::{crate_name, FoundCrate}; -use quote::quote; -use syn::{spanned::Spanned, Error, Ident, ItemImpl, Path, Result}; -/// Should a qualified trait path be required? -/// -/// e.g. `path::Trait` is qualified and `Trait` is not. -#[allow(dead_code)] -pub enum RequireQualifiedTraitPath { - Yes, - No, -} -/// Extract the trait that is implemented by the given `ItemImpl`. -pub fn extract_impl_trait(impl_: &ItemImpl, require: RequireQualifiedTraitPath) -> Result<&Path> { - impl_ - .trait_ - .as_ref() - .map(|v| &v.1) - .ok_or_else(|| Error::new(impl_.span(), "Only implementation of traits are supported!")) - .and_then(|p| { - if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) { - Ok(p) - } else { - Err(Error::new( - p.span(), - "The implemented trait has to be referenced with a path, \ - e.g. `impl pvq_extension_core::ExtensionCore for Runtime`.", - )) - } - }) -} - -/// Generates the name of the module that contains the trait declaration for the runtime. -pub fn generate_mod_name_for_trait(trait_: &Ident) -> Ident { - Ident::new( - &format!("decl_extension_for_{}", trait_.to_string().to_snake_case()), - Span::call_site(), - ) -} - -pub fn generate_crate_access(def_crate: &str) -> Result { - match crate_name(def_crate) { - Ok(FoundCrate::Itself) => Ok(quote!(crate)), - Ok(FoundCrate::Name(name)) => { - let name = name.replace('-', "_"); - let ident = Ident::new(&name, Span::call_site()); - Ok(quote!(#ident)) +pub fn generate_crate_access(def_crate: &str) -> syn::Result { + let ident = match crate_name(def_crate) { + Ok(FoundCrate::Itself) => { + let name = def_crate.replace('-', "_"); + Ok(syn::Ident::new(&name, Span::call_site())) } - Err(e) => Err(Error::new(Span::call_site(), e)), - } + Ok(FoundCrate::Name(name)) => Ok(syn::Ident::new(&name, Span::call_site())), + Err(e) => Err(syn::Error::new(Span::call_site(), e)), + }?; + Ok(syn::Path::from(ident)) } diff --git a/pvq-extension/procedural/tests/tests.rs b/pvq-extension/procedural/tests/tests.rs new file mode 100644 index 0000000..f732206 --- /dev/null +++ b/pvq-extension/procedural/tests/tests.rs @@ -0,0 +1,11 @@ +#[test] +fn test_macros() { + let t = trybuild::TestCases::new(); + // Test successful cases + t.pass("tests/ui/extension_decl/pass/*.rs"); + t.pass("tests/ui/extensions_impl/pass/*.rs"); + + // Test failing cases + t.compile_fail("tests/ui/extension_decl/*.rs"); + t.compile_fail("tests/ui/extensions_impl/*.rs"); +} diff --git a/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.rs b/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.rs new file mode 100644 index 0000000..d268342 --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.rs @@ -0,0 +1,9 @@ +use pvq_extension_procedural::extension_decl; + +// This should fail because extension_decl can only be used on modules +#[extension_decl] +struct InvalidUsage { + field: u32, +} + +fn main() {} diff --git a/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.stderr b/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.stderr new file mode 100644 index 0000000..54b5e63 --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extension_decl/not_on_mod.stderr @@ -0,0 +1,5 @@ +error: expected `mod` + --> tests/ui/extension_decl/not_on_mod.rs:5:1 + | +5 | struct InvalidUsage { + | ^^^^^^ diff --git a/pvq-extension/procedural/tests/ui/extension_decl/pass/sucess.rs b/pvq-extension/procedural/tests/ui/extension_decl/pass/sucess.rs new file mode 100644 index 0000000..1233340 --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extension_decl/pass/sucess.rs @@ -0,0 +1,12 @@ +use pvq_extension_procedural::extension_decl; + +#[extension_decl] +pub mod test_extension { + #[extension_decl::extension] + pub trait TestExtension { + type Value; + fn test_fn(value: Self::Value) -> bool; + } +} + +fn main() {} diff --git a/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.rs b/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.rs new file mode 100644 index 0000000..4a8a6c5 --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.rs @@ -0,0 +1,26 @@ +use pvq_extension_procedural::{extension_decl, extensions_impl}; + +#[extension_decl] +pub mod test_extension { + #[extension_decl::extension] + pub trait TestExtension { + type Value; + fn test_fn(value: Self::Value) -> bool; + } +} + +#[extensions_impl] +mod test_impl { + // Missing #[extensions_impl::impl_struct] attribute + pub struct TestImpl; + + #[extensions_impl::extension] + impl test_extension::TestExtension for TestImpl { + type Value = u32; + fn test_fn(value: u32) -> bool { + value > 0 + } + } +} + +fn main() {} diff --git a/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.stderr b/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.stderr new file mode 100644 index 0000000..7b74361 --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extensions_impl/missing_impl_struct.stderr @@ -0,0 +1,10 @@ +error: Missing `#[extensions_impl::impl_struct]` + --> tests/ui/extensions_impl/missing_impl_struct.rs:13:1 + | +13 | / mod test_impl { +14 | | // Missing #[extensions_impl::impl_struct] attribute +15 | | pub struct TestImpl; +... | +23 | | } +24 | | } + | |_^ diff --git a/pvq-extension/procedural/tests/ui/extensions_impl/pass/success.rs b/pvq-extension/procedural/tests/ui/extensions_impl/pass/success.rs new file mode 100644 index 0000000..7cd88dc --- /dev/null +++ b/pvq-extension/procedural/tests/ui/extensions_impl/pass/success.rs @@ -0,0 +1,27 @@ +use pvq_extension_procedural::{extension_decl, extensions_impl}; + +#[extension_decl] +pub mod test_extension { + #[extension_decl::extension] + pub trait TestExtension { + type Value; + fn test_fn(value: Self::Value) -> bool; + } +} + +#[extensions_impl] +mod test_impl { + use super::test_extension; + #[extensions_impl::impl_struct] + pub struct TestImpl; + + #[extensions_impl::extension] + impl test_extension::TestExtension for TestImpl { + type Value = u32; + fn test_fn(value: u32) -> bool { + value > 0 + } + } +} + +fn main() {} diff --git a/pvq-extension/src/calldata.rs b/pvq-extension/src/calldata.rs new file mode 100644 index 0000000..94385a5 --- /dev/null +++ b/pvq-extension/src/calldata.rs @@ -0,0 +1,30 @@ +use parity_scale_codec::Decode; +use scale_info::prelude::vec::Vec; + +/// Type for extension IDs +pub type ExtensionIdTy = u64; + +/// Trait for identifying extensions +pub trait ExtensionId { + const EXTENSION_ID: ExtensionIdTy; +} + +/// Trait for dispatching extension calls +pub trait Dispatchable { + fn dispatch(self) -> Result, DispatchError>; +} + +/// Error type for dispatch operations +#[derive(Debug)] +pub enum DispatchError { + PhantomData, +} + +/// Trait for extension call data +/// +/// This trait combines several traits that are required for extension call data: +/// - `Dispatchable`: Allows dispatching calls to the extension functions +/// - `ExtensionId`: Identifies the extension +/// - `Decode`: Allows decoding the call data +pub trait CallData: Dispatchable + ExtensionId + Decode {} +impl CallData for T where T: Dispatchable + ExtensionId + Decode {} diff --git a/pvq-extension/src/context.rs b/pvq-extension/src/context.rs new file mode 100644 index 0000000..27dfff1 --- /dev/null +++ b/pvq-extension/src/context.rs @@ -0,0 +1,98 @@ +use pvq_executor::{Caller, Linker, PvqExecutorContext}; +use scale_info::prelude::marker::PhantomData; + +use crate::{ + error::ExtensionError, + perm_controller::{InvokeSource, PermissionController}, + CallDataTuple, +}; + +/// Execution context for extensions +/// +/// This struct provides the context for executing extensions. +/// It includes the invoke source and user data. +pub struct Context { + /// The source of the invocation + invoke_source: InvokeSource, + /// User data for the context + user_data: (), + /// Marker for the generic parameters + _marker: PhantomData<(C, P)>, +} + +impl Context { + /// Create a new context + /// + /// # Arguments + /// + /// * `invoke_source` - The source of the invocation + pub fn new(invoke_source: InvokeSource) -> Self { + Self { + invoke_source, + user_data: (), + _marker: PhantomData, + } + } +} + +impl PvqExecutorContext for Context { + type UserData = (); + type UserError = ExtensionError; + + fn data(&mut self) -> &mut Self::UserData { + &mut self.user_data + } + + fn register_host_functions(&mut self, linker: &mut Linker) { + let invoke_source = self.invoke_source; + + // Register the host_call function + linker + .define_typed( + "host_call", + move |caller: Caller<'_, Self::UserData>, + extension_id: u64, + call_ptr: u32, + call_len: u32| + -> Result { + // Read the call data from memory + let call_bytes = caller.instance.read_memory(call_ptr, call_len)?; + tracing::info!("(host call): call_ptr: {}, call_len: {:?}", call_ptr, call_len); + tracing::info!( + "(host call): extension_id: {}, call_bytes: {:?}", + extension_id, + call_bytes + ); + + // Check permissions + if !P::is_allowed(extension_id, &call_bytes, invoke_source) { + return Err(ExtensionError::PermissionError); + } + + // Dispatch the call + let res_bytes = C::dispatch(extension_id, &call_bytes)?; + tracing::debug!("(host call): res_bytes: {:?}", res_bytes); + + // Allocate memory for the response + let res_bytes_len = res_bytes.len(); + let res_ptr = caller + .instance + .sbrk(0) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + caller + .instance + .sbrk(res_bytes_len as u32) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + + // Write the response to memory + caller.instance.write_memory(res_ptr, &res_bytes)?; + + // Return the pointer and length + Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) + }, + ) + .expect("Failed to register host_call function"); + } +} diff --git a/pvq-extension/src/dispatchable.rs b/pvq-extension/src/dispatchable.rs deleted file mode 100644 index fba1a00..0000000 --- a/pvq-extension/src/dispatchable.rs +++ /dev/null @@ -1,9 +0,0 @@ -use scale_info::prelude::vec::Vec; -pub trait Dispatchable { - fn dispatch(self) -> Result, DispatchError>; -} - -#[derive(Debug)] -pub enum DispatchError { - PhantomData, -} diff --git a/pvq-extension/src/error.rs b/pvq-extension/src/error.rs index c7991f2..47cfb1e 100644 --- a/pvq-extension/src/error.rs +++ b/pvq-extension/src/error.rs @@ -1,18 +1,61 @@ // TODO: contain source error use crate::DispatchError; -use parity_scale_codec::Error as CodeCError; +use parity_scale_codec::Error as CodecError; +use scale_info::prelude::fmt; +use scale_info::prelude::fmt::{Display, Formatter}; + +/// Errors that can occur when working with extensions #[derive(Debug)] pub enum ExtensionError { + /// Permission denied for the requested operation PermissionError, + + /// Failed to allocate memory MemoryAllocationError, + + /// Error accessing memory MemoryAccessError(polkavm::MemoryAccessError), - DecodeError(CodeCError), + + /// Error decoding data + DecodeError(CodecError), + + /// Error dispatching a call DispatchError(DispatchError), + + /// The requested extension is not supported UnsupportedExtension, } +impl Display for ExtensionError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::PermissionError => write!(f, "Permission denied"), + Self::MemoryAllocationError => write!(f, "Failed to allocate memory"), + Self::MemoryAccessError(e) => write!(f, "Memory access error: {:?}", e), + Self::DecodeError(e) => write!(f, "Decode error: {:?}", e), + Self::DispatchError(e) => write!(f, "Dispatch error: {:?}", e), + Self::UnsupportedExtension => write!(f, "Unsupported extension"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ExtensionError {} + impl From for ExtensionError { fn from(e: polkavm::MemoryAccessError) -> Self { Self::MemoryAccessError(e) } } + +impl From for ExtensionError { + fn from(e: CodecError) -> Self { + Self::DecodeError(e) + } +} + +impl From for ExtensionError { + fn from(e: DispatchError) -> Self { + Self::DispatchError(e) + } +} diff --git a/pvq-extension/src/executor.rs b/pvq-extension/src/executor.rs new file mode 100644 index 0000000..b6dae94 --- /dev/null +++ b/pvq-extension/src/executor.rs @@ -0,0 +1,45 @@ +use pvq_executor::PvqExecutor; +use pvq_primitives::{PvqError, PvqResult}; + +use crate::{ + perm_controller::{InvokeSource, PermissionController}, + CallDataTuple, Context, +}; + +/// Executor for extensions +/// +/// This struct provides an executor for running extension code. +/// It wraps a PvqExecutor with a Context for extensions. +pub struct ExtensionsExecutor { + /// The underlying PVQ executor + executor: PvqExecutor>, +} + +impl ExtensionsExecutor { + /// Create a new extensions executor + /// + /// # Arguments + /// + /// * `source` - The source of the invocation + pub fn new(source: InvokeSource) -> Self { + let context = Context::::new(source); + let executor = PvqExecutor::new(Default::default(), context); + Self { executor } + } + + /// Execute a method on an extension + /// + /// # Arguments + /// + /// * `program` - The program data + /// * `args` - The input data + /// + /// # Returns + /// + /// The result of the execution or an error + pub fn execute_method(&mut self, program: &[u8], args: &[u8], gas_limit: u64) -> PvqResult { + self.executor + .execute(program, args, gas_limit) + .map_err(|e| PvqError::Custom(scale_info::prelude::format!("{:?}", e))) + } +} diff --git a/pvq-extension/src/extension_id.rs b/pvq-extension/src/extension_id.rs deleted file mode 100644 index 095b5df..0000000 --- a/pvq-extension/src/extension_id.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub type ExtensionIdTy = u64; - -pub trait ExtensionId { - const EXTENSION_ID: ExtensionIdTy; -} diff --git a/pvq-extension/src/guest.rs b/pvq-extension/src/guest.rs deleted file mode 100644 index 380d4da..0000000 --- a/pvq-extension/src/guest.rs +++ /dev/null @@ -1,11 +0,0 @@ -use scale_info::prelude::string::String; -pub trait Guest { - fn program(&self) -> &[u8]; -} - -pub type Method = String; - -pub trait Input { - fn method(&self) -> Method; - fn args(&self) -> &[u8]; -} diff --git a/pvq-extension/src/lib.rs b/pvq-extension/src/lib.rs index 849e6a0..0f12aa3 100644 --- a/pvq-extension/src/lib.rs +++ b/pvq-extension/src/lib.rs @@ -1,138 +1,40 @@ #![cfg_attr(not(feature = "std"), no_std)] -use parity_scale_codec::Decode; -use pvq_executor::{Caller, Linker, PvqExecutor, PvqExecutorContext}; -use scale_info::prelude::{format, marker::PhantomData, vec::Vec}; -pub type PvqResponse = Vec; -use pvq_primitives::{PvqError, PvqResult}; - -mod dispatchable; -pub use dispatchable::{DispatchError, Dispatchable}; -mod extension_id; -pub mod metadata; -pub use extension_id::{ExtensionId, ExtensionIdTy}; -pub use metadata::{CallMetadata, ExtensionImplMetadata}; +//! # PVQ Extension System +//! +//! This crate provides an extension system for PVQ (PolkaVM Query). +//! It allows defining and implementing extensions that can be called from PVQ queries. +//! +//! ## Overview +//! +//! The extension system consists of: +//! +//! - **Extension Definitions**: Traits that define the API of an extension +//! - **Extension Implementations**: Implementations of extension traits +//! - **Extension Executor**: A runtime for executing queries +//! - **Permission Control**: Access control mechanisms to control access to extensions +//! +//! ## Usage +//! +//! Extensions are defined using the `#[extension_decl]` macro and implemented using the +//! `#[extensions_impl]` macro. See the examples directory for usage examples. + +// Re-exports +pub use pvq_extension_procedural::{extension_decl, extensions_impl}; + +// Module declarations +mod calldata; +mod context; mod error; -pub use error::ExtensionError; +mod executor; mod macros; -pub use pvq_extension_procedural::{decl_extensions, impl_extensions}; - +pub mod metadata; mod perm_controller; -pub use perm_controller::{InvokeSource, PermissionController}; - -mod guest; -pub use guest::{Guest, Input, Method}; - -// Call data -pub trait CallData: Dispatchable + CallMetadata + ExtensionId + Decode {} -impl CallData for T where T: Dispatchable + CallMetadata + ExtensionId + Decode {} -pub trait CallDataTuple { - fn dispatch(extension_id: ExtensionIdTy, data: &[u8]) -> Result, ExtensionError>; - // TODO: check if use metadata api - fn return_ty(extension_id: ExtensionIdTy, call_index: u32) -> Result, ExtensionError>; -} - -struct Context { - invoke_source: InvokeSource, - user_data: (), - _marker: PhantomData<(C, P)>, -} - -impl Context { - pub fn new(invoke_source: InvokeSource) -> Self { - Self { - invoke_source, - user_data: (), - _marker: PhantomData, - } - } -} - -impl PvqExecutorContext for Context { - type UserData = (); - type UserError = ExtensionError; - fn data(&mut self) -> &mut Self::UserData { - &mut self.user_data - } - fn register_host_functions(&mut self, linker: &mut Linker) { - let invoke_source = self.invoke_source; - linker - .define_typed( - "host_call", - move |caller: Caller<'_, Self::UserData>, - extension_id: u64, - call_ptr: u32, - call_len: u32| - -> Result { - // useful closure to handle early return - let call_bytes = caller.instance.read_memory(call_ptr, call_len)?; - tracing::info!("(host call): call_ptr: {}, call_len: {:?}", call_ptr, call_len); - tracing::info!( - "(host call): extension_id: {}, call_bytes: {:?}", - extension_id, - call_bytes - ); - if !P::is_allowed(extension_id, &call_bytes, invoke_source) { - return Err(ExtensionError::PermissionError); - } - let res_bytes = C::dispatch(extension_id, &call_bytes)?; - tracing::debug!("(host call): res_bytes: {:?}", res_bytes); - let res_bytes_len = res_bytes.len(); - let res_ptr = caller - .instance - .sbrk(0) - .map_err(|_| ExtensionError::MemoryAllocationError)? - .ok_or(ExtensionError::MemoryAllocationError)?; - caller - .instance - .sbrk(res_bytes_len as u32) - .map_err(|_| ExtensionError::MemoryAllocationError)? - .ok_or(ExtensionError::MemoryAllocationError)?; - caller.instance.write_memory(res_ptr, &res_bytes)?; - Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) - }, - ) - .unwrap(); - linker - .define_typed( - "return_ty", - move |caller: Caller<_>, extension_id: u64, call_index: u32| -> Result { - let res_bytes = C::return_ty(extension_id, call_index)?; - tracing::debug!("(host call): res_bytes: {:?}", res_bytes); - let res_bytes_len = res_bytes.len(); - let res_ptr = caller - .instance - .sbrk(0) - .map_err(|_| ExtensionError::MemoryAllocationError)? - .ok_or(ExtensionError::MemoryAllocationError)?; - caller - .instance - .sbrk(res_bytes_len as u32) - .map_err(|_| ExtensionError::MemoryAllocationError)? - .ok_or(ExtensionError::MemoryAllocationError)?; - caller.instance.write_memory(res_ptr, &res_bytes)?; - Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) - }, - ) - .unwrap(); - } -} - -pub struct ExtensionsExecutor { - executor: PvqExecutor>, -} -impl ExtensionsExecutor { - #[allow(dead_code)] - pub fn new(source: InvokeSource) -> Self { - let context = Context::::new(source); - let executor = PvqExecutor::new(Default::default(), context); - Self { executor } - } - - #[allow(dead_code)] - pub fn execute_method(&mut self, query: &[u8], input: &[u8]) -> PvqResult { - self.executor - .execute(query, input) - .map_err(|e| PvqError::Custom(format!("{:?}", e))) - } -} +// Public exports +pub use calldata::{CallData, DispatchError, Dispatchable, ExtensionId, ExtensionIdTy}; +pub use context::Context; +pub use error::ExtensionError; +pub use executor::ExtensionsExecutor; +pub use macros::CallDataTuple; +pub use metadata::{ExtensionImplMetadata, ExtensionMetadata}; +pub use perm_controller::{InvokeSource, PermissionController}; diff --git a/pvq-extension/src/macros.rs b/pvq-extension/src/macros.rs index fcc872e..4d8e90e 100644 --- a/pvq-extension/src/macros.rs +++ b/pvq-extension/src/macros.rs @@ -1,11 +1,22 @@ -use crate::CallData; -use crate::CallDataTuple; -use crate::ExtensionError; -use crate::ExtensionIdTy; -use crate::Vec; +use crate::{CallData, ExtensionError, ExtensionIdTy}; use fortuples::fortuples; -use parity_scale_codec::Encode; -use scale_info::{PortableRegistry, Registry}; +use scale_info::prelude::vec::Vec; + +/// Trait for a tuple of extension call data types +pub trait CallDataTuple { + /// Dispatch a call to an extension + /// + /// # Arguments + /// + /// * `extension_id` - The ID of the extension to call + /// * `data` - The encoded call data + /// + /// # Returns + /// + /// The encoded response data or an error + fn dispatch(extension_id: ExtensionIdTy, data: &[u8]) -> Result, ExtensionError>; +} + // Use the macro to implement ExtensionTuple for tuples of different lengths fortuples! { impl CallDataTuple for #Tuple where #(#Member: CallData),*{ @@ -19,20 +30,5 @@ fortuples! { )* Err(ExtensionError::UnsupportedExtension) } - #[allow(unused_variables)] - fn return_ty(extension_id: ExtensionIdTy, call_index: u32) -> Result, ExtensionError> { - #( - if extension_id == #Member::EXTENSION_ID { - let extension_metadata = #Member::call_metadata(); - // comparing the registry is equivalent to comparing the type - let return_ty = extension_metadata.methods[call_index as usize].output; - let mut registry = Registry::new(); - registry.register_type(&return_ty); - let portable_registry: PortableRegistry = registry.into(); - return Ok(portable_registry.encode()); - } - )* - Err(ExtensionError::UnsupportedExtension) - } } } diff --git a/pvq-extension/src/metadata.rs b/pvq-extension/src/metadata.rs index f6e136e..3d0e78c 100644 --- a/pvq-extension/src/metadata.rs +++ b/pvq-extension/src/metadata.rs @@ -1,12 +1,8 @@ -use crate::extension_id; -// This trait is for CallData -pub trait CallMetadata { - fn call_metadata() -> ExtensionMetadata; -} +use crate::ExtensionIdTy; // This trait is for ExtensionImpl pub trait ExtensionImplMetadata { - fn extension_metadata(extension_id: extension_id::ExtensionIdTy) -> ExtensionMetadata; + fn extension_metadata(extension_id: ExtensionIdTy) -> ExtensionMetadata; } use parity_scale_codec::Encode; @@ -37,7 +33,7 @@ impl Metadata { #[derive(Clone, PartialEq, Eq, Encode, Debug)] pub struct ExtensionMetadata { pub name: T::String, - pub methods: Vec>, + pub functions: Vec>, } impl IntoPortable for ExtensionMetadata { @@ -46,27 +42,27 @@ impl IntoPortable for ExtensionMetadata { fn into_portable(self, registry: &mut Registry) -> Self::Output { ExtensionMetadata { name: self.name.into_portable(registry), - methods: registry.map_into_portable(self.methods), + functions: registry.map_into_portable(self.functions), } } } -/// Metadata of a runtime method. +/// Metadata of a runtime function. #[derive(Clone, PartialEq, Eq, Encode, Debug)] -pub struct MethodMetadata { +pub struct FunctionMetadata { /// Method name. pub name: T::String, /// Method parameters. - pub inputs: Vec>, + pub inputs: Vec>, /// Method output. pub output: T::Type, } -impl IntoPortable for MethodMetadata { - type Output = MethodMetadata; +impl IntoPortable for FunctionMetadata { + type Output = FunctionMetadata; fn into_portable(self, registry: &mut Registry) -> Self::Output { - MethodMetadata { + FunctionMetadata { name: self.name.into_portable(registry), inputs: registry.map_into_portable(self.inputs), output: registry.register_type(&self.output), @@ -76,18 +72,18 @@ impl IntoPortable for MethodMetadata { /// Metadata of a runtime method parameter. #[derive(Clone, PartialEq, Eq, Encode, Debug)] -pub struct MethodParamMetadata { +pub struct FunctionParamMetadata { /// Parameter name. pub name: T::String, /// Parameter type. pub ty: T::Type, } -impl IntoPortable for MethodParamMetadata { - type Output = MethodParamMetadata; +impl IntoPortable for FunctionParamMetadata { + type Output = FunctionParamMetadata; fn into_portable(self, registry: &mut Registry) -> Self::Output { - MethodParamMetadata { + FunctionParamMetadata { name: self.name.into_portable(registry), ty: registry.register_type(&self.ty), } diff --git a/pvq-extension/src/perm_controller.rs b/pvq-extension/src/perm_controller.rs index 40ae1a7..8a98ef4 100644 --- a/pvq-extension/src/perm_controller.rs +++ b/pvq-extension/src/perm_controller.rs @@ -1,18 +1,43 @@ use crate::ExtensionIdTy; + +/// Source of an extension invocation +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum InvokeSource { + /// Invoked from a runtime API + RuntimeAPI, + + /// Invoked from XCM (Cross-Consensus Message) + XCM, + + /// Invoked from an extrinsic + Extrinsic, + + /// Invoked from the runtime inside + Runtime, +} + +/// Controller for extension permissions +/// +/// This trait is used to control access to extensions based on the extension ID, +/// call data, and invocation source. pub trait PermissionController { + /// Check if a call to an extension is allowed + /// + /// # Arguments + /// + /// * `extension_id` - The ID of the extension + /// * `call` - The encoded call data + /// * `source` - The source of the invocation + /// + /// # Returns + /// + /// `true` if the call is allowed, `false` otherwise fn is_allowed(extension_id: ExtensionIdTy, call: &[u8], source: InvokeSource) -> bool; } +/// Default permission controller that allows everything impl PermissionController for () { - fn is_allowed(_extension_id: ExtensionIdTy, _call: &[u8], _context: InvokeSource) -> bool { + fn is_allowed(_extension_id: ExtensionIdTy, _call: &[u8], _source: InvokeSource) -> bool { true } } - -#[derive(Copy, Clone)] -pub enum InvokeSource { - RuntimeAPI, - XCM, - Extrinsic, - Runtime, -} diff --git a/pvq-extension/tests/lib.rs b/pvq-extension/tests/lib.rs new file mode 100644 index 0000000..a863114 --- /dev/null +++ b/pvq-extension/tests/lib.rs @@ -0,0 +1,186 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +mod extension_core { + use parity_scale_codec::{Codec, Decode, Encode}; + use pvq_extension::{DispatchError, Dispatchable, ExtensionId, ExtensionIdTy}; + + pub trait ExtensionCore { + type ExtensionId: Codec + scale_info::TypeInfo + 'static; + fn has_extension(id: Self::ExtensionId) -> bool; + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] + #[allow(non_camel_case_types)] + pub enum Functions { + has_extension { + id: Impl::ExtensionId, + }, + #[doc(hidden)] + __marker(core::marker::PhantomData), + } + + impl Dispatchable for Functions { + fn dispatch(self) -> Result, DispatchError> { + match self { + Functions::has_extension { id } => Ok(Impl::has_extension(id).encode()), + Functions::__marker(_) => Err(DispatchError::PhantomData), + } + } + } + + impl ExtensionId for Functions { + const EXTENSION_ID: ExtensionIdTy = 0u64; + } + + pub fn metadata() -> pvq_extension::metadata::ExtensionMetadata { + pvq_extension::metadata::ExtensionMetadata { + name: "ExtensionCore", + functions: vec![pvq_extension::metadata::FunctionMetadata { + name: "has_extension", + inputs: vec![pvq_extension::metadata::FunctionParamMetadata { + name: "id", + ty: scale_info::meta_type::(), + }], + output: scale_info::meta_type::(), + }], + } + } +} + +mod extension_fungibles { + use parity_scale_codec::{Codec, Decode, Encode}; + use pvq_extension::{DispatchError, Dispatchable, ExtensionId, ExtensionIdTy}; + + pub trait ExtensionFungibles { + type AssetId: Codec + scale_info::TypeInfo + 'static; + type AccountId: Codec + scale_info::TypeInfo + 'static; + type Balance: Codec + scale_info::TypeInfo + 'static; + fn total_supply(asset: Self::AssetId) -> Self::Balance; + fn balance(asset: Self::AssetId, who: Self::AccountId) -> Self::Balance; + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] + #[allow(non_camel_case_types)] + pub enum Functions { + total_supply { + asset: Impl::AssetId, + }, + balance { + asset: Impl::AssetId, + who: Impl::AccountId, + }, + #[doc(hidden)] + __marker(core::marker::PhantomData), + } + + impl Dispatchable for Functions { + fn dispatch(self) -> Result, DispatchError> { + match self { + Functions::total_supply { asset } => Ok(Impl::total_supply(asset).encode()), + Functions::balance { asset, who } => Ok(Impl::balance(asset, who).encode()), + Functions::__marker(_) => Err(DispatchError::PhantomData), + } + } + } + + impl ExtensionId for Functions { + const EXTENSION_ID: ExtensionIdTy = 1u64; + } + + pub fn metadata() -> pvq_extension::metadata::ExtensionMetadata { + pvq_extension::metadata::ExtensionMetadata { + name: "ExtensionFungibles", + functions: vec![ + pvq_extension::metadata::FunctionMetadata { + name: "total_supply", + inputs: vec![pvq_extension::metadata::FunctionParamMetadata { + name: "asset", + ty: scale_info::meta_type::(), + }], + output: scale_info::meta_type::(), + }, + pvq_extension::metadata::FunctionMetadata { + name: "balance", + inputs: vec![ + pvq_extension::metadata::FunctionParamMetadata { + name: "asset", + ty: scale_info::meta_type::(), + }, + pvq_extension::metadata::FunctionParamMetadata { + name: "who", + ty: scale_info::meta_type::(), + }, + ], + output: scale_info::meta_type::(), + }, + ], + } + } +} + +mod extensions_impl { + pub struct ExtensionsImpl; + use super::*; + + impl extension_core::ExtensionCore for ExtensionsImpl { + type ExtensionId = u64; + fn has_extension(id: u64) -> bool { + matches!(id, 0 | 1) + } + } + + impl extension_fungibles::ExtensionFungibles for ExtensionsImpl { + type AssetId = u32; + type AccountId = [u8; 32]; + type Balance = u64; + fn total_supply(asset: u32) -> u64 { + 200 + } + fn balance(asset: u32, who: [u8; 32]) -> u64 { + 100 + } + } + + pub type Extensions = ( + extension_core::Functions, + extension_fungibles::Functions, + ); + + pub fn metadata() -> pvq_extension::metadata::Metadata { + pvq_extension::metadata::Metadata::new(vec![ + extension_core::metadata::(), + extension_fungibles::metadata::(), + ]) + } +} + +mod tests { + use super::*; + use parity_scale_codec::Encode; + use pvq_extension::{ExtensionsExecutor, InvokeSource}; + use tracing_subscriber::prelude::*; + + #[test] + fn test_runtime_executor() { + let registry = tracing_subscriber::registry(); + + let filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive(tracing::Level::DEBUG.into()) + .from_env_lossy(); + + registry + .with(tracing_subscriber::fmt::layer().with_filter(filter)) + .try_init() + .expect("Failed to initialize tracing"); + + let mut executor = ExtensionsExecutor::::new(InvokeSource::Runtime); + let program_blob = include_bytes!("../../output/poc-guest-transparent-call.polkavm").to_vec(); + let mut args = vec![]; + args.extend(1u64.to_le_bytes()); + extension_fungibles::Functions::::total_supply { asset: 1u32 } + .encode_to(&mut args); + let res = executor.execute_method(&program_blob, &args, 0); + println!("res: {:?}", res); + assert_eq!(res, Ok(200u64.to_le_bytes().to_vec())); + } +} diff --git a/pvq-extension/tests/with_associated_types_works.rs b/pvq-extension/tests/with_associated_types_works.rs deleted file mode 100644 index 96f5269..0000000 --- a/pvq-extension/tests/with_associated_types_works.rs +++ /dev/null @@ -1,164 +0,0 @@ -use parity_scale_codec::{Codec, Decode, Encode}; -use pvq_extension::metadata::Metadata; -use pvq_extension::{decl_extensions, impl_extensions, ExtensionsExecutor, InvokeSource}; - -mod extension_core { - use super::*; - pub trait Config { - type ExtensionId: Codec; - } - decl_extensions! { - pub trait ExtensionCore { - type Config:Config; - fn has_extension(id: ::ExtensionId) -> bool; - // crypto functions - // fn blake2_64(data: Vec) -> [u8; 8]; - // fn blake2_128(data: Vec) -> [u8; 16]; - // fn blake2_256(data: Vec) -> [u8; 32]; - // fn twox_64(data: Vec) -> [u8; 8]; - // fn read_storage(key: Vec) -> Option>; - } - } -} - -mod extension_fungibles { - use super::*; - pub trait Config { - type AssetId: Codec; - type AccountId: Codec; - type Balance: Codec; - } - decl_extensions! { - pub trait ExtensionFungibles { - type Config:Config; - fn total_supply(asset: ::AssetId) -> ::Balance; - fn balance(asset: ::AssetId, who: ::AccountId) -> ::Balance; - } - } -} - -pub struct ExtensionImpl; - -impl_extensions! { - impl extension_core::ExtensionCore for ExtensionImpl { - type Config = ExtensionImpl; - fn has_extension(id: ::ExtensionId) -> bool { - matches!(id, 0 | 1) - } - } - - impl extension_fungibles::ExtensionFungibles for ExtensionImpl { - type Config = ExtensionImpl; - #[allow(unused_variables)] - fn total_supply(asset: ::AssetId) -> ::Balance { - 200 - } - #[allow(unused_variables)] - fn balance(asset: ::AssetId, who: ::AccountId) -> ::Balance { - 100 - } - } -} -impl extension_core::Config for ExtensionImpl { - type ExtensionId = u64; -} - -impl extension_fungibles::Config for ExtensionImpl { - type AssetId = u32; - type AccountId = [u8; 32]; - type Balance = u64; -} - -#[derive(Encode, Decode)] -enum CoreMethod { - HasExtension { id: u64 }, -} - -#[derive(Encode, Decode)] -enum FungiblesMethod { - TotalSupply { asset: u32 }, - Balance { asset: u32, who: [u8; 32] }, -} -#[test] -fn call_core_works() { - let blob = include_bytes!("../../output/poc-guest-transparent-call.polkavm"); - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - let method = CoreMethod::HasExtension { id: 0 }; - let mut input_data = extension_core::EXTENSION_ID.encode(); - input_data.extend_from_slice(&method.encode()); - let res = executor.execute_method(blob, &input_data).unwrap(); - assert_eq!(res, vec![1]); -} -#[test] -fn multi_calls_works() { - let blob = include_bytes!("../../output/poc-guest-sum-balance-percent.polkavm"); - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - let mut input_data = extension_fungibles::EXTENSION_ID.encode(); - input_data.extend_from_slice(&[2u8]); - let method1 = FungiblesMethod::Balance { - asset: 1, - who: [0u8; 32], - }; - let method1_encoded = method1.encode(); - input_data.extend_from_slice(&[method1_encoded.len() as u8]); - let method2 = FungiblesMethod::Balance { - asset: 1, - who: [1u8; 32], - }; - input_data.extend_from_slice(&method1_encoded); - input_data.extend_from_slice(&method2.encode()); - input_data.extend_from_slice(&extension_fungibles::EXTENSION_ID.encode()); - let method3 = FungiblesMethod::TotalSupply { asset: 1 }; - let method3_encoded = method3.encode(); - input_data.extend_from_slice(&[method3_encoded.len() as u8]); - input_data.extend_from_slice(&method3_encoded); - let res = executor.execute_method(blob, &input_data).unwrap(); - assert_eq!(res, vec![100u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]); -} - -#[test] -fn calls_vec_works() { - let blob = include_bytes!("../../output/poc-guest-sum-balance.polkavm"); - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - let mut input_data = extension_fungibles::EXTENSION_ID.encode(); - input_data.extend_from_slice(&vec![2u8]); - let method1 = FungiblesMethod::Balance { - asset: 1, - who: [0u8; 32], - }; - let method1_encoded = method1.encode(); - input_data.extend_from_slice(&vec![method1_encoded.len() as u8]); - let method2 = FungiblesMethod::Balance { - asset: 2, - who: [0u8; 32], - }; - input_data.extend_from_slice(&method1_encoded); - input_data.extend_from_slice(&method2.encode()); - let res = executor.execute_method(blob, &input_data).unwrap(); - assert_eq!(res, vec![200u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]); -} - -#[test] -fn single_call_works() { - let blob = include_bytes!("../../output/poc-guest-total-supply.polkavm"); - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - let mut input_data = extension_fungibles::EXTENSION_ID.encode(); - let method1 = FungiblesMethod::TotalSupply { asset: 1 }; - let method1_encoded = method1.encode(); - input_data.extend_from_slice(&vec![method1_encoded.len() as u8]); - input_data.extend_from_slice(&method1_encoded); - let res = executor.execute_method(blob, &input_data).unwrap(); - assert_eq!(res, vec![200u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]); -} - -#[test] -fn metadata_works() { - let metadata: Metadata = ExtensionImpl::metadata(); - let registry = metadata.types; - let extension_metadata_list = metadata.extensions; - // bool, u8, u32, u64, [u8;32] - assert_eq!(registry.types.len(), 5); - assert_eq!(extension_metadata_list.len(), 2); - assert_eq!(extension_metadata_list[0].name, "ExtensionCore"); - assert_eq!(extension_metadata_list[1].name, "ExtensionFungibles"); -} diff --git a/pvq-test-runner/src/main.rs b/pvq-test-runner/src/main.rs index fc2f7e2..b17b3a9 100644 --- a/pvq-test-runner/src/main.rs +++ b/pvq-test-runner/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; -use parity_scale_codec::{Decode, Encode}; -use pvq_extension::{impl_extensions, ExtensionsExecutor, InvokeSource}; +use parity_scale_codec::Encode; +use pvq_extension::{extensions_impl, ExtensionsExecutor, InvokeSource}; use tracing_subscriber::prelude::*; #[derive(Parser, Debug)] @@ -26,69 +26,52 @@ fn main() { let cli = Cli::parse(); let blob = std::fs::read(cli.program).expect("Failed to read program"); - let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); - let mut input_data = pvq_extension_fungibles::EXTENSION_ID.encode(); + let mut executor = ExtensionsExecutor::::new(InvokeSource::RuntimeAPI); + let mut input_data = pvq_extension_core::extension::extension_id().encode(); input_data.extend_from_slice(&[2u8]); - let method1 = FungiblesMethod::Balance { + let method1 = pvq_extension_fungibles::extension::Functions::::balance { asset: 1, who: [0u8; 32], - }; + } + .encode(); let method1_encoded = method1.encode(); input_data.extend_from_slice(&[method1_encoded.len() as u8]); - let method2 = FungiblesMethod::Balance { + let method2 = pvq_extension_fungibles::extension::Functions::::balance { asset: 1, who: [1u8; 32], - }; + } + .encode(); input_data.extend_from_slice(&method1_encoded); input_data.extend_from_slice(&method2.encode()); tracing::info!("Input data: {:?}", input_data); - let res = executor.execute_method(&blob, &input_data).unwrap(); + let res = executor.execute_method(&blob, &input_data, 0).unwrap(); tracing::info!("Result: {:?}", res); } -pub struct ExtensionImpl; - -impl pvq_extension_core::Config for ExtensionImpl { - type ExtensionId = u64; -} - -impl pvq_extension_fungibles::Config for ExtensionImpl { - type AssetId = u32; - type AccountId = [u8; 32]; - type Balance = u64; -} +#[extensions_impl] +pub mod extensions { + #[extensions_impl::impl_struct] + pub struct ExtensionsImpl; -impl_extensions! { - impl pvq_extension_core::ExtensionCore for ExtensionImpl { - type Config = ExtensionImpl; - fn has_extension(id: ::ExtensionId) -> bool { + #[extensions_impl::extension] + impl pvq_extension_core::extension::ExtensionCore for ExtensionsImpl { + type ExtensionId = u64; + fn has_extension(id: Self::ExtensionId) -> bool { matches!(id, 0 | 1) } } - impl pvq_extension_fungibles::ExtensionFungibles for ExtensionImpl { - type Config = ExtensionImpl; - #[allow(unused_variables)] - fn balance( - asset: ::AssetId, - who: ::AccountId, - ) -> ::Balance { + #[extensions_impl::extension] + impl pvq_extension_fungibles::extension::ExtensionFungibles for ExtensionsImpl { + type AssetId = u32; + type AccountId = [u8; 32]; + type Balance = u64; + fn total_supply(_asset: Self::AssetId) -> Self::Balance { 100 } - #[allow(unused_variables)] - fn total_supply(asset: ::AssetId) -> ::Balance { - 200 + fn balance(_asset: Self::AssetId, _who: Self::AccountId) -> Self::Balance { + 100 } } } -#[derive(Encode, Decode)] -enum CoreMethod { - HasExtension { id: u64 }, -} - -#[derive(Encode, Decode)] -enum FungiblesMethod { - TotalSupply { asset: u64 }, - Balance { asset: u64, who: [u8; 32] }, -}