diff --git a/Cargo.lock b/Cargo.lock index 1741b55968..dc96363599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,15 @@ dependencies = [ "gimli", ] +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -33,7 +42,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -42,10 +51,20 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", ] +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + [[package]] name = "aes" version = "0.6.0" @@ -70,17 +89,14 @@ dependencies = [ ] [[package]] -name = "aes-gcm" -version = "0.8.0" +name = "aes" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "aead 0.3.2", - "aes 0.6.0", - "cipher 0.2.5", - "ctr 0.6.0", - "ghash 0.3.1", - "subtle", + "cfg-if", + "cipher 0.4.4", + "cpufeatures", ] [[package]] @@ -97,6 +113,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead 0.5.2", + "aes 0.8.3", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + [[package]] name = "aes-soft" version = "0.6.4" @@ -123,7 +153,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -135,20 +165,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.8", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[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" @@ -184,15 +226,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -208,9 +250,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -218,9 +260,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "approx" @@ -288,7 +330,7 @@ dependencies = [ "ark-serialize", "ark-std", "derivative", - "digest 0.10.6", + "digest 0.10.7", "itertools", "num-bigint", "num-traits", @@ -304,7 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -317,7 +359,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -341,7 +383,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-serialize-derive", "ark-std", - "digest 0.10.6", + "digest 0.10.7", "num-bigint", ] @@ -353,7 +395,7 @@ checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -374,9 +416,9 @@ checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -386,9 +428,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asn1-rs" @@ -403,14 +445,14 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.25", ] [[package]] name = "asn1-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive 0.4.0", "asn1-rs-impl", @@ -419,7 +461,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.25", ] [[package]] @@ -430,7 +472,7 @@ checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -442,7 +484,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -454,7 +496,7 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -465,9 +507,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", @@ -488,7 +530,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.19", + "rustix 0.37.23", "slab", "socket2 0.4.9", "waker-fn", @@ -496,12 +538,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -512,14 +553,14 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] name = "asynchronous-codec" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ "bytes", "futures-sink", @@ -539,9 +580,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -556,14 +597,14 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -574,16 +615,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line", + "addr2line 0.20.0", "cc", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.31.1", "rustc-demangle", ] @@ -613,15 +654,24 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-toml" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bfc506e7a2370ec239e1d072507b2a80c833083699d3c6fa176fbb4de8448c6" +dependencies = [ + "serde", +] [[package]] name = "beef" @@ -653,13 +703,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.6", + "prettyplease 0.2.12", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -670,9 +720,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bitvec" @@ -692,7 +742,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -702,32 +752,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.2.4", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", ] [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", ] [[package]] name = "blake3" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "cc", "cfg-if", - "constant_time_eq 0.2.4", + "constant_time_eq 0.3.0", ] [[package]] @@ -748,16 +798,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -805,9 +855,20 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.1.0" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata 0.1.10", +] + +[[package]] +name = "bstr" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -824,9 +885,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-slice-cast" @@ -871,18 +932,18 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.2" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" dependencies = [ "serde", ] @@ -895,19 +956,26 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_json", "thiserror", ] +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + [[package]] name = "cc" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -932,9 +1000,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70d3ad08698a0568b0562f22710fe6bfc1f4a61a367c77d0398c562eadd453a" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" dependencies = [ "smallvec", ] @@ -978,13 +1046,13 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time 0.1.45", "wasm-bindgen", @@ -1010,7 +1078,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1019,14 +1087,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", ] [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1065,7 +1143,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -1103,30 +1181,70 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" @@ -1140,9 +1258,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core2" @@ -1164,19 +1282,13 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "cranelift-bforest" version = "0.95.1" @@ -1277,9 +1389,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] @@ -1301,9 +1413,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1311,9 +1423,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -1322,14 +1434,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.9.0", "scopeguard", ] @@ -1345,9 +1457,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1364,7 +1476,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1376,7 +1488,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1388,7 +1500,8 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -1398,17 +1511,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -1418,26 +1521,26 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] [[package]] name = "ctr" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher 0.2.5", + "cipher 0.3.0", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.3.0", + "cipher 0.4.4", ] [[package]] @@ -1482,9 +1585,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.87" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" +checksum = "ba1ba0a82363c553ecb7b4cd58ba6e1c017baef8e3cca4e7d66ca17879201144" dependencies = [ "cc", "cxxbridge-flags", @@ -1494,9 +1597,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.87" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" +checksum = "ac9ec8372f860c6ee7c6463b96a26d08dd590bcbcd9bf2d1894db09ae81410d3" dependencies = [ "cc", "codespan-reporting", @@ -1504,31 +1607,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "cxxbridge-flags" -version = "1.0.87" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" +checksum = "409667bbb937bae87f7cfa91ca29e1415bb92d415371e3344b5269c10d90d595" [[package]] name = "cxxbridge-macro" -version = "1.0.87" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" +checksum = "5fb2a9757fb085d6d97856b28d4f049141ca4a61a64c697f4426433b5f6caa1f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "darling" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -1536,40 +1639,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "data-encoding-macro" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1577,12 +1680,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" dependencies = [ "data-encoding", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1598,9 +1701,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.5" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", @@ -1622,11 +1725,11 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "displaydoc", "nom", "num-bigint", @@ -1634,6 +1737,12 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" + [[package]] name = "derivative" version = "2.2.0" @@ -1642,7 +1751,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1653,7 +1762,7 @@ checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1674,7 +1783,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1684,7 +1793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" dependencies = [ "derive_builder_core", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1693,11 +1802,19 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 1.0.107", + "rustc_version", + "syn 1.0.109", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" @@ -1719,16 +1836,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -1777,13 +1894,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -1800,9 +1917,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dtoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00704156a7de8df8da0911424e30c2049957b0a714542a44e05fe693dd85313" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dyn-clonable" @@ -1822,14 +1939,14 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1845,13 +1962,13 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.5", - "digest 0.10.6", - "elliptic-curve 0.13.4", + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.5", "rfc6979 0.4.0", "signature 2.1.0", "spki 0.7.2", @@ -1896,9 +2013,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" dependencies = [ "serde", ] @@ -1912,9 +2029,9 @@ dependencies = [ "base16ct 0.1.1", "crypto-bigint 0.4.9", "der 0.6.1", - "digest 0.10.6", + "digest 0.10.7", "ff 0.12.1", - "generic-array 0.14.6", + "generic-array 0.14.7", "group 0.12.1", "hkdf", "pem-rfc7468", @@ -1927,23 +2044,29 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" dependencies = [ "base16ct 0.2.0", "crypto-bigint 0.5.2", - "digest 0.10.6", + "digest 0.10.7", "ff 0.13.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "group 0.13.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.1", + "sec1 0.7.3", "subtle", "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enum-as-inner" version = "0.5.1" @@ -1953,7 +2076,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1983,9 +2106,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -2130,7 +2253,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -2147,9 +2270,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2411,13 +2534,13 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] @@ -2457,9 +2580,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", @@ -2484,7 +2607,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project", - "spin 0.9.7", + "spin 0.9.8", ] [[package]] @@ -2518,9 +2641,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2782,7 +2905,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -2794,7 +2917,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -2804,7 +2927,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#2b76b44e6f7e dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -3061,11 +3184,11 @@ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand 1.8.0", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -3082,7 +3205,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -3152,9 +3275,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -3184,9 +3307,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -3195,22 +3318,22 @@ dependencies = [ [[package]] name = "ghash" -version = "0.3.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.4.5", + "polyval 0.5.3", ] [[package]] name = "ghash" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.5.3", + "polyval 0.6.1", ] [[package]] @@ -3220,7 +3343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" dependencies = [ "fallible-iterator", - "indexmap 1.9.2", + "indexmap 1.9.3", "stable_deref_trait", ] @@ -3232,12 +3355,12 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", - "bstr", + "bstr 1.6.0", "fnv", "log", "regex", @@ -3277,7 +3400,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.2", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -3286,9 +3409,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -3342,21 +3465,25 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] [[package]] name = "hashlink" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.0", ] [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" dependencies = [ "unicode-segmentation", ] @@ -3372,18 +3499,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -3416,16 +3534,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.11.0" @@ -3442,7 +3550,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3452,7 +3560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3469,9 +3577,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3491,9 +3599,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -3547,7 +3655,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.21.5", + "rustls 0.21.6", "rustls-native-certs", "tokio", "tokio-rustls", @@ -3555,26 +3663,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -3596,9 +3703,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3616,9 +3723,9 @@ dependencies = [ [[package]] name = "if-watch" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" dependencies = [ "async-io", "core-foundation", @@ -3630,7 +3737,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows", + "windows 0.34.0", ] [[package]] @@ -3668,14 +3775,14 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -3692,6 +3799,15 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.12" @@ -3731,11 +3847,11 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.2", "libc", "windows-sys 0.48.0", ] @@ -3748,31 +3864,30 @@ checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" [[package]] name = "ipconfig" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.4.9", + "socket2 0.5.3", "widestring", - "winapi", + "windows-sys 0.48.0", "winreg", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.19", + "hermit-abi 0.3.2", + "rustix 0.38.8", "windows-sys 0.48.0", ] @@ -3787,15 +3902,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -3829,7 +3944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "async-trait", "beef", "futures-channel", @@ -3858,7 +3973,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3904,17 +4019,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.7", - "elliptic-curve 0.13.4", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -3998,7 +4113,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.8", + "getrandom 0.2.10", "instant", "libp2p-allow-block-list", "libp2p-connection-limits", @@ -4124,7 +4239,7 @@ dependencies = [ "multihash", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "zeroize", ] @@ -4135,7 +4250,7 @@ version = "0.43.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "asynchronous-codec", "bytes", "either", @@ -4149,7 +4264,7 @@ dependencies = [ "log", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "uint", @@ -4207,7 +4322,7 @@ dependencies = [ "once_cell", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "snow", "static_assertions", "thiserror", @@ -4299,7 +4414,7 @@ checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" dependencies = [ "heck", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4490,9 +4605,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "pkg-config", @@ -4501,9 +4616,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] @@ -4525,9 +4640,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" +checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" dependencies = [ "nalgebra", ] @@ -4540,9 +4655,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" @@ -4552,9 +4667,9 @@ checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -4615,49 +4730,65 @@ dependencies = [ [[package]] name = "macro_magic" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614b1304ab7877b499925b4dcc5223ff480f2646ad4db1ee7065badb8d530439" +checksum = "aee866bfee30d2d7e83835a4574aad5b45adba4cc807f2a3bbba974e5d4383c9" dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] name = "macro_magic_core" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d72c1b662d07b8e482c80d3a7fc4168e058b3bef4c573e94feb714b670f406" +checksum = "7e766a20fd9c72bab3e1e64ed63f36bd08410e75803813df210d1ce297d7ad00" dependencies = [ + "const-random", "derive-syn-parse", "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] name = "macro_magic_core_macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d7d9e6e234c040dafc745c7592738d56a03ad04b1fa04ab60821deb597466a" +checksum = "c12469fc165526520dff2807c2975310ab47cf7190a45b99b49a7dc8befab17b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] name = "macro_magic_macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd19f13cfd2bfbd83692adfef8c244fe5109b3eb822a1fb4e0a6253b406cd81" +checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.26", + "syn 2.0.28", +] + +[[package]] +name = "macrotest" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7489ae0986ce45414b7b3122c2e316661343ecf396b206e3e15f07c846616f10" +dependencies = [ + "diff", + "glob", + "prettyplease 0.1.25", + "serde", + "serde_json", + "syn 1.0.109", + "toml 0.5.11", ] [[package]] @@ -4678,7 +4809,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -4689,10 +4820,11 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" dependencies = [ + "autocfg", "rawpointer", ] @@ -4702,7 +4834,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4713,18 +4845,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.36.13", + "rustix 0.37.23", ] [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -4740,18 +4872,18 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -4785,9 +4917,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -4805,9 +4937,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", @@ -4820,14 +4952,14 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4870,9 +5002,9 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "unsigned-varint", ] @@ -4887,7 +5019,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -4913,9 +5045,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6515c882ebfddccaa73ead7320ca28036c4bc84c9bcca3cc0cbba8efe89223a" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ "approx", "matrixmultiply", @@ -4929,13 +5061,13 @@ dependencies = [ [[package]] name = "nalgebra-macros" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4993,9 +5125,9 @@ dependencies = [ [[package]] name = "netlink-packet-utils" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", @@ -5020,9 +5152,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "futures", @@ -5105,7 +5237,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "itoa", ] @@ -5144,20 +5276,20 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] @@ -5179,18 +5311,27 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", ] [[package]] name = "object" -version = "0.30.4" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ - "crc32fast", - "hashbrown 0.13.2", - "indexmap 1.9.2", "memchr", ] @@ -5209,7 +5350,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", ] [[package]] @@ -5232,9 +5373,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.55" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -5247,13 +5388,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -5264,9 +5405,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.90" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ "cc", "libc", @@ -5282,7 +5423,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -5293,7 +5434,7 @@ checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -5769,7 +5910,7 @@ version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bitvec", "byte-slice-cast", "bytes", @@ -5787,7 +5928,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5804,9 +5945,9 @@ checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -5853,7 +5994,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] @@ -5864,9 +6005,9 @@ checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156" [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -5883,7 +6024,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -5912,15 +6053,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -5928,9 +6069,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -5938,26 +6079,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -5967,27 +6108,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap 1.9.2", + "indexmap 1.9.3", ] [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -6024,15 +6165,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.5", + "der 0.7.8", "spki 0.7.2", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" @@ -6042,9 +6183,9 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polling" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", @@ -6064,30 +6205,31 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.4.5" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cpuid-bool", + "cfg-if", + "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] @@ -6096,6 +6238,75 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precompile-utils" +version = "0.1.0" +dependencies = [ + "derive_more", + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "impl-trait-for-tuples", + "log", + "num_enum", + "pallet-evm", + "parity-scale-codec", + "precompile-utils-macro", + "scale-info", + "serde", + "similar-asserts", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.1.0" +dependencies = [ + "case", + "fp-evm", + "frame-support", + "macrotest", + "num_enum", + "precompile-utils", + "prettyplease 0.2.12", + "proc-macro2", + "quote", + "sp-core-hashing", + "sp-std", + "syn 1.0.109", + "trybuild", +] + +[[package]] +name = "precompile-utils-tests-external" +version = "0.1.0" +dependencies = [ + "derive_more", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "precompile-utils", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "predicates" version = "2.1.5" @@ -6112,15 +6323,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -6128,22 +6339,22 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "prettyplease" -version = "0.2.6" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -6179,7 +6390,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "version_check", ] @@ -6194,6 +6405,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro-warning" version = "0.4.1" @@ -6202,7 +6419,7 @@ checksum = "70550716265d1ec349c41f70dd4f964b4fd88394efe4405f0c1da679c4799a07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -6248,14 +6465,14 @@ checksum = "72b6a5217beb0ad503ee7fa752d451c905113d70721b937126158f3106a48cc1" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "prost" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -6263,9 +6480,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -6274,35 +6491,34 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease 0.1.23", + "prettyplease 0.1.25", "prost", "prost-types", "regex", - "syn 1.0.107", + "syn 1.0.109", "tempfile", "which", ] [[package]] name = "prost-derive" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.6" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] @@ -6356,9 +6572,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" +checksum = "f31999cfc7927c4e212e60fd50934ab40e8e8bfd2d493d6095d2d306bc0764d9" dependencies = [ "bytes", "rand 0.8.5", @@ -6374,9 +6590,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -6446,7 +6662,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", ] [[package]] @@ -6475,9 +6691,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -6485,9 +6701,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -6503,7 +6719,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.25", "x509-parser 0.13.2", "yasna", ] @@ -6516,7 +6732,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.25", "yasna", ] @@ -6544,29 +6760,29 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -6583,13 +6799,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.6", + "regex-syntax 0.7.4", ] [[package]] @@ -6598,14 +6815,31 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "resolv-conf" @@ -6659,7 +6893,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6681,7 +6915,7 @@ checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6757,9 +6991,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -6779,7 +7013,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.18", ] [[package]] @@ -6793,9 +7027,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.13" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ "bitflags 1.3.2", "errno", @@ -6807,25 +7041,25 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.3.7", + "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", "linux-raw-sys 0.4.5", @@ -6859,9 +7093,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.5" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring", @@ -6871,9 +7105,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -6883,11 +7117,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", ] [[package]] @@ -6902,9 +7136,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rw-stream-sink" @@ -6919,15 +7153,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safe_arch" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ "bytemuck", ] @@ -7017,7 +7251,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -7355,7 +7589,7 @@ dependencies = [ "cfg-if", "libc", "log", - "rustix 0.36.13", + "rustix 0.36.15", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -7842,7 +8076,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -7925,16 +8159,16 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -7968,15 +8202,15 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "sct" @@ -8018,7 +8252,7 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct 0.1.1", "der 0.6.1", - "generic-array 0.14.6", + "generic-array 0.14.7", "pkcs8 0.9.0", "subtle", "zeroize", @@ -8026,13 +8260,13 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.5", - "generic-array 0.14.6", + "der 0.7.8", + "generic-array 0.14.7", "pkcs8 0.10.2", "subtle", "zeroize", @@ -8067,9 +8301,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -8080,9 +8314,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -8099,9 +8333,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -8114,22 +8348,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.179" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.179" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -8145,9 +8379,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -8165,6 +8399,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.8.2" @@ -8192,22 +8437,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -8228,9 +8473,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -8241,7 +8486,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -8251,15 +8496,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] [[package]] name = "simba" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ "approx", "num-complex", @@ -8268,6 +8513,26 @@ dependencies = [ "wide", ] +[[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf644ad016b75129f01a34a355dcb8d66a5bc803e417c7a77cc5d5ee9fa0f18" +dependencies = [ + "console", + "similar", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -8276,18 +8541,18 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" @@ -8314,7 +8579,7 @@ dependencies = [ "rand_core 0.6.4", "ring", "rustc_version", - "sha2 0.10.6", + "sha2 0.10.7", "subtle", ] @@ -8386,7 +8651,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -8577,8 +8842,8 @@ source = "git+https://github.com/paritytech/substrate?branch=master#2b76b44e6f7e dependencies = [ "blake2b_simd", "byteorder", - "digest 0.10.6", - "sha2 0.10.6", + "digest 0.10.7", + "sha2 0.10.7", "sha3", "twox-hash", ] @@ -8590,7 +8855,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#2b76b44e6f7e dependencies = [ "quote", "sp-core-hashing", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -8609,7 +8874,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#2b76b44e6f7e dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -8691,7 +8956,7 @@ version = "4.1.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#2b76b44e6f7e948c8dffb7ff72d349f0702f5acf" dependencies = [ "thiserror", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] @@ -8784,7 +9049,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -8968,7 +9233,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -9007,9 +9272,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -9031,7 +9296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.5", + "der 0.7.8", ] [[package]] @@ -9086,7 +9351,7 @@ dependencies = [ "paste", "percent-encoding", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat", "thiserror", @@ -9106,7 +9371,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -9124,10 +9389,10 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core", "sqlx-sqlite", - "syn 1.0.107", + "syn 1.0.109", "tempfile", "tokio", "url", @@ -9157,9 +9422,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.38.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" dependencies = [ "Inflector", "num-format", @@ -9207,7 +9472,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -9235,7 +9500,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -9416,7 +9681,7 @@ dependencies = [ "sp-maybe-compressed-blob", "strum", "tempfile", - "toml 0.7.3", + "toml 0.7.6", "walkdir", "wasm-opt", ] @@ -9438,9 +9703,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -9449,9 +9714,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -9466,15 +9731,15 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "unicode-xid", ] [[package]] name = "system-configuration" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -9499,9 +9764,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tempfile" @@ -9512,7 +9777,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.38.4", + "rustix 0.38.8", "windows-sys 0.48.0", ] @@ -9527,9 +9792,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" @@ -9548,7 +9813,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -9559,10 +9824,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -9577,9 +9843,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.3+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" dependencies = [ "cc", "libc", @@ -9598,10 +9864,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -9610,15 +9877,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -9635,7 +9902,7 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "rustc-hash", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -9672,9 +9939,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -9703,7 +9970,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", ] [[package]] @@ -9712,15 +9979,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.5", + "rustls 0.21.6", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite 0.2.12", @@ -9730,9 +9997,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -9754,9 +10021,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -9766,20 +10033,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap 1.9.2", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -9799,11 +10066,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "bytes", "futures-core", "futures-util", @@ -9842,20 +10109,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -9999,6 +10266,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "trybuild" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84e0202ea606ba5ebee8507ab2bfbe89b98551ed9b8f0be198109275cff284b" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + [[package]] name = "tt-call" version = "1.0.9" @@ -10031,7 +10313,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "digest 0.10.6", + "digest 0.10.7", "rand 0.8.5", "static_assertions", ] @@ -10044,9 +10326,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -10062,15 +10344,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -10111,7 +10393,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", "subtle", ] @@ -10135,12 +10427,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", ] @@ -10152,11 +10444,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.2.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.10", ] [[package]] @@ -10200,22 +10492,20 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -10258,7 +10548,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -10292,7 +10582,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10373,7 +10663,7 @@ version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" dependencies = [ - "indexmap 1.9.2", + "indexmap 1.9.3", "url", ] @@ -10386,10 +10676,10 @@ dependencies = [ "anyhow", "bincode", "cfg-if", - "indexmap 1.9.2", + "indexmap 1.9.3", "libc", "log", - "object", + "object 0.30.4", "once_cell", "paste", "psm", @@ -10421,14 +10711,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64 0.21.0", + "base64 0.21.2", "bincode", "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.13", + "rustix 0.36.15", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "toml 0.5.11", "windows-sys 0.45.0", "zstd 0.11.2+zstd.1.5.2", @@ -10448,7 +10738,7 @@ dependencies = [ "cranelift-wasm", "gimli", "log", - "object", + "object 0.30.4", "target-lexicon", "thiserror", "wasmparser", @@ -10466,7 +10756,7 @@ dependencies = [ "cranelift-codegen", "cranelift-native", "gimli", - "object", + "object 0.30.4", "target-lexicon", "wasmtime-environ", ] @@ -10480,9 +10770,9 @@ dependencies = [ "anyhow", "cranelift-entity", "gimli", - "indexmap 1.9.2", + "indexmap 1.9.3", "log", - "object", + "object 0.30.4", "serde", "target-lexicon", "thiserror", @@ -10496,14 +10786,14 @@ version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" dependencies = [ - "addr2line", + "addr2line 0.19.0", "anyhow", "bincode", "cfg-if", "cpp_demangle", "gimli", "log", - "object", + "object 0.30.4", "rustc-demangle", "serde", "target-lexicon", @@ -10520,9 +10810,9 @@ version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" dependencies = [ - "object", + "object 0.30.4", "once_cell", - "rustix 0.36.13", + "rustix 0.36.15", ] [[package]] @@ -10545,7 +10835,7 @@ dependencies = [ "anyhow", "cc", "cfg-if", - "indexmap 1.9.2", + "indexmap 1.9.3", "libc", "log", "mach", @@ -10553,7 +10843,7 @@ dependencies = [ "memoffset 0.8.0", "paste", "rand 0.8.5", - "rustix 0.36.13", + "rustix 0.36.15", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -10574,9 +10864,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -10634,10 +10924,10 @@ dependencies = [ "sdp", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "stun", "thiserror", - "time 0.3.17", + "time 0.3.25", "tokio", "turn", "url", @@ -10669,22 +10959,22 @@ dependencies = [ [[package]] name = "webrtc-dtls" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" dependencies = [ "aes 0.6.0", - "aes-gcm 0.8.0", + "aes-gcm 0.10.2", "async-trait", "bincode", "block-modes", "byteorder", "ccm", "curve25519-dalek 3.2.0", - "der-parser 8.1.0", + "der-parser 8.2.0", "elliptic-curve 0.12.3", "hkdf", - "hmac 0.10.1", + "hmac 0.12.1", "log", "oid-registry 0.6.1", "p256", @@ -10696,8 +10986,8 @@ dependencies = [ "rustls 0.19.1", "sec1 0.3.0", "serde", - "sha-1", - "sha2 0.9.9", + "sha1", + "sha2 0.10.7", "signature 1.6.4", "subtle", "thiserror", @@ -10710,9 +11000,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" dependencies = [ "arc-swap", "async-trait", @@ -10747,18 +11037,15 @@ dependencies = [ [[package]] name = "webrtc-media" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" dependencies = [ "byteorder", "bytes", - "derive_builder", - "displaydoc", "rand 0.8.5", "rtp", "thiserror", - "webrtc-util", ] [[package]] @@ -10836,9 +11123,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.8" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f" dependencies = [ "bytemuck", "safe_arch", @@ -10846,9 +11133,9 @@ dependencies = [ [[package]] name = "widestring" -version = "0.5.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -10895,18 +11182,12 @@ dependencies = [ ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", + "windows-targets 0.48.1", ] [[package]] @@ -10915,7 +11196,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.1", + "windows-targets 0.42.2", ] [[package]] @@ -10924,29 +11205,29 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -10959,9 +11240,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" @@ -10977,9 +11258,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -10995,9 +11276,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -11013,9 +11294,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -11031,9 +11312,9 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -11043,9 +11324,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -11061,9 +11342,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -11073,20 +11354,21 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "19f495880723d0999eb3500a9064d8dbcf836460b24c17df80ea7b5794053aac" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -11136,7 +11418,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.25", ] [[package]] @@ -11145,16 +11427,16 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "base64 0.13.1", "data-encoding", - "der-parser 8.1.0", + "der-parser 8.2.0", "lazy_static", "nom", "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.25", ] [[package]] @@ -11173,11 +11455,11 @@ dependencies = [ [[package]] name = "yasna" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.17", + "time 0.3.25", ] [[package]] @@ -11191,14 +11473,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", - "synstructure", + "syn 2.0.28", ] [[package]] @@ -11212,11 +11493,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -11231,9 +11512,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/Cargo.toml b/Cargo.toml index bcb593ca81..7833617d6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,9 @@ members = [ "primitives/self-contained", "template/node", "template/runtime", + "precompiles", + "precompiles/macro", + "precompiles/tests-external", ] resolver = "2" @@ -43,6 +46,7 @@ repository = "https://github.com/paritytech/frontier/" async-trait = "0.1" bn = { package = "substrate-bn", version = "0.6", default-features = false } clap = { version = "4.3", features = ["derive", "deprecated"] } +derive_more = "0.99" environmental = { version = "1.1.4", default-features = false } ethereum = { version = "0.14.0", default-features = false } ethereum-types = { version = "0.14.1", default-features = false } @@ -51,10 +55,12 @@ futures = "0.3.28" hex = { version = "0.4.3", default-features = false, features = ["alloc"] } hex-literal = "0.4.1" impl-serde = { version = "0.4.0", default-features = false } +impl-trait-for-tuples = "0.2.1" jsonrpsee = "0.16.2" kvdb-rocksdb = "0.19.0" libsecp256k1 = { version = "0.7.1", default-features = false } log = { version = "0.4.20", default-features = false } +num_enum = { version = "0.6.1", default-features = false } parity-db = "0.4.10" parking_lot = "0.12.1" rlp = { version = "0.5.2", default-features = false } @@ -62,6 +68,7 @@ scale-codec = { package = "parity-scale-codec", version = "3.6.4", default-featu scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0" +similar-asserts = "1.1.0" sqlx = { version = "0.7.1", default-features = false, features = ["macros"] } thiserror = "1.0" tokio = "1.32.0" @@ -96,6 +103,8 @@ sp-consensus = { version = "0.10.0-dev", git = "https://github.com/paritytech/su sp-consensus-aura = { version = "0.10.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-consensus-grandpa = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { version = "21.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core-hashing = { version = "9.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core-hashing-proc-macro = { version = "9.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-database = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master" } sp-inherents = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { version = "23.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -165,6 +174,10 @@ pallet-evm-test-vector-support = { version = "1.0.0-dev", path = "frame/evm/test pallet-hotfix-sufficients = { version = "1.0.0", path = "frame/hotfix-sufficients", default-features = false } # Frontier Template frontier-template-runtime = { path = "template/runtime", default-features = false } + +# Frontier utils +precompile-utils = { path = "precompiles", default-features = false } + # Arkworks ark-bls12-377 = { version = "0.4.0", default-features = false, features = ["curve"] } ark-bw6-761 = { version = "0.4.0", default-features = false } diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 7af212593a..274990137f 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -1242,6 +1242,8 @@ mod tests { &config, &MockPrecompileSet, false, + None, + None, |_| { let res = Runner::::execute( H160::default(), @@ -1252,6 +1254,8 @@ mod tests { &config, &MockPrecompileSet, false, + None, + None, |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()), ); assert_matches!( @@ -1282,6 +1286,8 @@ mod tests { &config, &MockPrecompileSet, false, + None, + None, |_| (ExitReason::Succeed(ExitSucceed::Stopped), ()), ); assert!(res.is_ok()); diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml new file mode 100644 index 0000000000..bb7f4359ef --- /dev/null +++ b/precompiles/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "precompile-utils" +authors = { workspace = true } +description = "Utils to write EVM precompiles." +edition = "2021" +version = "0.1.0" + +[dependencies] +derive_more = { workspace = true, optional = true } +environmental = { workspace = true } +hex = { workspace = true } +hex-literal = { workspace = true, optional = true } +impl-trait-for-tuples = { workspace = true } +log = { workspace = true } +num_enum = { workspace = true } +scale-info = { workspace = true, optional = true, features = ["derive"] } +serde = { workspace = true, optional = true } +similar-asserts = { workspace = true, optional = true } + +# Moonbeam +precompile-utils-macro = { path = "macro" } + +# Substrate +frame-support = { workspace = true } +frame-system = { workspace = true } +scale-codec = { package = "parity-scale-codec", workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Frontier +evm = { workspace = true, features = ["with-codec"] } +fp-evm = { workspace = true } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } + +[dev-dependencies] +hex-literal = { workspace = true } + +[features] +default = ["std"] +std = [ + "environmental/std", + "fp-evm/std", + "frame-support/std", + "frame-system/std", + "pallet-evm/std", + "scale-codec/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", +] +testing = ["derive_more", "hex-literal", "scale-info", "serde", "similar-asserts", "std"] diff --git a/precompiles/macro/Cargo.toml b/precompiles/macro/Cargo.toml new file mode 100644 index 0000000000..45af88fa25 --- /dev/null +++ b/precompiles/macro/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "precompile-utils-macro" +authors = { workspace = true } +description = "" +edition = "2021" +version = "0.1.0" + +[lib] +proc-macro = true + +[[test]] +name = "tests" +path = "tests/tests.rs" + +[dependencies] +case = "1.0" +num_enum = { workspace = true } +prettyplease = "0.2.12" +proc-macro2 = "1.0" +quote = "1.0" +sp-core-hashing = { workspace = true } +syn = { version = "1.0", features = ["extra-traits", "fold", "full", "visit"] } + +[dev-dependencies] +macrotest = "1.0.9" +trybuild = "1.0" + +precompile-utils = { path = "../", features = ["testing"] } + +fp-evm = { workspace = true } +frame-support = { workspace = true } +sp-core-hashing = { workspace = true } +sp-std = { workspace = true } diff --git a/precompiles/macro/docs/precompile_macro.md b/precompiles/macro/docs/precompile_macro.md new file mode 100644 index 0000000000..e72a4e002a --- /dev/null +++ b/precompiles/macro/docs/precompile_macro.md @@ -0,0 +1,199 @@ +# `#[precompile]` procedural macro. + +This procedural macro allows to simplify the implementation of an EVM precompile or precompile set +using an `impl` block with annotations to automatically generate: + +- the implementation of the trait `Precompile` or `PrecompileSet` (exposed by the `fp_evm` crate) +- parsing of the method parameters from Solidity encoding into Rust type, based on the `solidity::Codec` + trait (exposed by the `precompile-utils` crate) +- a test to ensure the types expressed in the Solidity signature match the Rust types in the + implementation. + +## How to use + +Define your precompile type and write an `impl` block that will contain the precompile methods +implementation. This `impl` block can have type parameters and a `where` clause, which will be +reused to generate the `Precompile`/`PrecompileSet` trait implementation and the enum representing +each public function of precompile with its parsed arguments. + +```rust,ignore +pub struct ExemplePrecompile(PhantomData<(R,I)>); + +#[precomile_utils::precompile] +impl ExemplePrecompile +where + R: pallet_evm::Config +{ + #[precompile::public("example(uint32)")] + fn example(handle: &mut impl PrecompileHandle, arg: u32) -> EvmResult { + Ok(arg * 2) + } +} +``` + +The example code above will automatically generate an enum like + +```rust,ignore +#[allow(non_camel_case_types)] +pub enum ExemplePrecompileCall +where + R: pallet_evm::Config +{ + example { + arg: u32 + }, + // + an non constrible variant with a PhantomData<(R,I)> +} +``` + +This enum have the function `parse_call_data` that can parse the calldata, recognize the Solidity +4-bytes selector and parse the appropriate enum variant. + +It will also generate automatically an implementation of `Precompile`/`PrecompileSet` that calls +this function and the content of the variant to its associated function of the `impl` block. + +## Function attributes + +`#[precompile::public("signature")]` allows to declare a function as a public method of the +precompile with the provided Solidity signature. A function can have multiple `public` attributes to +support renamed functions with backward compatibility, however the arguments must have the same +type. It is not allowed to use the exact same signature multiple times. + +The function must take a `&mut impl PrecompileHandle` as parameter, followed by all the parameters +of the Solidity function in the same order. Those parameters types must implement `solidity::Codec`, and +their name should match the one used in the Solidity interface (.sol) while being in `snake_case`, +which will automatically be converted to `camelCase` in revert messages. The function must return an +`EvmResult`, which is an alias of `Result`. This `T` must implement the +`solidity::Codec` trait and must match the return type in the Solidity interface. The macro will +automatically encode it to Solidity format. + +By default those functions are considered non-payable and non-view (can cause state changes). This +can be changed using either `#[precompile::payable]` or `#[precompile::view]`. Only one can be used. + +It is also possible to declare a fallback function using `#[precompile::fallback]`. This function +will be called if the selector is unknown or if the input is less than 4-bytes long (no selector). +This function cannot have any parameter outside of the `PrecompileHandle`. A function can be both +`public` and `fallback`. + +In case some check must be performed before parsing the input, such as forbidding being called from +some address, a function can be annotated with `#[precompile::pre_check]`: + +```rust,ignore +#[precompile::pre_check] +fn pre_check(handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("Perform your check here") +} +``` + +This function cannot have other attributes. + +## PrecompileSet + +By default the macro considers the `impl` block to represent a precompile and this will implement +the `Precompile` trait. If you want to instead implement a precompile set, you must add the +`#[precompile::precompile_set]` to the `impl` block. + +Then, it is necessary to have a function annotated with the `#[precompile::discriminant]` attribute. +This function is called with the **code address**, the address of the precompile. It must return +`None` if this address is not part of the precompile set, or `Some` if it is. The `Some` variants +contains a value of a type of your choice that represents which member of the set this address +corresponds to. For example for our XC20 precompile sets this function returns the asset id +corresponding to this address if it exists. + +Finally, every other function annotated with a `precompile::_` attribute must now take this +discriminant as first parameter, before the `PrecompileHandle`. + +```rust,ignore +pub struct ExemplePrecompileSet(PhantomData); + +#[precompile_utils::precompile] +#[precompile::precompile_set] +impl ExamplePrecompileSet +where + R: pallet_evm::Config +{ + #[precompile::discriminant] + fn discriminant(address: H160) -> Option { + // Replace with your discriminant logic. + Some(match address { + a if a == H160::from(42) => 1 + a if a == H160::from(43) => 2, + _ => return None, + }) + } + + #[precompile::public("example(uint32)")] + fn example(discriminant: u8, handle: &mut impl PrecompileHandle, arg: u32) -> EvmResult { + // Discriminant can be used here. + Ok(arg * discriminant) + } +} +``` + +## Solidity signatures test + +The macro will automatically generate a unit test to ensure that the types expressed in a `public` +attribute matches the Rust parameters of the function, thanks to the `solidity::Codec` trait having the +`solidity_type() -> String` function. + +If any **parsed** argument (discriminant is not concerned) depends on the type parameters of the +`impl` block, the macro will not be able to produce valid code and output an error like: + +```text +error[E0412]: cannot find type `R` in this scope + --> tests/precompile/compile-fail/test/generic-arg.rs:25:63 + | +23 | impl> Precompile { + | - help: you might be missing a type parameter: `` +24 | #[precompile::public("foo(bytes)")] +25 | fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult { + | ^ not found in this scope +``` + +In this case you need to annotate the `impl` block with the `#[precompile::test_concrete_types(...)]` +attributes. The `...` should be replaced with concrete types for each type parameter, like a mock +runtime. Those types are only used to generate the test and only one set of types can be used. + +```rust,ignore +pub struct ExamplePrecompile(PhantomData<(R, I)>); + +pub struct GetMaxSize(PhantomData<(R, I)>); + +impl Get for GetMaxSize { + fn get() -> u32 { + >::SomeConstant::get() + } +} + +#[precompile_utils::precompile] +#[precompile::test_concrete_types(mock::Runtime, Instance1)] +impl ExamplePrecompile +where + R: pallet_evm::Config + SomeConfig +{ + #[precompile::public("example(bytes)")] + fn example( + handle: &mut impl PrecompileHandle, + data: BoundedBytes>, + ) -> EvmResult { + todo!("Method implementation") + } +} +``` + +## Enum functions + +The generated enums exposes the following public functions: + +- `parse_call_data`: take a `PrecompileHandle` and tries to parse the call data. Returns an + `EvmResult`. It **DOES NOT** execute the code of the annotated `impl` block. +- `supports_selector`: take a selector as a `u32` is returns if this selector is supported by the + precompile(set) as a `bool`. Note that the presence of a fallback function is not taken into + account. +- `selectors`: returns a static array (`&'static [u32]`) of all the supported selectors. +- For each variant/public function `foo`, there is a function `foo_selectors` which returns a static + array of all the supported selectors **for that function**. That can be used to ensure in tests + that some function have a selector that was computed by hand. +- `encode`: take `self` and encodes it in Solidity format. Additionally, `Vec` implements + `From` which simply call encodes. This is useful to write tests as you can construct the + variant you want and it will be encoded to Solidity format for you. diff --git a/precompiles/macro/src/derive_codec.rs b/precompiles/macro/src/derive_codec.rs new file mode 100644 index 0000000000..c9e0804b5d --- /dev/null +++ b/precompiles/macro/src/derive_codec.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{quote, quote_spanned}; +use syn::{ + parse_macro_input, punctuated::Punctuated, spanned::Spanned, DeriveInput, Ident, LitStr, Path, + PathSegment, PredicateType, TraitBound, TraitBoundModifier, +}; + +pub fn main(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + mut generics, + data, + .. + } = parse_macro_input!(input as DeriveInput); + + let syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(fields), + .. + }) = data + else { + return quote_spanned! { ident.span() => + compile_error!("Codec can only be derived for structs with named fields"); + } + .into(); + }; + let fields = fields.named; + + if fields.is_empty() { + return quote_spanned! { ident.span() => + compile_error!("Codec can only be derived for structs with at least one field"); + } + .into(); + } + + if let Some(unamed_field) = fields.iter().find(|f| f.ident.is_none()) { + return quote_spanned! { unamed_field.ty.span() => + compile_error!("Codec can only be derived for structs with named fields"); + } + .into(); + } + + let fields_ty: Vec<_> = fields.iter().map(|f| &f.ty).collect(); + let fields_ident: Vec<_> = fields + .iter() + .map(|f| f.ident.as_ref().expect("None case checked above")) + .collect(); + let fields_name_lit: Vec<_> = fields_ident + .iter() + .map(|i| LitStr::new(&i.to_string(), i.span())) + .collect(); + + let evm_data_trait_path = { + let mut segments = Punctuated::::new(); + segments.push(Ident::new("precompile_utils", Span::call_site()).into()); + segments.push(Ident::new("solidity", Span::call_site()).into()); + segments.push(Ident::new("Codec", Span::call_site()).into()); + Path { + leading_colon: Some(Default::default()), + segments, + } + }; + let where_clause = generics.make_where_clause(); + + for ty in &fields_ty { + let mut bounds = Punctuated::new(); + bounds.push( + TraitBound { + paren_token: None, + modifier: TraitBoundModifier::None, + lifetimes: None, + path: evm_data_trait_path.clone(), + } + .into(), + ); + + where_clause.predicates.push( + PredicateType { + lifetimes: None, + bounded_ty: (*ty).clone(), + colon_token: Default::default(), + bounds, + } + .into(), + ); + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + quote! { + impl #impl_generics ::precompile_utils::solidity::codec::Codec for #ident #ty_generics + #where_clause { + fn read( + reader: &mut ::precompile_utils::solidity::codec::Reader + ) -> ::precompile_utils::solidity::revert::MayRevert { + use ::precompile_utils::solidity::revert::BacktraceExt as _; + let (#(#fields_ident,)*): (#(#fields_ty,)*) = reader + .read() + .map_in_tuple_to_field(&[#(#fields_name_lit),*])?; + Ok(Self { + #(#fields_ident,)* + }) + } + + fn write(writer: &mut ::precompile_utils::solidity::codec::Writer, value: Self) { + ::precompile_utils::solidity::codec::Codec::write(writer, (#(value.#fields_ident,)*)); + } + + fn has_static_size() -> bool { + <(#(#fields_ty,)*)>::has_static_size() + } + + fn signature() -> String { + <(#(#fields_ty,)*)>::signature() + } + } + } + .into() +} diff --git a/precompiles/macro/src/generate_function_selector.rs b/precompiles/macro/src/generate_function_selector.rs new file mode 100644 index 0000000000..36a67363d4 --- /dev/null +++ b/precompiles/macro/src/generate_function_selector.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; + +pub fn main(_: TokenStream, input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as ItemEnum); + + let ItemEnum { + attrs, + vis, + enum_token, + ident, + variants, + .. + } = item; + + let mut ident_expressions: Vec = vec![]; + let mut variant_expressions: Vec = vec![]; + let mut variant_attrs: Vec> = vec![]; + for variant in variants { + match variant.discriminant { + Some((_, Expr::Lit(ExprLit { lit, .. }))) => { + if let Lit::Str(lit_str) = lit { + let digest = Keccak256::digest(lit_str.value().as_bytes()); + let selector = u32::from_be_bytes([digest[0], digest[1], digest[2], digest[3]]); + ident_expressions.push(variant.ident); + variant_expressions.push(Expr::Lit(ExprLit { + lit: Lit::Verbatim(Literal::u32_suffixed(selector)), + attrs: Default::default(), + })); + variant_attrs.push(variant.attrs); + } else { + return quote_spanned! { + lit.span() => compile_error!("Expected literal string"); + } + .into(); + } + } + Some((_eg, expr)) => { + return quote_spanned! { + expr.span() => compile_error!("Expected literal"); + } + .into() + } + None => { + return quote_spanned! { + variant.span() => compile_error!("Each variant must have a discriminant"); + } + .into() + } + } + } + + (quote! { + #(#attrs)* + #[derive(num_enum::TryFromPrimitive, num_enum::IntoPrimitive)] + #[repr(u32)] + #vis #enum_token #ident { + #( + #(#variant_attrs)* + #ident_expressions = #variant_expressions, + )* + } + }) + .into() +} diff --git a/precompiles/macro/src/lib.rs b/precompiles/macro/src/lib.rs new file mode 100644 index 0000000000..04252ba81d --- /dev/null +++ b/precompiles/macro/src/lib.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![crate_type = "proc-macro"] +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use sp_core_hashing::keccak_256; +use syn::{parse_macro_input, spanned::Spanned, Expr, Ident, ItemType, Lit, LitStr}; + +mod derive_codec; +mod precompile; +mod precompile_name_from_address; + +struct Bytes(Vec); + +impl ::std::fmt::Debug for Bytes { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result { + let data = &self.0; + write!(f, "[")?; + if !data.is_empty() { + write!(f, "{:#04x}u8", data[0])?; + for unit in data.iter().skip(1) { + write!(f, ", {:#04x}", unit)?; + } + } + write!(f, "]") + } +} + +#[proc_macro] +pub fn keccak256(input: TokenStream) -> TokenStream { + let lit_str = parse_macro_input!(input as LitStr); + + let hash = keccak_256(lit_str.value().as_bytes()); + + let bytes = Bytes(hash.to_vec()); + let eval_str = format!("{:?}", bytes); + let eval_ts: proc_macro2::TokenStream = eval_str.parse().unwrap_or_else(|_| { + panic!( + "Failed to parse the string \"{}\" to TokenStream.", + eval_str + ); + }); + quote!(#eval_ts).into() +} + +#[proc_macro_attribute] +pub fn precompile(attr: TokenStream, input: TokenStream) -> TokenStream { + precompile::main(attr, input) +} + +#[proc_macro_attribute] +pub fn precompile_name_from_address(attr: TokenStream, input: TokenStream) -> TokenStream { + precompile_name_from_address::main(attr, input) +} + +#[proc_macro_derive(Codec)] +pub fn derive_codec(input: TokenStream) -> TokenStream { + derive_codec::main(input) +} diff --git a/precompiles/macro/src/precompile/attr.rs b/precompiles/macro/src/precompile/attr.rs new file mode 100644 index 0000000000..3304a959bd --- /dev/null +++ b/precompiles/macro/src/precompile/attr.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use proc_macro2::Span; +use quote::ToTokens; +use syn::spanned::Spanned; + +pub fn take_attributes(attributes: &mut Vec) -> syn::Result> +where + A: syn::parse::Parse, +{ + let mut output = vec![]; + let pred = |attr: &syn::Attribute| { + attr.path + .segments + .first() + .map_or(false, |segment| segment.ident == "precompile") + }; + + while let Some(index) = attributes.iter().position(pred) { + let attr = attributes.remove(index); + let attr = syn::parse2(attr.into_token_stream())?; + output.push(attr) + } + Ok(output) +} + +/// List of additional token to be used for parsing. +pub mod keyword { + syn::custom_keyword!(precompile); + syn::custom_keyword!(public); + syn::custom_keyword!(fallback); + syn::custom_keyword!(payable); + syn::custom_keyword!(view); + syn::custom_keyword!(discriminant); + syn::custom_keyword!(precompile_set); + syn::custom_keyword!(test_concrete_types); + syn::custom_keyword!(pre_check); +} + +/// Attributes for methods. +pub enum MethodAttr { + Public(Span, syn::LitStr), + Fallback(Span), + Payable(Span), + View(Span), + Discriminant(Span), + PreCheck(Span), +} + +impl syn::parse::Parse for MethodAttr { + 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::public) { + let span = content.parse::()?.span(); + + let inner; + syn::parenthesized!(inner in content); + let signature = inner.parse::()?; + + Ok(MethodAttr::Public(span, signature)) + } else if lookahead.peek(keyword::fallback) { + Ok(MethodAttr::Fallback( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::payable) { + Ok(MethodAttr::Payable( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::view) { + Ok(MethodAttr::View(content.parse::()?.span())) + } else if lookahead.peek(keyword::discriminant) { + Ok(MethodAttr::Discriminant( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::pre_check) { + Ok(MethodAttr::PreCheck( + content.parse::()?.span(), + )) + } else { + Err(lookahead.error()) + } + } +} + +/// Attributes for the main impl Block. +pub enum ImplAttr { + PrecompileSet(Span), + TestConcreteTypes(Span, Vec), +} + +impl syn::parse::Parse for ImplAttr { + 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::precompile_set) { + Ok(ImplAttr::PrecompileSet( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::test_concrete_types) { + let span = content.parse::()?.span(); + + let inner; + syn::parenthesized!(inner in content); + let types = inner.parse_terminated::<_, syn::Token![,]>(syn::Type::parse)?; + + Ok(ImplAttr::TestConcreteTypes( + span, + types.into_iter().collect(), + )) + } else { + Err(lookahead.error()) + } + } +} diff --git a/precompiles/macro/src/precompile/expand.rs b/precompiles/macro/src/precompile/expand.rs new file mode 100644 index 0000000000..c6fb0f5cdb --- /dev/null +++ b/precompiles/macro/src/precompile/expand.rs @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; + +impl Precompile { + /// Main expand function, which expands everything else. + pub fn expand(&self) -> impl ToTokens { + let enum_ = self.expand_enum_decl(); + let enum_impl = self.expand_enum_impl(); + let precomp_impl = self.expand_precompile_impl(); + let test_signature = self.expand_test_solidity_signature(); + + quote! { + #enum_ + #enum_impl + #precomp_impl + #test_signature + } + } + + /// Expands the call enum declaration. + pub fn expand_enum_decl(&self) -> impl ToTokens { + let enum_ident = &self.enum_ident; + let (_impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + let type_parameters = self.generics.type_params().map(|p| &p.ident); + + let variants: Vec<_> = self.variants_content.keys().collect(); + let idents: Vec> = self + .variants_content + .values() + .map(|v| v.arguments.iter().map(|a| &a.ident).collect()) + .collect(); + let types: Vec> = self + .variants_content + .values() + .map(|v| v.arguments.iter().map(|a| &a.ty).collect()) + .collect(); + + quote!( + #[allow(non_camel_case_types)] + pub enum #enum_ident #ty_generics #where_clause { + #( + #variants { + #( + #idents: #types + ),* + }, + )* + + #[doc(hidden)] + __phantom( + ::core::marker::PhantomData<( #( #type_parameters ),* )>, + ::core::convert::Infallible + ), + } + ) + } + + /// Expands the parse function for each variants. + pub fn expand_variants_parse_fn(&self) -> impl ToTokens { + let span = Span::call_site(); + + let fn_parse = self + .variants_content + .keys() + .map(Self::variant_ident_to_parse_fn); + + let modifier_check = self.variants_content.values().map(|variant| { + let modifier = match variant.modifier { + Modifier::NonPayable => "NonPayable", + Modifier::Payable => "Payable", + Modifier::View => "View", + }; + + let modifier = syn::Ident::new(modifier, span); + + quote!( + use ::precompile_utils::solidity::modifier::FunctionModifier; + use ::precompile_utils::evm::handle::PrecompileHandleExt; + handle.check_function_modifier(FunctionModifier::#modifier)?; + ) + }); + + let variant_parsing = self + .variants_content + .iter() + .map(|(variant_ident, variant)| { + Self::expand_variant_parsing_from_handle(variant_ident, variant) + }); + + quote!( + #( + fn #fn_parse( + handle: &mut impl PrecompileHandle + ) -> ::precompile_utils::EvmResult { + use ::precompile_utils::solidity::revert::InjectBacktrace; + + #modifier_check + #variant_parsing + } + )* + ) + } + + /// Generates the parsing code for a variant, reading the input from the handle and + /// parsing it using Reader. + fn expand_variant_parsing_from_handle( + variant_ident: &syn::Ident, + variant: &Variant, + ) -> impl ToTokens { + if variant.arguments.is_empty() { + quote!( Ok(Self::#variant_ident {})).to_token_stream() + } else { + use case::CaseExt; + + let args_parse = variant.arguments.iter().map(|arg| { + let ident = &arg.ident; + let span = ident.span(); + let name = ident.to_string().to_camel_lowercase(); + + quote_spanned!(span=> #ident: input.read().in_field(#name)?,) + }); + let args_count = variant.arguments.len(); + + quote!( + let mut input = handle.read_after_selector()?; + input.expect_arguments(#args_count)?; + + Ok(Self::#variant_ident { + #(#args_parse)* + }) + ) + .to_token_stream() + } + } + + /// Expands the call enum impl block. + pub fn expand_enum_impl(&self) -> impl ToTokens { + let enum_ident = &self.enum_ident; + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + let match_selectors = self.selector_to_variant.keys(); + let match_selectors2 = self.selector_to_variant.keys(); + + let variants_parsing = self.expand_variants_parse_fn(); + + let variants_ident2: Vec<_> = self.variants_content.keys().collect(); + let variants_selectors_fn: Vec<_> = self + .variants_content + .keys() + .map(|name| format_ident!("{}_selectors", name)) + .collect(); + let variants_selectors: Vec<_> = self + .variants_content + .values() + .map(|variant| &variant.selectors) + .collect(); + + let variants_list: Vec> = self + .variants_content + .values() + .map(|variant| variant.arguments.iter().map(|arg| &arg.ident).collect()) + .collect(); + + let variants_encode: Vec<_> = self + .variants_content + .values() + .map(Self::expand_variant_encoding) + .collect(); + + let parse_call_data_fn = self.expand_enum_parse_call_data(); + let execute_fn = self.expand_enum_execute_fn(); + + quote!( + impl #impl_generics #enum_ident #ty_generics #where_clause { + #parse_call_data_fn + + #variants_parsing + + #execute_fn + + pub fn supports_selector(selector: u32) -> bool { + match selector { + #( + #match_selectors => true, + )* + _ => false, + } + } + + pub fn selectors() -> &'static [u32] { + &[#( + #match_selectors2 + ),*] + } + + #( + pub fn #variants_selectors_fn() -> &'static [u32] { + &[#( + #variants_selectors + ),*] + } + )* + + pub fn encode(self) -> ::sp_std::vec::Vec { + use ::precompile_utils::solidity::codec::Writer; + match self { + #( + Self::#variants_ident2 { #(#variants_list),* } => { + #variants_encode + }, + )* + Self::__phantom(_, _) => panic!("__phantom variant should not be used"), + } + } + } + + impl #impl_generics From<#enum_ident #ty_generics> for ::sp_std::vec::Vec + #where_clause + { + fn from(a: #enum_ident #ty_generics) -> ::sp_std::vec::Vec { + a.encode() + } + } + ) + } + + /// Expand the execute fn of the enum. + fn expand_enum_execute_fn(&self) -> impl ToTokens { + let impl_type = &self.impl_type; + + let variants_ident: Vec<_> = self.variants_content.keys().collect(); + + let variants_arguments: Vec> = self + .variants_content + .values() + .map(|variant| variant.arguments.iter().map(|arg| &arg.ident).collect()) + .collect(); + + // If there is no precompile set there is no discriminant. + let opt_discriminant_arg = self + .precompile_set_discriminant_type + .as_ref() + .map(|ty| quote!( discriminant: #ty,)); + + let variants_call = self + .variants_content + .iter() + .map(|(variant_ident, variant)| { + let arguments = variant.arguments.iter().map(|arg| &arg.ident); + + let output_span = variant.fn_output.span(); + let opt_discriminant_arg = self + .precompile_set_discriminant_fn + .as_ref() + .map(|_| quote!(discriminant,)); + + let write_output = quote_spanned!(output_span=> + ::precompile_utils::solidity::encode_return_value(output?) + ); + + quote!( + let output = <#impl_type>::#variant_ident( + #opt_discriminant_arg + handle, + #(#arguments),* + ); + #write_output + ) + }); + + quote!( + pub fn execute( + self, + #opt_discriminant_arg + handle: &mut impl PrecompileHandle + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + use ::precompile_utils::solidity::codec::Writer; + use ::fp_evm::{PrecompileOutput, ExitSucceed}; + + let output = match self { + #( + Self::#variants_ident { #(#variants_arguments),* } => { + #variants_call + }, + )* + Self::__phantom(_, _) => panic!("__phantom variant should not be used"), + }; + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output + }) + } + ) + } + + /// Expand how a variant can be Solidity encoded. + fn expand_variant_encoding(variant: &Variant) -> impl ToTokens { + match variant.selectors.first() { + Some(selector) => { + let write_arguments = variant.arguments.iter().map(|arg| { + let ident = &arg.ident; + let span = ident.span(); + quote_spanned!(span=> .write(#ident)) + }); + + quote!( + Writer::new_with_selector(#selector) + #(#write_arguments)* + .build() + ) + .to_token_stream() + } + None => quote!(Default::default()).to_token_stream(), + } + } + + /// Expand the main parsing function that, based on the selector in the + /// input, dispatch the decoding to one of the variants parsing function. + fn expand_enum_parse_call_data(&self) -> impl ToTokens { + let selectors = self.selector_to_variant.keys(); + let parse_fn = self + .selector_to_variant + .values() + .map(Self::variant_ident_to_parse_fn); + + let match_fallback = match &self.fallback_to_variant { + Some(variant) => { + let parse_fn = Self::variant_ident_to_parse_fn(variant); + quote!(_ => Self::#parse_fn(handle),).to_token_stream() + } + None => quote!( + Some(_) => Err(RevertReason::UnknownSelector.into()), + None => Err(RevertReason::read_out_of_bounds("selector").into()), + ) + .to_token_stream(), + }; + + quote!( + pub fn parse_call_data( + handle: &mut impl PrecompileHandle + ) -> ::precompile_utils::EvmResult { + use ::precompile_utils::solidity::revert::RevertReason; + + let input = handle.input(); + + let selector = input.get(0..4).map(|s| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(s); + u32::from_be_bytes(buffer) + }); + + match selector { + #( + Some(#selectors) => Self::#parse_fn(handle), + )* + #match_fallback + } + } + ) + } + + fn variant_ident_to_parse_fn(ident: &syn::Ident) -> syn::Ident { + format_ident!("_parse_{}", ident) + } + + /// Expands the impl of the Precomile(Set) trait. + pub fn expand_precompile_impl(&self) -> impl ToTokens { + let impl_type = &self.impl_type; + let enum_ident = &self.enum_ident; + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + if let Some(discriminant_fn) = &self.precompile_set_discriminant_fn { + let opt_pre_check = self.pre_check.as_ref().map(|ident| { + let span = ident.span(); + quote_spanned!(span=> + let _: () = <#impl_type>::#ident(discriminant, handle) + .map_err(|err| Some(err))?; + ) + }); + + quote!( + impl #impl_generics ::fp_evm::PrecompileSet for #impl_type #where_clause { + fn execute( + &self, + handle: &mut impl PrecompileHandle + ) -> Option<::precompile_utils::EvmResult<::fp_evm::PrecompileOutput>> { + use ::precompile_utils::precompile_set::DiscriminantResult; + + let discriminant = <#impl_type>::#discriminant_fn( + handle.code_address(), + handle.remaining_gas() + ); + + if let DiscriminantResult::Some(_, cost) | DiscriminantResult::None(cost) = discriminant { + let result = handle.record_cost(cost); + if let Err(e) = result { + return Some(Err(e.into())); + } + } + + let discriminant = match discriminant { + DiscriminantResult::Some(d, _) => d, + DiscriminantResult::None(cost) => return None, + DiscriminantResult::OutOfGas => return Some(Err(ExitError::OutOfGas.into())) + }; + + #opt_pre_check + + Some( + <#enum_ident #ty_generics>::parse_call_data(handle) + .and_then(|call| call.execute(discriminant, handle)) + ) + } + + fn is_precompile(&self, address: H160, gas: u64) -> ::fp_evm::IsPrecompileResult { + <#impl_type>::#discriminant_fn(address, gas).into() + } + } + ) + .to_token_stream() + } else { + let opt_pre_check = self.pre_check.as_ref().map(|ident| { + let span = ident.span(); + quote_spanned!(span=>let _: () = <#impl_type>::#ident(handle)?;) + }); + + quote!( + impl #impl_generics ::fp_evm::Precompile for #impl_type #where_clause { + fn execute( + handle: &mut impl PrecompileHandle + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + #opt_pre_check + + <#enum_ident #ty_generics>::parse_call_data(handle)?.execute(handle) + } + } + ) + .to_token_stream() + } + } + + /// Expands the Solidity signature test. + /// The macro expands an "inner" function in all build profiles, which is + /// then called by a test in test profile. This allows to display errors that occurs in + /// the expansion of the test without having to build in test profile, which is usually + /// related to the use of a type parameter in one of the parsed parameters of a method. + pub fn expand_test_solidity_signature(&self) -> impl ToTokens { + let variant_test: Vec<_> = self + .variants_content + .iter() + .map(|(ident, variant)| { + let span = ident.span(); + + let solidity = &variant.solidity_arguments_type; + let name = ident.to_string(); + let types: Vec<_> = variant.arguments.iter().map(|arg| &arg.ty).collect(); + + quote_spanned!(span=> + assert_eq!( + #solidity, + <(#(#types,)*) as Codec>::signature(), + "{} function signature doesn't match (left: attribute, right: computed \ + from Rust types)", + #name + ); + ) + }) + .collect(); + + let test_name = format_ident!("__{}_test_solidity_signatures", self.impl_ident); + let inner_name = format_ident!("__{}_test_solidity_signatures_inner", self.impl_ident); + + if let Some(test_types) = &self.test_concrete_types { + let (impl_generics, _ty_generics, where_clause) = self.generics.split_for_impl(); + + quote!( + #[allow(non_snake_case)] + pub(crate) fn #inner_name #impl_generics () #where_clause { + use ::precompile_utils::solidity::Codec; + #(#variant_test)* + } + + #[test] + #[allow(non_snake_case)] + fn #test_name() { + #inner_name::< #(#test_types),* >(); + } + ) + .to_token_stream() + } else { + quote!( + #[allow(non_snake_case)] + pub(crate) fn #inner_name() { + use ::precompile_utils::solidity::Codec; + #(#variant_test)* + } + + #[test] + #[allow(non_snake_case)] + fn #test_name() { + #inner_name(); + } + ) + .to_token_stream() + } + } +} diff --git a/precompiles/macro/src/precompile/mod.rs b/precompiles/macro/src/precompile/mod.rs new file mode 100644 index 0000000000..10ee09fe6a --- /dev/null +++ b/precompiles/macro/src/precompile/mod.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![doc = include_str!("../../docs/precompile_macro.md")] + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use sp_core_hashing::keccak_256; +use std::collections::BTreeMap; +use syn::{parse_macro_input, spanned::Spanned}; + +pub mod attr; +pub mod expand; +pub mod parse; + +pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { + // Macro must be used on `impl` block. + let mut impl_item = parse_macro_input!(item as syn::ItemImpl); + + // We inspect the block to collect all the data we need for the + // expansion, and make various checks. + let precompile = match Precompile::try_from(&mut impl_item) { + Ok(p) => p, + Err(e) => return e.into_compile_error().into(), + }; + + // We generate additional code based on the collected data. + let new_items = precompile.expand(); + let output = quote!( + #impl_item + #new_items + ); + + output.into() +} + +struct Precompile { + /// Impl struct type. + impl_type: syn::Type, + + /// Impl struct ident. + impl_ident: syn::Ident, + + /// New parsing enum ident. + enum_ident: syn::Ident, + + /// Generic part that needs to also be used by the input enum. + generics: syn::Generics, + + /// Which selector corresponds to which variant of the input enum. + selector_to_variant: BTreeMap, + + /// Optional fallback function if no selector matches. + fallback_to_variant: Option, + + /// Describes the content of each variant based on the precompile methods. + variants_content: BTreeMap, + + /// Since being a precompile set implies lots of changes, we must know it early + /// in the form of an attribute on the impl block itself. + tagged_as_precompile_set: bool, + + /// Ident of the function returning the PrecompileSet discriminant. + precompile_set_discriminant_fn: Option, + + /// Type of the PrecompileSet discriminant. + precompile_set_discriminant_type: Option, + + /// When generating the selector test the data types might depend on type parameters. + /// The test thus need to be written using concrete types. + test_concrete_types: Option>, + + /// Ident of a function that performs a check before the call is dispatched to the proper + /// function. + pre_check: Option, +} + +#[derive(Debug, PartialEq, Eq)] +enum Modifier { + NonPayable, + Payable, + View, +} + +#[derive(Debug)] +struct Variant { + /// Description of the arguments of this method, which will also + /// be members of a struct variant. + arguments: Vec, + + /// String extracted from the selector attribute. + /// A unit test will be generated to check that this selector matches + /// the Rust arguments. + /// + /// > solidity::Codec trait allows to generate this string at runtime only. Thus + /// > it is required to write it manually in the selector attribute, and + /// > a unit test is generated to check it matches. + solidity_arguments_type: String, + + /// Modifier of the function. They are all exclusive and defaults to + /// `NonPayable`. + modifier: Modifier, + + /// Selectors of this function to be able to encode back the data. + /// Empty if it only the fallback function. + selectors: Vec, + + /// Output of the variant fn (for better error messages). + fn_output: syn::Type, +} + +#[derive(Debug)] +struct Argument { + /// Identifier of the argument, which will be used in the struct variant. + ident: syn::Ident, + + /// Type of the argument, which will be used in the struct variant and + /// to parse the input. + ty: syn::Type, +} diff --git a/precompiles/macro/src/precompile/parse.rs b/precompiles/macro/src/precompile/parse.rs new file mode 100644 index 0000000000..569618777f --- /dev/null +++ b/precompiles/macro/src/precompile/parse.rs @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; + +impl Precompile { + /// Try to extract information out of an annotated `impl` block. + pub fn try_from(impl_: &mut syn::ItemImpl) -> syn::Result { + // Extract the name of the type used in the `impl` block. + let impl_ident = Self::extract_impl_ident(impl_)?; + let enum_ident = format_ident!("{}Call", impl_ident); + + // We setup the data collection struct. + let mut precompile = Precompile { + impl_type: impl_.self_ty.as_ref().clone(), + impl_ident, + enum_ident, + generics: impl_.generics.clone(), + selector_to_variant: BTreeMap::new(), + variants_content: BTreeMap::new(), + fallback_to_variant: None, + tagged_as_precompile_set: false, + precompile_set_discriminant_fn: None, + precompile_set_discriminant_type: None, + test_concrete_types: None, + pre_check: None, + }; + + precompile.process_impl_attr(impl_)?; + for mut item in &mut impl_.items { + // We only interact with methods and leave the rest as-is. + if let syn::ImplItem::Method(ref mut method) = &mut item { + precompile.process_method(method)?; + } + } + + // Check constraint of PrecompileSet. + if precompile.tagged_as_precompile_set + && precompile.precompile_set_discriminant_fn.is_none() + { + let msg = "A PrecompileSet must have exactly one function tagged with \ + `#[precompile::discriminant]`"; + return Err(syn::Error::new(Span::call_site(), msg)); + } + + Ok(precompile) + } + + /// Process the attributes used on the `impl` block, which allows to declare + /// if it is a PrecompileSet or not, and to provide concrete types for tests if necessary. + fn process_impl_attr(&mut self, impl_: &mut syn::ItemImpl) -> syn::Result<()> { + let attrs = attr::take_attributes::(&mut impl_.attrs)?; + + for attr in attrs { + match attr { + attr::ImplAttr::PrecompileSet(_) => { + self.tagged_as_precompile_set = true; + } + attr::ImplAttr::TestConcreteTypes(span, types) => { + if types.len() != self.generics.params.len() { + let msg = "The amount of types should match the amount of type parameters \ + of the impl block"; + return Err(syn::Error::new(span, msg)); + } + + if self.test_concrete_types.is_some() { + let msg = "Only one set of types can be provided to generate tests"; + return Err(syn::Error::new(span, msg)); + } + + self.test_concrete_types = Some(types); + } + } + } + + Ok(()) + } + + /// Extract the ident of the type of the `impl` block. + /// This ident is used to generate new idents such as the name of the Call enum and + /// the Solidity selector test. + fn extract_impl_ident(impl_: &syn::ItemImpl) -> syn::Result { + let type_path = match impl_.self_ty.as_ref() { + syn::Type::Path(p) => p, + _ => { + let msg = "The type in the impl block must be a path, like `Precompile` or + `example::Precompile`"; + return Err(syn::Error::new(impl_.self_ty.span(), msg)); + } + }; + + let final_path = type_path.path.segments.last().ok_or_else(|| { + let msg = "The type path must be non empty."; + syn::Error::new(impl_.self_ty.span(), msg) + })?; + + Ok(final_path.ident.clone()) + } + + /// Process a single method, looking for attributes and checking mandatory parameters. + fn process_method(&mut self, method: &mut syn::ImplItemMethod) -> syn::Result<()> { + // Take (remove) all attributes related to this macro. + let attrs = attr::take_attributes::(&mut method.attrs)?; + + // If there are no attributes it is a private function and we ignore it. + if attrs.is_empty() { + return Ok(()); + } + + // A method cannot have modifiers if it isn't a fallback and/or doesn't have a selector. + let mut used = false; + + let method_name = method.sig.ident.clone(); + let mut modifier = Modifier::NonPayable; + let mut solidity_arguments_type: Option = None; + let mut arguments = vec![]; + let mut is_fallback = false; + let mut selectors = vec![]; + let initial_arguments = if self.tagged_as_precompile_set { 2 } else { 1 }; + + // We first look for unique attributes. + if let Some(attr::MethodAttr::Discriminant(span)) = attrs.first() { + let span = *span; + + if attrs.len() != 1 { + let msg = "The discriminant attribute must be the only precompile attribute of \ + a function"; + return Err(syn::Error::new(span, msg)); + } + + return self.parse_discriminant_fn(span, method); + } + + if let Some(attr::MethodAttr::PreCheck(span)) = attrs.first() { + let span = *span; + + if attrs.len() != 1 { + let msg = "The pre_check attribute must be the only precompile attribute of \ + a function"; + return Err(syn::Error::new(span, msg)); + } + + return self.parse_pre_check_fn(span, method); + } + + // We iterate over all attributes of the method. + for attr in attrs { + match attr { + attr::MethodAttr::Discriminant(span) => { + let msg = "The discriminant attribute must be the only precompile \ + attribute of the function"; + return Err(syn::Error::new(span, msg)); + } + attr::MethodAttr::PreCheck(span) => { + let msg = "The pre_check attribute must be the only precompile \ + attribute of the function"; + return Err(syn::Error::new(span, msg)); + } + attr::MethodAttr::Fallback(span) => { + if self.fallback_to_variant.is_some() { + let msg = "A precompile can only have 1 fallback function"; + return Err(syn::Error::new(span, msg)); + } + + self.fallback_to_variant = Some(method_name.clone()); + used = true; + is_fallback = true; + } + attr::MethodAttr::Payable(span) => { + if modifier != Modifier::NonPayable { + let msg = + "A precompile method can have at most one modifier (payable, view)"; + return Err(syn::Error::new(span, msg)); + } + + modifier = Modifier::Payable; + } + attr::MethodAttr::View(span) => { + if modifier != Modifier::NonPayable { + let msg = + "A precompile method can have at most one modifier (payable, view)"; + return Err(syn::Error::new(span, msg)); + } + + modifier = Modifier::View; + } + attr::MethodAttr::Public(_, signature_lit) => { + used = true; + + let selector = self.parse_public_attr( + signature_lit, + &method_name, + &mut solidity_arguments_type, + )?; + selectors.push(selector); + } + } + } + + // A method cannot have attributes without being public or fallback. + if !used { + let msg = + "A precompile method cannot have modifiers without being a fallback or having\ + a `public` attribute"; + return Err(syn::Error::new(method.span(), msg)); + } + + // We forbid type parameters. + if let Some(param) = method.sig.generics.params.first() { + let msg = "Exposed precompile methods cannot have type parameters"; + return Err(syn::Error::new(param.span(), msg)); + } + + // Fallback method cannot have custom parameters. + if is_fallback { + if let Some(input) = method.sig.inputs.iter().nth(initial_arguments) { + let msg = if self.tagged_as_precompile_set { + "Fallback methods cannot take any parameter outside of the discriminant and \ + PrecompileHandle" + } else { + "Fallback methods cannot take any parameter outside of the PrecompileHandle" + }; + + return Err(syn::Error::new(input.span(), msg)); + } + } + + let mut method_inputs = method.sig.inputs.iter(); + + // We check the first parameters of the method. + // If this is a PrecompileSet it will look for a discriminant. + // Then for all precompile(set)s it will look for the PrecompileHandle. + // We take them from the iterator such that we are only left with the + // custom arguments. + self.check_initial_parameters(&mut method_inputs, method.sig.span())?; + + // We go through each parameter to collect each name and type that will be used to + // generate the input enum and parse the call data. + for input in method_inputs { + let input = match input { + syn::FnArg::Typed(t) => t, + _ => { + // I don't think it is possible to encounter this error since a self receiver + // seems to only be possible in the first position which is checked in + // `check_initial_parameters`. + let msg = "Exposed precompile methods cannot have a `self` parameter"; + return Err(syn::Error::new(input.span(), msg)); + } + }; + + let msg = "Parameter must be of the form `name: Type`, optionally prefixed by `mut`"; + let ident = match input.pat.as_ref() { + syn::Pat::Ident(pat) => { + if pat.by_ref.is_some() || pat.subpat.is_some() { + return Err(syn::Error::new(pat.span(), msg)); + } + + pat.ident.clone() + } + _ => { + return Err(syn::Error::new(input.pat.span(), msg)); + } + }; + let ty = input.ty.as_ref().clone(); + self.check_type_parameter_usage(&ty)?; + + arguments.push(Argument { ident, ty }) + } + + // Function output. + let output_type = match &method.sig.output { + syn::ReturnType::Type(_, t) => t, + _ => { + let msg = "A precompile method must have a return type of `EvmResult<_>` (exposed \ + by `precompile_utils`)"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + }; + + // We insert the collected data in self. + if self + .variants_content + .insert( + method_name.clone(), + Variant { + arguments, + solidity_arguments_type: solidity_arguments_type.unwrap_or(String::from("()")), + modifier, + selectors, + fn_output: output_type.as_ref().clone(), + }, + ) + .is_some() + { + let msg = "Duplicate method name"; + return Err(syn::Error::new(method_name.span(), msg)); + } + + Ok(()) + } + + /// Check the initial parameters of most methods of a Precompile(Set). + fn check_initial_parameters<'a>( + &mut self, + method_inputs: &mut impl Iterator, + method_span: Span, + ) -> syn::Result<()> { + // Discriminant input + if self.tagged_as_precompile_set { + let input = match method_inputs.next() { + Some(a) => a, + None => { + let msg = "PrecompileSet methods must have at least 2 parameters (the \ + precompile instance discriminant and the PrecompileHandle)"; + return Err(syn::Error::new(method_span, msg)); + } + }; + + let input = match input { + syn::FnArg::Typed(a) => a, + _ => { + let msg = "self is not allowed in precompile methods"; + return Err(syn::Error::new(input.span(), msg)); + } + }; + + let input_type = input.ty.as_ref(); + + self.try_register_discriminant_type(input_type)?; + } + + // Precompile handle input + { + let input = match method_inputs.next() { + Some(a) => a, + None => { + let msg = if self.tagged_as_precompile_set { + "PrecompileSet methods must have at least 2 parameters (the precompile \ + instance discriminant and the PrecompileHandle)" + } else { + "Precompile methods must have at least 1 parameter (the PrecompileHandle)" + }; + + return Err(syn::Error::new(method_span, msg)); + } + }; + + let input = match input { + syn::FnArg::Typed(a) => a, + _ => { + let msg = "self is not allowed in precompile methods"; + return Err(syn::Error::new(input.span(), msg)); + } + }; + + let input_type = input.ty.as_ref(); + + if !is_same_type(input_type, &syn::parse_quote! {&mut impl PrecompileHandle}) { + let msg = "This parameter must have type `&mut impl PrecompileHandle`"; + return Err(syn::Error::new(input_type.span(), msg)); + } + } + + Ok(()) + } + + /// Records the type of the discriminant and ensure they all have the same type. + fn try_register_discriminant_type(&mut self, ty: &syn::Type) -> syn::Result<()> { + if let Some(known_type) = &self.precompile_set_discriminant_type { + if !is_same_type(known_type, ty) { + let msg = format!( + "All discriminants must have the same type (found {} before)", + known_type.to_token_stream() + ); + return Err(syn::Error::new(ty.span(), msg)); + } + } else { + self.precompile_set_discriminant_type = Some(ty.clone()); + } + + Ok(()) + } + + /// Process the discriminant function. + fn parse_discriminant_fn( + &mut self, + span: Span, + method: &syn::ImplItemMethod, + ) -> syn::Result<()> { + if !self.tagged_as_precompile_set { + let msg = "The impl block must be tagged with `#[precompile::precompile_set]` for + the discriminant attribute to be used"; + return Err(syn::Error::new(span, msg)); + } + + if self.precompile_set_discriminant_fn.is_some() { + let msg = "A PrecompileSet can only have 1 discriminant function"; + return Err(syn::Error::new(span, msg)); + } + + let span = method.sig.span(); + + if method.sig.inputs.len() != 2 { + let msg = "The discriminant function must only take code address (H160) and \ + remaining gas (u64) as parameters."; + return Err(syn::Error::new(span, msg)); + } + + let msg = "The discriminant function must return an DiscriminantResult<_> (no type alias)"; + + let return_type = match &method.sig.output { + syn::ReturnType::Type(_, t) => t.as_ref(), + _ => return Err(syn::Error::new(span, msg)), + }; + + let return_path = match return_type { + syn::Type::Path(p) => p, + _ => return Err(syn::Error::new(span, msg)), + }; + + if return_path.qself.is_some() { + return Err(syn::Error::new(span, msg)); + } + + let return_path = &return_path.path; + + if return_path.leading_colon.is_some() || return_path.segments.len() != 1 { + return Err(syn::Error::new(span, msg)); + } + + let return_segment = &return_path.segments[0]; + + if return_segment.ident != "DiscriminantResult" { + return Err(syn::Error::new(return_segment.ident.span(), msg)); + } + + let result_arguments = match &return_segment.arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => return Err(syn::Error::new(return_segment.ident.span(), msg)), + }; + + if result_arguments.args.len() != 1 { + let msg = "DiscriminantResult type should only have 1 type argument"; + return Err(syn::Error::new(result_arguments.args.span(), msg)); + } + + let discriminant_type: &syn::Type = match &result_arguments.args[0] { + syn::GenericArgument::Type(t) => t, + _ => return Err(syn::Error::new(result_arguments.args.span(), msg)), + }; + + self.try_register_discriminant_type(discriminant_type)?; + + self.precompile_set_discriminant_fn = Some(method.sig.ident.clone()); + + Ok(()) + } + + /// Process the pre_check function. + fn parse_pre_check_fn(&mut self, span: Span, method: &syn::ImplItemMethod) -> syn::Result<()> { + if self.pre_check.is_some() { + let msg = "A Precompile can only have 1 pre_check function"; + return Err(syn::Error::new(span, msg)); + } + + let span = method.sig.span(); + + let mut method_inputs = method.sig.inputs.iter(); + + self.check_initial_parameters(&mut method_inputs, span)?; + + if method_inputs.next().is_some() { + let msg = if self.tagged_as_precompile_set { + "PrecompileSet pre_check method must have exactly 2 parameters (the precompile \ + instance discriminant and the PrecompileHandle)" + } else { + "Precompile pre_check method must have exactly 1 parameter (the \ + PrecompileHandle)" + }; + + return Err(syn::Error::new(span, msg)); + } + + self.pre_check = Some(method.sig.ident.clone()); + + Ok(()) + } + + /// Process a `public` attribute on a method. + fn parse_public_attr( + &mut self, + signature_lit: syn::LitStr, + method_name: &syn::Ident, + solidity_arguments_type: &mut Option, + ) -> syn::Result { + let signature = signature_lit.value(); + // Split signature to get arguments type. + let split: Vec<_> = signature.splitn(2, '(').collect(); + if split.len() != 2 { + let msg = "Selector must have form \"foo(arg1,arg2,...)\""; + return Err(syn::Error::new(signature_lit.span(), msg)); + } + + let local_args_type = format!("({}", split[1]); // add back initial parenthesis + + // If there are multiple public attributes we check that they all have + // the same type. + if let Some(ref args_type) = solidity_arguments_type { + if args_type != &local_args_type { + let msg = "Method cannot have selectors with different types."; + return Err(syn::Error::new(signature_lit.span(), msg)); + } + } else { + *solidity_arguments_type = Some(local_args_type); + } + + // Compute the 4-bytes selector. + let digest = keccak_256(signature.as_bytes()); + let selector = u32::from_be_bytes([digest[0], digest[1], digest[2], digest[3]]); + + if let Some(previous) = self + .selector_to_variant + .insert(selector, method_name.clone()) + { + let msg = format!("Selector collision with method {}", previous.to_string()); + return Err(syn::Error::new(signature_lit.span(), msg)); + } + + Ok(selector) + } + + /// Check that the provided type doesn't depend on one of the type parameters of the + /// precompile. Check is skipped if `test_concrete_types` attribute is used. + fn check_type_parameter_usage(&self, ty: &syn::Type) -> syn::Result<()> { + if self.test_concrete_types.is_some() { + return Ok(()); + } + + const ERR_MESSAGE: &str = + "impl type parameter is used in functions arguments. Arguments should not have a type +depending on a type parameter, unless it is a length bound for BoundedBytes, +BoundedString or alike, which doesn't affect the Solidity type. + +In that case, you must add a #[precompile::test_concrete_types(...)] attribute on the impl +block to provide concrete types that will be used to run the automatically generated tests +ensuring the Solidity function signatures are correct."; + + match ty { + syn::Type::Array(syn::TypeArray { elem, .. }) + | syn::Type::Group(syn::TypeGroup { elem, .. }) + | syn::Type::Paren(syn::TypeParen { elem, .. }) + | syn::Type::Reference(syn::TypeReference { elem, .. }) + | syn::Type::Ptr(syn::TypePtr { elem, .. }) + | syn::Type::Slice(syn::TypeSlice { elem, .. }) => self.check_type_parameter_usage(elem)?, + + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => { + let impl_params: Vec<_> = self + .generics + .params + .iter() + .filter_map(|param| match param { + syn::GenericParam::Type(syn::TypeParam { ident, .. }) => Some(ident), + _ => None, + }) + .collect(); + + for segment in segments { + if impl_params.contains(&&segment.ident) { + return Err(syn::Error::new(segment.ident.span(), ERR_MESSAGE)); + } + + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + let types = args.args.iter().filter_map(|arg| match arg { + syn::GenericArgument::Type(ty) + | syn::GenericArgument::Binding(syn::Binding { ty, .. }) => Some(ty), + _ => None, + }); + + for ty in types { + self.check_type_parameter_usage(ty)?; + } + } + } + } + syn::Type::Tuple(tuple) => { + for ty in tuple.elems.iter() { + self.check_type_parameter_usage(ty)?; + } + } + // BareFn => very unlikely this appear as parameter + // ImplTrait => will cause other errors, it must be a concrete type + // TypeInfer => it must be explicit concrete types since it ends up in enum fields + // Macro => Cannot check easily + // Never => Function will not be callable. + ty => println!("Skipping type parameter check for non supported kind of type: {ty:?}"), + } + + Ok(()) + } +} + +/// Helper to check 2 types are equal. +/// Having a function with explicit type annotation helps type inference at callsite, +/// which have trouble if `==` is used inline. +fn is_same_type(a: &syn::Type, b: &syn::Type) -> bool { + a == b +} diff --git a/precompiles/macro/src/precompile_name_from_address.rs b/precompiles/macro/src/precompile_name_from_address.rs new file mode 100644 index 0000000000..0cc48ece70 --- /dev/null +++ b/precompiles/macro/src/precompile_name_from_address.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; +use syn::{GenericArgument, Type}; + +pub fn main(_: TokenStream, input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as ItemType); + + let ItemType { + attrs, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + } = item; + + if let Type::Tuple(ref type_tuple) = *ty { + let variants: Vec<(Ident, u64)> = type_tuple + .elems + .iter() + .filter_map(extract_precompile_name_and_prefix) + .collect(); + + let ident_expressions: Vec<&Ident> = variants.iter().map(|(ident, _)| ident).collect(); + let variant_expressions: Vec<&u64> = variants.iter().map(|(_, id)| id).collect(); + + (quote! { + #(#attrs)* + #vis #type_token #ident #generics #eq_token #ty #semi_token + + #[derive(num_enum::TryFromPrimitive, num_enum::IntoPrimitive, Debug)] + #[repr(u64)] + pub enum PrecompileName { + #( + #ident_expressions = #variant_expressions, + )* + } + + impl PrecompileName { + pub fn from_address(address: sp_core::H160) -> Option { + let _u64 = address.to_low_u64_be(); + if address == sp_core::H160::from_low_u64_be(_u64) { + use num_enum::TryFromPrimitive; + Self::try_from_primitive(_u64).ok() + } else { + None + } + } + } + }) + .into() + } else { + quote_spanned! { + ty.span() => compile_error!("Expected tuple"); + } + .into() + } +} + +fn extract_precompile_name_and_prefix(type_: &Type) -> Option<(Ident, u64)> { + match type_ { + Type::Path(type_path) => { + if let Some(path_segment) = type_path.path.segments.last() { + match path_segment.ident.to_string().as_ref() { + "PrecompileAt" => { + extract_precompile_name_and_prefix_for_precompile_at(path_segment) + } + _ => None, + } + } else { + None + } + } + _ => None, + } +} + +fn extract_precompile_name_and_prefix_for_precompile_at( + path_segment: &syn::PathSegment, +) -> Option<(Ident, u64)> { + if let syn::PathArguments::AngleBracketed(generics) = &path_segment.arguments { + let mut iter = generics.args.iter(); + if let ( + Some(GenericArgument::Type(Type::Path(type_path_1))), + Some(GenericArgument::Type(Type::Path(type_path_2))), + ) = (iter.next(), iter.next()) + { + if let (Some(path_segment_1), Some(path_segment_2)) = ( + type_path_1.path.segments.last(), + type_path_2.path.segments.last(), + ) { + if let syn::PathArguments::AngleBracketed(generics_) = &path_segment_1.arguments { + if let Some(GenericArgument::Const(Expr::Lit(lit))) = generics_.args.first() { + if let Lit::Int(int) = &lit.lit { + if let Ok(precompile_id) = int.base10_parse() { + if &path_segment_2.ident.to_string() == "CollectivePrecompile" { + if let Some(instance_ident) = + precompile_instance_ident(path_segment_2) + { + return Some((instance_ident, precompile_id)); + } + } else { + return Some((path_segment_2.ident.clone(), precompile_id)); + } + } + } + } + } + } + } + } + + None +} + +fn precompile_instance_ident(path_segment: &syn::PathSegment) -> Option { + if let syn::PathArguments::AngleBracketed(generics_) = &path_segment.arguments { + if let Some(GenericArgument::Type(Type::Path(instance_type_path))) = generics_.args.last() { + if let Some(instance_type) = instance_type_path.path.segments.last() { + return Some(instance_type.ident.clone()); + } + } + } + + None +} diff --git a/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.rs b/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.rs new file mode 100644 index 0000000000..7b3abbb7d8 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use precompile_utils::prelude::*; + +#[derive(solidity::Codec)] +struct Empty1; + +#[derive(solidity::Codec)] +struct Empty2 {} + +#[derive(solidity::Codec)] +struct Empty3(); + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.stderr b/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.stderr new file mode 100644 index 0000000000..8c0a9d8bae --- /dev/null +++ b/precompiles/macro/tests/compile-fail/derive_codec/empty_struct.stderr @@ -0,0 +1,17 @@ +error: Codec can only be derived for structs with named fields + --> tests/compile-fail/derive_codec/empty_struct.rs:20:8 + | +20 | struct Empty1; + | ^^^^^^ + +error: Codec can only be derived for structs with at least one field + --> tests/compile-fail/derive_codec/empty_struct.rs:23:8 + | +23 | struct Empty2 {} + | ^^^^^^ + +error: Codec can only be derived for structs with named fields + --> tests/compile-fail/derive_codec/empty_struct.rs:26:8 + | +26 | struct Empty3 (); + | ^^^^^^ diff --git a/precompiles/macro/tests/compile-fail/derive_codec/enum.rs b/precompiles/macro/tests/compile-fail/derive_codec/enum.rs new file mode 100644 index 0000000000..8f586fde15 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/derive_codec/enum.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use precompile_utils::prelude::*; + +#[derive(solidity::Codec)] +enum Test { + One, + Two(u8), + Three { test: u16 }, +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/derive_codec/enum.stderr b/precompiles/macro/tests/compile-fail/derive_codec/enum.stderr new file mode 100644 index 0000000000..42a65d4a17 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/derive_codec/enum.stderr @@ -0,0 +1,5 @@ +error: Codec can only be derived for structs with named fields + --> tests/compile-fail/derive_codec/enum.rs:20:6 + | +20 | enum Test { + | ^^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs b/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs new file mode 100644 index 0000000000..fd31f34581 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use fp_evm::PrecompileHandle; +use precompile_utils::EvmResult; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr b/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr new file mode 100644 index 0000000000..1e87e3c771 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr @@ -0,0 +1,61 @@ +error[E0277]: the trait bound `String: Codec` is not satisfied + --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:43 + | +26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult { + | ^^^ the trait `Codec` is not implemented for `String` + | + = help: the following other types implement trait `Codec`: + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) + and $N others +note: required by a bound in `Reader::<'inner>::read` + --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs + | + | pub fn read(&mut self) -> MayRevert { + | ^^^^^ required by this bound in `Reader::<'inner>::read` + +error[E0277]: the trait bound `String: Codec` is not satisfied + --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:43 + | +26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult { + | ^^^ the trait `Codec` is not implemented for `String` + | + = help: the following other types implement trait `Codec`: + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) + and $N others +note: required by a bound in `precompile_utils::solidity::codec::Writer::write` + --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs + | + | pub fn write(mut self, value: T) -> Self { + | ^^^^^ required by this bound in `Writer::write` + +error[E0277]: the trait bound `String: Codec` is not satisfied + --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:5 + | +26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult { + | ^^^ the trait `Codec` is not implemented for `String` + | + = help: the following other types implement trait `Codec`: + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) + and $N others + = note: required for `(String,)` to implement `Codec` diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/no-output.rs b/precompiles/macro/tests/compile-fail/precompile/codec/no-output.rs new file mode 100644 index 0000000000..1375a521ec --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/no-output.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(test: &mut impl PrecompileHandle) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/no-output.stderr b/precompiles/macro/tests/compile-fail/precompile/codec/no-output.stderr new file mode 100644 index 0000000000..7a2758d0f5 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/no-output.stderr @@ -0,0 +1,5 @@ +error: A precompile method must have a return type of `EvmResult<_>` (exposed by `precompile_utils`) + --> tests/compile-fail/precompile/codec/no-output.rs:24:2 + | +24 | fn foo(test: &mut impl PrecompileHandle) { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs b/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs new file mode 100644 index 0000000000..e66f102c66 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use fp_evm::PrecompileHandle; +use precompile_utils::EvmResult; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr b/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr new file mode 100644 index 0000000000..52ba67e654 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `String: Codec` is not satisfied + --> tests/compile-fail/precompile/codec/output-dont-impl-codec.rs:26:46 + | +26 | fn foo(test: &mut impl PrecompileHandle) -> EvmResult { + | ^^^^^^^^^ the trait `Codec` is not implemented for `String` + | + = help: the following other types implement trait `Codec`: + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) + and $N others +note: required by a bound in `encode_arguments` + --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs + | + | pub fn encode_arguments(value: T) -> Vec { + | ^^^^^ required by this bound in `encode_arguments` diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.rs b/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.rs new file mode 100644 index 0000000000..1ebcaa6e45 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use fp_evm::PrecompileHandle; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(test: &mut impl PrecompileHandle) -> String { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.stderr b/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.stderr new file mode 100644 index 0000000000..c104ae8fb3 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-not-result.stderr @@ -0,0 +1,7 @@ +error[E0277]: the `?` operator can only be applied to values that implement `Try` + --> tests/compile-fail/precompile/codec/output-not-result.rs:25:46 + | +25 | fn foo(test: &mut impl PrecompileHandle) -> String { + | ^^^^^^ the `?` operator cannot be applied to type `String` + | + = help: the trait `Try` is not implemented for `String` diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs b/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs new file mode 100644 index 0000000000..aa52fabaa4 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use fp_evm::PrecompileHandle; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(test: &mut impl PrecompileHandle) -> Result<(), String> { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr b/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr new file mode 100644 index 0000000000..fa1fc8f71c --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr @@ -0,0 +1,13 @@ +error[E0277]: `?` couldn't convert the error to `PrecompileFailure` + --> tests/compile-fail/precompile/codec/output-wrong-error-result.rs:25:51 + | +25 | fn foo(test: &mut impl PrecompileHandle) -> Result<(), String> { + | ^ the trait `From` is not implemented for `PrecompileFailure` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + > + > + > + > + = note: required for `Result` to implement `FromResidual>` diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs new file mode 100644 index 0000000000..42487e2062 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::discriminant] + fn discriminant(address: H160) -> u32 { + 42 + } + + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr new file mode 100644 index 0000000000..66d90708f0 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr @@ -0,0 +1,5 @@ +error: The discriminant function must return an Option<_> (no type alias) + --> tests/compile-fail/precompile/discriminant/dont-return-option.rs:25:36 + | +25 | fn discriminant(address: H160) -> u32 { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs new file mode 100644 index 0000000000..64b04d6fe5 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::public("foo()")] + fn foo(_discriminant: u32, handle: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr new file mode 100644 index 0000000000..b24b8dddcb --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr @@ -0,0 +1,7 @@ +error: A PrecompileSet must have exactly one function tagged with `#[precompile::discriminant]` + --> tests/compile-fail/precompile/discriminant/missing-fn.rs:21:1 + | +21 | #[precompile_utils_macro::precompile] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `precompile_utils_macro::precompile` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.rs new file mode 100644 index 0000000000..5f706922a0 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::discriminant] + fn discriminant() -> Option { + Some(42) + } + + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr new file mode 100644 index 0000000000..3026b5ce42 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr @@ -0,0 +1,5 @@ +error: The discriminant function must only take the code address (H160) as parameter. + --> tests/compile-fail/precompile/discriminant/missing-param.rs:25:2 + | +25 | fn discriminant() -> Option { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs new file mode 100644 index 0000000000..8e3ef8f4c5 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::discriminant] + fn discriminant(address: H160) -> Option { + None + } + + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr new file mode 100644 index 0000000000..dc6db9f600 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr @@ -0,0 +1,5 @@ +error: The discriminant function must return an Option<_> (no type alias) + --> tests/compile-fail/precompile/discriminant/return-incomplete-option.rs:25:36 + | +25 | fn discriminant(address: H160) -> Option { + | ^^^^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs new file mode 100644 index 0000000000..2bbe1c6546 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::discriminant] + fn discriminant(address: H160, other: u32) -> Option { + Some(42) + } + + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr new file mode 100644 index 0000000000..a1b6e87865 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr @@ -0,0 +1,5 @@ +error: The discriminant function must only take the code address (H160) as parameter. + --> tests/compile-fail/precompile/discriminant/too-many-arguments.rs:25:2 + | +25 | fn discriminant(address: H160, other: u32) -> Option { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs new file mode 100644 index 0000000000..db641b65b7 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::discriminant] + fn discriminant(address: H160) -> Option { + Some(42) + } + + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr new file mode 100644 index 0000000000..8d999769e4 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr @@ -0,0 +1,5 @@ +error: All discriminants must have the same type (found u64 before) + --> tests/compile-fail/precompile/discriminant/type-mismatch-1.rs:30:24 + | +30 | fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs new file mode 100644 index 0000000000..6719d15ab5 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::public("foo()")] + fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult { + todo!() + } + + #[precompile::discriminant] + fn discriminant(address: H160) -> Option { + Some(42) + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr new file mode 100644 index 0000000000..d5ed6750af --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr @@ -0,0 +1,5 @@ +error: All discriminants must have the same type (found u32 before) + --> tests/compile-fail/precompile/discriminant/type-mismatch-2.rs:30:43 + | +30 | fn discriminant(address: H160) -> Option { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs new file mode 100644 index 0000000000..ef5737707d --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct PrecompileSet(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl PrecompileSet { + #[precompile::discriminant] + #[precompile::view] + fn foo(address: H160) -> Option { + None + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr new file mode 100644 index 0000000000..93f4fb2617 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr @@ -0,0 +1,5 @@ +error: The discriminant attribute must be the only precompile attribute of a function + --> tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs:24:16 + | +24 | #[precompile::discriminant] + | ^^^^^^^^^^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs new file mode 100644 index 0000000000..4aae4fbb2f --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + #[precompile::view] + #[precompile::payable] + fn foo(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr new file mode 100644 index 0000000000..678199ab8b --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr @@ -0,0 +1,5 @@ +error: A precompile method can have at most one modifier (payable, view) + --> tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs:25:16 + | +25 | #[precompile::payable] + | ^^^^^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs new file mode 100644 index 0000000000..fc5e7a1618 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::pre_check] + #[precompile::view] + fn foo(handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr new file mode 100644 index 0000000000..7f96f8b568 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr @@ -0,0 +1,5 @@ +error: The pre_check attribute must be the only precompile attribute of a function + --> tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs:23:16 + | +23 | #[precompile::pre_check] + | ^^^^^^^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/missing.rs b/precompiles/macro/tests/compile-fail/precompile/handle/missing.rs new file mode 100644 index 0000000000..467e6db2a8 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/missing.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo() { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/missing.stderr b/precompiles/macro/tests/compile-fail/precompile/handle/missing.stderr new file mode 100644 index 0000000000..2760a71988 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/missing.stderr @@ -0,0 +1,5 @@ +error: Precompile methods must have at least 1 parameter (the PrecompileHandle) + --> tests/compile-fail/precompile/handle/missing.rs:24:2 + | +24 | fn foo() { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.rs b/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.rs new file mode 100644 index 0000000000..44f64c1837 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::public("foo()")] + fn foo(_: u32) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.stderr b/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.stderr new file mode 100644 index 0000000000..8f0a10d8ff --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/set-missing.stderr @@ -0,0 +1,5 @@ +error: PrecompileSet methods must have at least 2 parameters (the precompile instance discriminant and the PrecompileHandle) + --> tests/compile-fail/precompile/handle/set-missing.rs:25:2 + | +25 | fn foo(_: u32) { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs b/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs new file mode 100644 index 0000000000..067678b9b2 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl Precompile { + #[precompile::public("foo()")] + fn foo(_discriminant: u32, _handle: u32) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr b/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr new file mode 100644 index 0000000000..bf62c1381c --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr @@ -0,0 +1,5 @@ +error: This parameter must have type `&mut impl PrecompileHandle` + --> tests/compile-fail/precompile/handle/set-wrong-type.rs:25:38 + | +25 | fn foo(_discriminant: u32, _handle: u32) { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.rs b/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.rs new file mode 100644 index 0000000000..c70e3416d9 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::public("foo()")] + fn foo(_handle: u32) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.stderr b/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.stderr new file mode 100644 index 0000000000..12e06e4889 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/handle/wrong-type.stderr @@ -0,0 +1,5 @@ +error: This parameter must have type `&mut impl PrecompileHandle` + --> tests/compile-fail/precompile/handle/wrong-type.rs:24:18 + | +24 | fn foo(_handle: u32) { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs b/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs new file mode 100644 index 0000000000..c570cb6929 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::pre_check] + fn pre_check() { + todo!() + } + + #[precompile::public("foo()")] + fn foo(_handle: &mut impl PrecompileHandle) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr b/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr new file mode 100644 index 0000000000..406806c51c --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr @@ -0,0 +1,5 @@ +error: Precompile methods must have at least 1 parameter (the PrecompileHandle) + --> tests/compile-fail/precompile/pre-check/no-parameter.rs:24:2 + | +24 | fn pre_check() { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs b/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs new file mode 100644 index 0000000000..77447ce52d --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::pre_check] + fn pre_check(_: &mut impl PrecompileHandle, _: u32) { + todo!() + } + + #[precompile::public("foo()")] + fn foo(_handle: &mut impl PrecompileHandle) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr b/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr new file mode 100644 index 0000000000..474b5c9dcd --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr @@ -0,0 +1,5 @@ +error: Precompile pre_check method must have exactly 1 parameter (the PrecompileHandle) + --> tests/compile-fail/precompile/pre-check/too-many-parameters.rs:24:2 + | +24 | fn pre_check(_: &mut impl PrecompileHandle, _: u32) { + | ^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs b/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs new file mode 100644 index 0000000000..40017d2655 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl Precompile { + #[precompile::pre_check] + fn pre_check(_: u32) { + todo!() + } + + #[precompile::public("foo()")] + fn foo(_handle: &mut impl PrecompileHandle) { + todo!() + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr b/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr new file mode 100644 index 0000000000..ea95fb0de8 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr @@ -0,0 +1,5 @@ +error: This parameter must have type `&mut impl PrecompileHandle` + --> tests/compile-fail/precompile/pre-check/wrong-parameter.rs:24:18 + | +24 | fn pre_check(_: u32) { + | ^^^ diff --git a/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs b/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs new file mode 100644 index 0000000000..775aa971a1 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; + +pub struct Precompile(PhantomData); + +#[precompile_utils_macro::precompile] +impl> Precompile { + #[precompile::public("foo(bytes)")] + fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult { + Ok(()) + } +} + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr b/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr new file mode 100644 index 0000000000..8b4daeb35f --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr @@ -0,0 +1,11 @@ +error: impl type parameter is used in functions arguments. Arguments should not have a type + depending on a type parameter, unless it is a length bound for BoundedBytes, + BoundedString or alike, which doesn't affect the Solidity type. + + In that case, you must add a #[precompile::test_concrete_types(...)] attribute on the impl + block to provide concrete types that will be used to run the automatically generated tests + ensuring the Solidity function signatures are correct. + --> tests/compile-fail/precompile/test-gen/generic-arg.rs:24:63 + | +24 | fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult { + | ^ diff --git a/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.rs b/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.rs new file mode 100644 index 0000000000..425c5cefb8 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +struct Dummy; + +#[precompile_utils_macro::precompile_name_from_address] +type Precompiles = Dummy; + +fn main() {} diff --git a/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.stderr b/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.stderr new file mode 100644 index 0000000000..343443a248 --- /dev/null +++ b/precompiles/macro/tests/compile-fail/precompile_name/not_tuple.stderr @@ -0,0 +1,5 @@ +error: Expected tuple + --> tests/compile-fail/precompile_name/not_tuple.rs:20:20 + | +20 | type Precompiles = Dummy; + | ^^^^^ diff --git a/precompiles/macro/tests/expand/precompile.expanded.rs b/precompiles/macro/tests/expand/precompile.expanded.rs new file mode 100644 index 0000000000..3153ec9d06 --- /dev/null +++ b/precompiles/macro/tests/expand/precompile.expanded.rs @@ -0,0 +1,364 @@ +use core::marker::PhantomData; +use frame_support::pallet_prelude::{ConstU32, Get}; +use precompile_utils::{prelude::*, EvmResult}; +use sp_core::{H160, U256}; +struct BatchPrecompile(PhantomData); +type GetCallDataLimit = ConstU32<42>; +type GetArrayLimit = ConstU32<42>; +impl BatchPrecompile +where + Runtime: Get, +{ + fn pre_check(handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("pre_check") + )) + } + fn batch_some( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("batch_some") + )) + } + fn batch_some_until_failure( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("batch_some_until_failure") + )) + } + fn batch_all( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("batch_all") + )) + } + fn fallback(handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("fallback") + )) + } +} +#[allow(non_camel_case_types)] +pub enum BatchPrecompileCall +where + Runtime: Get, +{ + batch_all { + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + }, + batch_some { + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + }, + batch_some_until_failure { + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + }, + fallback {}, + #[doc(hidden)] + __phantom( + ::core::marker::PhantomData<(Runtime)>, + ::core::convert::Infallible, + ), +} +impl BatchPrecompileCall +where + Runtime: Get, +{ + pub fn parse_call_data( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::solidity::revert::RevertReason; + let input = handle.input(); + let selector = input.get(0..4).map(|s| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(s); + u32::from_be_bytes(buffer) + }); + match selector { + Some(2044677020u32) => Self::_parse_batch_some(handle), + Some(2531431096u32) => Self::_parse_batch_all(handle), + Some(3473183175u32) => Self::_parse_batch_some_until_failure(handle), + _ => Self::_parse_fallback(handle), + } + } + fn _parse_batch_all(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(4usize)?; + Ok(Self::batch_all { + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + call_data: input.read().in_field("callData")?, + gas_limit: input.read().in_field("gasLimit")?, + }) + } + fn _parse_batch_some( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(4usize)?; + Ok(Self::batch_some { + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + call_data: input.read().in_field("callData")?, + gas_limit: input.read().in_field("gasLimit")?, + }) + } + fn _parse_batch_some_until_failure( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(4usize)?; + Ok(Self::batch_some_until_failure { + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + call_data: input.read().in_field("callData")?, + gas_limit: input.read().in_field("gasLimit")?, + }) + } + fn _parse_fallback(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::fallback {}) + } + pub fn execute( + self, + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + use fp_evm::{ExitSucceed, PrecompileOutput}; + use precompile_utils::solidity::codec::Writer; + let output = match self { + Self::batch_all { + to, + value, + call_data, + gas_limit, + } => { + let output = + >::batch_all(handle, to, value, call_data, gas_limit); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::batch_some { + to, + value, + call_data, + gas_limit, + } => { + let output = + >::batch_some(handle, to, value, call_data, gas_limit); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::batch_some_until_failure { + to, + value, + call_data, + gas_limit, + } => { + let output = >::batch_some_until_failure( + handle, to, value, call_data, gas_limit, + ); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::fallback {} => { + let output = >::fallback(handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + }; + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output, + }) + } + pub fn supports_selector(selector: u32) -> bool { + match selector { + 2044677020u32 => true, + 2531431096u32 => true, + 3473183175u32 => true, + _ => false, + } + } + pub fn selectors() -> &'static [u32] { + &[2044677020u32, 2531431096u32, 3473183175u32] + } + pub fn batch_all_selectors() -> &'static [u32] { + &[2531431096u32] + } + pub fn batch_some_selectors() -> &'static [u32] { + &[2044677020u32] + } + pub fn batch_some_until_failure_selectors() -> &'static [u32] { + &[3473183175u32] + } + pub fn fallback_selectors() -> &'static [u32] { + &[] + } + pub fn encode(self) -> ::sp_std::vec::Vec { + use precompile_utils::solidity::codec::Writer; + match self { + Self::batch_all { + to, + value, + call_data, + gas_limit, + } => Writer::new_with_selector(2531431096u32) + .write(to) + .write(value) + .write(call_data) + .write(gas_limit) + .build(), + Self::batch_some { + to, + value, + call_data, + gas_limit, + } => Writer::new_with_selector(2044677020u32) + .write(to) + .write(value) + .write(call_data) + .write(gas_limit) + .build(), + Self::batch_some_until_failure { + to, + value, + call_data, + gas_limit, + } => Writer::new_with_selector(3473183175u32) + .write(to) + .write(value) + .write(call_data) + .write(gas_limit) + .build(), + Self::fallback {} => Default::default(), + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + } + } +} +impl From> for ::sp_std::vec::Vec +where + Runtime: Get, +{ + fn from(a: BatchPrecompileCall) -> ::sp_std::vec::Vec { + a.encode() + } +} +impl ::fp_evm::Precompile for BatchPrecompile +where + Runtime: Get, +{ + fn execute( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + let _: () = >::pre_check(handle)?; + >::parse_call_data(handle)?.execute(handle) + } +} +#[allow(non_snake_case)] +pub(crate) fn __BatchPrecompile_test_solidity_signatures_inner() { + use precompile_utils::solidity::Codec; + match ( + &"(address[],uint256[],bytes[],uint64[])", + &<( + BoundedVec, + BoundedVec, + BoundedVec, GetArrayLimit>, + BoundedVec, + ) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "batch_all" ), ), ); + } + } + }; + match ( + &"(address[],uint256[],bytes[],uint64[])", + &<( + BoundedVec, + BoundedVec, + BoundedVec, GetArrayLimit>, + BoundedVec, + ) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "batch_some" ), ), ); + } + } + }; + match ( + &"(address[],uint256[],bytes[],uint64[])", + &<( + BoundedVec, + BoundedVec, + BoundedVec, GetArrayLimit>, + BoundedVec, + ) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "batch_some_until_failure" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "fallback" ), ), ); + } + } + }; +} diff --git a/precompiles/macro/tests/expand/precompile.rs b/precompiles/macro/tests/expand/precompile.rs new file mode 100644 index 0000000000..c51238bd51 --- /dev/null +++ b/precompiles/macro/tests/expand/precompile.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use frame_support::pallet_prelude::{ConstU32, Get}; +use precompile_utils::{prelude::*, EvmResult}; +use sp_core::{H160, U256}; + +// Based on Batch with stripped code. + +struct BatchPrecompile(PhantomData); + +type GetCallDataLimit = ConstU32<42>; +type GetArrayLimit = ConstU32<42>; + +#[precompile_utils_macro::precompile] +impl BatchPrecompile +where + Runtime: Get, +{ + #[precompile::pre_check] + fn pre_check(handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("pre_check") + } + + #[precompile::public("batchSome(address[],uint256[],bytes[],uint64[])")] + fn batch_some( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + todo!("batch_some") + } + + #[precompile::public("batchSomeUntilFailure(address[],uint256[],bytes[],uint64[])")] + fn batch_some_until_failure( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + todo!("batch_some_until_failure") + } + + #[precompile::public("batchAll(address[],uint256[],bytes[],uint64[])")] + fn batch_all( + handle: &mut impl PrecompileHandle, + to: BoundedVec, + value: BoundedVec, + call_data: BoundedVec, GetArrayLimit>, + gas_limit: BoundedVec, + ) -> EvmResult { + todo!("batch_all") + } + + // additional function to check fallback + #[precompile::fallback] + fn fallback(handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("fallback") + } +} diff --git a/precompiles/macro/tests/expand/precompile_name.expanded.rs b/precompiles/macro/tests/expand/precompile_name.expanded.rs new file mode 100644 index 0000000000..ebe51ecade --- /dev/null +++ b/precompiles/macro/tests/expand/precompile_name.expanded.rs @@ -0,0 +1,37 @@ +struct PrecompileAt(PhantomData<(T, U, V)>); +struct AddressU64; +struct FooPrecompile(PhantomData); +struct BarPrecompile(PhantomData<(R, S)>); +struct MockCheck; +type Precompiles = ( + PrecompileAt, FooPrecompile>, + PrecompileAt, BarPrecompile, (MockCheck, MockCheck)>, +); +#[repr(u64)] +pub enum PrecompileName { + FooPrecompile = 1u64, + BarPrecompile = 2u64, +} +#[automatically_derived] +impl ::core::fmt::Debug for PrecompileName { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::write_str( + f, + match self { + PrecompileName::FooPrecompile => "FooPrecompile", + PrecompileName::BarPrecompile => "BarPrecompile", + }, + ) + } +} +impl PrecompileName { + pub fn from_address(address: sp_core::H160) -> Option { + let _u64 = address.to_low_u64_be(); + if address == sp_core::H160::from_low_u64_be(_u64) { + use num_enum::TryFromPrimitive; + Self::try_from_primitive(_u64).ok() + } else { + None + } + } +} diff --git a/precompiles/macro/tests/expand/precompile_name.rs b/precompiles/macro/tests/expand/precompile_name.rs new file mode 100644 index 0000000000..261cdfba29 --- /dev/null +++ b/precompiles/macro/tests/expand/precompile_name.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Few mock structs to check the macro. +struct PrecompileAt(PhantomData<(T, U, V)>); +struct AddressU64; +struct FooPrecompile(PhantomData); +struct BarPrecompile(PhantomData<(R, S)>); +struct MockCheck; + +#[precompile_utils_macro::precompile_name_from_address] +type Precompiles = ( + PrecompileAt, FooPrecompile>, + PrecompileAt, BarPrecompile, (MockCheck, MockCheck)>, +); diff --git a/precompiles/macro/tests/expand/precompileset.expanded.rs b/precompiles/macro/tests/expand/precompileset.expanded.rs new file mode 100644 index 0000000000..d3750bb6bc --- /dev/null +++ b/precompiles/macro/tests/expand/precompileset.expanded.rs @@ -0,0 +1,1257 @@ +use core::marker::PhantomData; +use precompile_utils::{prelude::*, testing::PrecompileTesterExt, EvmResult}; +use sp_core::H160; +struct PrecompileSet(PhantomData); +type Discriminant = u32; +type GetAssetsStringLimit = R; +type MockRuntime = ConstU32<42>; +impl PrecompileSet +where + Runtime: Get, +{ + /// PrecompileSet discrimiant. Allows to knows if the address maps to an asset id, + /// and if this is the case which one. + fn discriminant(address: H160) -> Option { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("discriminant") + )) + } + fn total_supply(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("total_supply") + )) + } + fn balance_of( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + who: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("balance_of") + )) + } + fn allowance( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + spender: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("allowance") + )) + } + fn approve( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + spender: Address, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("approve") + )) + } + fn approve_inner( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: H160, + spender: H160, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("approve_inner") + )) + } + fn transfer( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + to: Address, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("transfer") + )) + } + fn transfer_from( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + from: Address, + to: Address, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("transfer_from") + )) + } + fn name( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("name") + )) + } + fn symbol( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("symbol") + )) + } + fn decimals(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("decimals") + )) + } + fn mint( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + to: Address, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("mint") + )) + } + fn burn( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + from: Address, + value: U256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("burn") + )) + } + fn freeze( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + account: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("freeze") + )) + } + fn thaw( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + account: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("thaw") + )) + } + fn freeze_asset(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("freeze_asset") + )) + } + fn thaw_asset(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("thaw_asset") + )) + } + fn transfer_ownership( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("transfer_ownership") + )) + } + fn set_team( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + issuer: Address, + admin: Address, + freezer: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("set_team") + )) + } + fn set_metadata( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + name: BoundedString>, + symbol: BoundedString>, + decimals: u8, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("set_metadata") + )) + } + fn clear_metadata( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("clear_metadata") + )) + } + fn eip2612_permit( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + spender: Address, + value: U256, + deadline: U256, + v: u8, + r: H256, + s: H256, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("eip2612_permit") + )) + } + fn eip2612_nonces( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("eip2612_nonces") + )) + } + fn eip2612_domain_separator( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("eip2612_domain_separator") + )) + } +} +#[allow(non_camel_case_types)] +pub enum PrecompileSetCall +where + Runtime: Get, +{ + allowance { + owner: Address, + spender: Address, + }, + approve { + spender: Address, + value: U256, + }, + balance_of { + who: Address, + }, + burn { + from: Address, + value: U256, + }, + clear_metadata {}, + decimals {}, + eip2612_domain_separator {}, + eip2612_nonces { + owner: Address, + }, + eip2612_permit { + owner: Address, + spender: Address, + value: U256, + deadline: U256, + v: u8, + r: H256, + s: H256, + }, + freeze { + account: Address, + }, + freeze_asset {}, + mint { + to: Address, + value: U256, + }, + name {}, + set_metadata { + name: BoundedString>, + symbol: BoundedString>, + decimals: u8, + }, + set_team { + issuer: Address, + admin: Address, + freezer: Address, + }, + symbol {}, + thaw { + account: Address, + }, + thaw_asset {}, + total_supply {}, + transfer { + to: Address, + value: U256, + }, + transfer_from { + from: Address, + to: Address, + value: U256, + }, + transfer_ownership { + owner: Address, + }, + #[doc(hidden)] + __phantom( + ::core::marker::PhantomData<(Runtime)>, + ::core::convert::Infallible, + ), +} +impl PrecompileSetCall +where + Runtime: Get, +{ + pub fn parse_call_data( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::solidity::revert::RevertReason; + let input = handle.input(); + let selector = input.get(0..4).map(|s| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(s); + u32::from_be_bytes(buffer) + }); + match selector { + Some(117300739u32) => Self::_parse_name(handle), + Some(157198259u32) => Self::_parse_approve(handle), + Some(404098525u32) => Self::_parse_total_supply(handle), + Some(484305945u32) => Self::_parse_thaw_asset(handle), + Some(599290589u32) => Self::_parse_transfer_from(handle), + Some(826074471u32) => Self::_parse_decimals(handle), + Some(910484757u32) => Self::_parse_eip2612_domain_separator(handle), + Some(936559348u32) => Self::_parse_set_metadata(handle), + Some(1086394137u32) => Self::_parse_mint(handle), + Some(1374431959u32) => Self::_parse_thaw_asset(handle), + Some(1587675670u32) => Self::_parse_thaw(handle), + Some(1804030401u32) => Self::_parse_freeze_asset(handle), + Some(1889567281u32) => Self::_parse_balance_of(handle), + Some(2127478272u32) => Self::_parse_eip2612_nonces(handle), + Some(2367676207u32) => Self::_parse_freeze(handle), + Some(2514000705u32) => Self::_parse_symbol(handle), + Some(2646777772u32) => Self::_parse_burn(handle), + Some(2835717307u32) => Self::_parse_transfer(handle), + Some(3352902745u32) => Self::_parse_set_team(handle), + Some(3552201630u32) => Self::_parse_clear_metadata(handle), + Some(3566436177u32) => Self::_parse_freeze_asset(handle), + Some(3573918927u32) => Self::_parse_eip2612_permit(handle), + Some(3714247998u32) => Self::_parse_allowance(handle), + Some(3999121892u32) => Self::_parse_set_metadata(handle), + Some(4021736498u32) => Self::_parse_clear_metadata(handle), + Some(4030008324u32) => Self::_parse_transfer_ownership(handle), + Some(4076725131u32) => Self::_parse_transfer_ownership(handle), + Some(4173303445u32) => Self::_parse_set_team(handle), + Some(_) => Err(RevertReason::UnknownSelector.into()), + None => Err(RevertReason::read_out_of_bounds("selector").into()), + } + } + fn _parse_allowance(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(2usize)?; + Ok(Self::allowance { + owner: input.read().in_field("owner")?, + spender: input.read().in_field("spender")?, + }) + } + fn _parse_approve(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(2usize)?; + Ok(Self::approve { + spender: input.read().in_field("spender")?, + value: input.read().in_field("value")?, + }) + } + fn _parse_balance_of( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(1usize)?; + Ok(Self::balance_of { + who: input.read().in_field("who")?, + }) + } + fn _parse_burn(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(2usize)?; + Ok(Self::burn { + from: input.read().in_field("from")?, + value: input.read().in_field("value")?, + }) + } + fn _parse_clear_metadata( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::clear_metadata {}) + } + fn _parse_decimals(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::decimals {}) + } + fn _parse_eip2612_domain_separator( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::View)?; + Ok(Self::eip2612_domain_separator {}) + } + fn _parse_eip2612_nonces( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::View)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(1usize)?; + Ok(Self::eip2612_nonces { + owner: input.read().in_field("owner")?, + }) + } + fn _parse_eip2612_permit( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(7usize)?; + Ok(Self::eip2612_permit { + owner: input.read().in_field("owner")?, + spender: input.read().in_field("spender")?, + value: input.read().in_field("value")?, + deadline: input.read().in_field("deadline")?, + v: input.read().in_field("v")?, + r: input.read().in_field("r")?, + s: input.read().in_field("s")?, + }) + } + fn _parse_freeze(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(1usize)?; + Ok(Self::freeze { + account: input.read().in_field("account")?, + }) + } + fn _parse_freeze_asset( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::freeze_asset {}) + } + fn _parse_mint(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(2usize)?; + Ok(Self::mint { + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + }) + } + fn _parse_name(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::name {}) + } + fn _parse_set_metadata( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(3usize)?; + Ok(Self::set_metadata { + name: input.read().in_field("name")?, + symbol: input.read().in_field("symbol")?, + decimals: input.read().in_field("decimals")?, + }) + } + fn _parse_set_team(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(3usize)?; + Ok(Self::set_team { + issuer: input.read().in_field("issuer")?, + admin: input.read().in_field("admin")?, + freezer: input.read().in_field("freezer")?, + }) + } + fn _parse_symbol(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::symbol {}) + } + fn _parse_thaw(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(1usize)?; + Ok(Self::thaw { + account: input.read().in_field("account")?, + }) + } + fn _parse_thaw_asset( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::thaw_asset {}) + } + fn _parse_total_supply( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::total_supply {}) + } + fn _parse_transfer(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(2usize)?; + Ok(Self::transfer { + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + }) + } + fn _parse_transfer_from( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(3usize)?; + Ok(Self::transfer_from { + from: input.read().in_field("from")?, + to: input.read().in_field("to")?, + value: input.read().in_field("value")?, + }) + } + fn _parse_transfer_ownership( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + let mut input = handle.read_after_selector()?; + input.expect_arguments(1usize)?; + Ok(Self::transfer_ownership { + owner: input.read().in_field("owner")?, + }) + } + pub fn execute( + self, + discriminant: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + use fp_evm::{ExitSucceed, PrecompileOutput}; + use precompile_utils::solidity::codec::Writer; + let output = match self { + Self::allowance { owner, spender } => { + let output = + >::allowance(discriminant, handle, owner, spender); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::approve { spender, value } => { + let output = + >::approve(discriminant, handle, spender, value); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::balance_of { who } => { + let output = >::balance_of(discriminant, handle, who); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::burn { from, value } => { + let output = >::burn(discriminant, handle, from, value); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::clear_metadata {} => { + let output = >::clear_metadata(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::decimals {} => { + let output = >::decimals(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::eip2612_domain_separator {} => { + let output = + >::eip2612_domain_separator(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::eip2612_nonces { owner } => { + let output = >::eip2612_nonces(discriminant, handle, owner); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::eip2612_permit { + owner, + spender, + value, + deadline, + v, + r, + s, + } => { + let output = >::eip2612_permit( + discriminant, + handle, + owner, + spender, + value, + deadline, + v, + r, + s, + ); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::freeze { account } => { + let output = >::freeze(discriminant, handle, account); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::freeze_asset {} => { + let output = >::freeze_asset(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::mint { to, value } => { + let output = >::mint(discriminant, handle, to, value); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::name {} => { + let output = >::name(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::set_metadata { + name, + symbol, + decimals, + } => { + let output = >::set_metadata( + discriminant, + handle, + name, + symbol, + decimals, + ); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::set_team { + issuer, + admin, + freezer, + } => { + let output = >::set_team( + discriminant, + handle, + issuer, + admin, + freezer, + ); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::symbol {} => { + let output = >::symbol(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::thaw { account } => { + let output = >::thaw(discriminant, handle, account); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::thaw_asset {} => { + let output = >::thaw_asset(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::total_supply {} => { + let output = >::total_supply(discriminant, handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::transfer { to, value } => { + let output = >::transfer(discriminant, handle, to, value); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::transfer_from { from, to, value } => { + let output = + >::transfer_from(discriminant, handle, from, to, value); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::transfer_ownership { owner } => { + let output = + >::transfer_ownership(discriminant, handle, owner); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + }; + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output, + }) + } + pub fn supports_selector(selector: u32) -> bool { + match selector { + 117300739u32 => true, + 157198259u32 => true, + 404098525u32 => true, + 484305945u32 => true, + 599290589u32 => true, + 826074471u32 => true, + 910484757u32 => true, + 936559348u32 => true, + 1086394137u32 => true, + 1374431959u32 => true, + 1587675670u32 => true, + 1804030401u32 => true, + 1889567281u32 => true, + 2127478272u32 => true, + 2367676207u32 => true, + 2514000705u32 => true, + 2646777772u32 => true, + 2835717307u32 => true, + 3352902745u32 => true, + 3552201630u32 => true, + 3566436177u32 => true, + 3573918927u32 => true, + 3714247998u32 => true, + 3999121892u32 => true, + 4021736498u32 => true, + 4030008324u32 => true, + 4076725131u32 => true, + 4173303445u32 => true, + _ => false, + } + } + pub fn selectors() -> &'static [u32] { + &[ + 117300739u32, + 157198259u32, + 404098525u32, + 484305945u32, + 599290589u32, + 826074471u32, + 910484757u32, + 936559348u32, + 1086394137u32, + 1374431959u32, + 1587675670u32, + 1804030401u32, + 1889567281u32, + 2127478272u32, + 2367676207u32, + 2514000705u32, + 2646777772u32, + 2835717307u32, + 3352902745u32, + 3552201630u32, + 3566436177u32, + 3573918927u32, + 3714247998u32, + 3999121892u32, + 4021736498u32, + 4030008324u32, + 4076725131u32, + 4173303445u32, + ] + } + pub fn allowance_selectors() -> &'static [u32] { + &[3714247998u32] + } + pub fn approve_selectors() -> &'static [u32] { + &[157198259u32] + } + pub fn balance_of_selectors() -> &'static [u32] { + &[1889567281u32] + } + pub fn burn_selectors() -> &'static [u32] { + &[2646777772u32] + } + pub fn clear_metadata_selectors() -> &'static [u32] { + &[4021736498u32, 3552201630u32] + } + pub fn decimals_selectors() -> &'static [u32] { + &[826074471u32] + } + pub fn eip2612_domain_separator_selectors() -> &'static [u32] { + &[910484757u32] + } + pub fn eip2612_nonces_selectors() -> &'static [u32] { + &[2127478272u32] + } + pub fn eip2612_permit_selectors() -> &'static [u32] { + &[3573918927u32] + } + pub fn freeze_selectors() -> &'static [u32] { + &[2367676207u32] + } + pub fn freeze_asset_selectors() -> &'static [u32] { + &[3566436177u32, 1804030401u32] + } + pub fn mint_selectors() -> &'static [u32] { + &[1086394137u32] + } + pub fn name_selectors() -> &'static [u32] { + &[117300739u32] + } + pub fn set_metadata_selectors() -> &'static [u32] { + &[936559348u32, 3999121892u32] + } + pub fn set_team_selectors() -> &'static [u32] { + &[3352902745u32, 4173303445u32] + } + pub fn symbol_selectors() -> &'static [u32] { + &[2514000705u32] + } + pub fn thaw_selectors() -> &'static [u32] { + &[1587675670u32] + } + pub fn thaw_asset_selectors() -> &'static [u32] { + &[1374431959u32, 484305945u32] + } + pub fn total_supply_selectors() -> &'static [u32] { + &[404098525u32] + } + pub fn transfer_selectors() -> &'static [u32] { + &[2835717307u32] + } + pub fn transfer_from_selectors() -> &'static [u32] { + &[599290589u32] + } + pub fn transfer_ownership_selectors() -> &'static [u32] { + &[4076725131u32, 4030008324u32] + } + pub fn encode(self) -> ::sp_std::vec::Vec { + use precompile_utils::solidity::codec::Writer; + match self { + Self::allowance { owner, spender } => Writer::new_with_selector(3714247998u32) + .write(owner) + .write(spender) + .build(), + Self::approve { spender, value } => Writer::new_with_selector(157198259u32) + .write(spender) + .write(value) + .build(), + Self::balance_of { who } => Writer::new_with_selector(1889567281u32).write(who).build(), + Self::burn { from, value } => Writer::new_with_selector(2646777772u32) + .write(from) + .write(value) + .build(), + Self::clear_metadata {} => Writer::new_with_selector(4021736498u32).build(), + Self::decimals {} => Writer::new_with_selector(826074471u32).build(), + Self::eip2612_domain_separator {} => Writer::new_with_selector(910484757u32).build(), + Self::eip2612_nonces { owner } => Writer::new_with_selector(2127478272u32) + .write(owner) + .build(), + Self::eip2612_permit { + owner, + spender, + value, + deadline, + v, + r, + s, + } => Writer::new_with_selector(3573918927u32) + .write(owner) + .write(spender) + .write(value) + .write(deadline) + .write(v) + .write(r) + .write(s) + .build(), + Self::freeze { account } => Writer::new_with_selector(2367676207u32) + .write(account) + .build(), + Self::freeze_asset {} => Writer::new_with_selector(3566436177u32).build(), + Self::mint { to, value } => Writer::new_with_selector(1086394137u32) + .write(to) + .write(value) + .build(), + Self::name {} => Writer::new_with_selector(117300739u32).build(), + Self::set_metadata { + name, + symbol, + decimals, + } => Writer::new_with_selector(936559348u32) + .write(name) + .write(symbol) + .write(decimals) + .build(), + Self::set_team { + issuer, + admin, + freezer, + } => Writer::new_with_selector(3352902745u32) + .write(issuer) + .write(admin) + .write(freezer) + .build(), + Self::symbol {} => Writer::new_with_selector(2514000705u32).build(), + Self::thaw { account } => Writer::new_with_selector(1587675670u32) + .write(account) + .build(), + Self::thaw_asset {} => Writer::new_with_selector(1374431959u32).build(), + Self::total_supply {} => Writer::new_with_selector(404098525u32).build(), + Self::transfer { to, value } => Writer::new_with_selector(2835717307u32) + .write(to) + .write(value) + .build(), + Self::transfer_from { from, to, value } => Writer::new_with_selector(599290589u32) + .write(from) + .write(to) + .write(value) + .build(), + Self::transfer_ownership { owner } => Writer::new_with_selector(4076725131u32) + .write(owner) + .build(), + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + } + } +} +impl From> for ::sp_std::vec::Vec +where + Runtime: Get, +{ + fn from(a: PrecompileSetCall) -> ::sp_std::vec::Vec { + a.encode() + } +} +impl ::fp_evm::PrecompileSet for PrecompileSet +where + Runtime: Get, +{ + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option<::precompile_utils::EvmResult<::fp_evm::PrecompileOutput>> { + let discriminant = match >::discriminant(handle.code_address()) { + Some(d) => d, + None => return None, + }; + Some( + >::parse_call_data(handle) + .and_then(|call| call.execute(discriminant, handle)), + ) + } + fn is_precompile(&self, address: H160, gas: u64) -> ::fp_evm::IsPrecompileResult { + >::discriminant(address, gas).is_some() + } +} +#[allow(non_snake_case)] +pub(crate) fn __PrecompileSet_test_solidity_signatures_inner() +where + Runtime: Get, +{ + use precompile_utils::solidity::Codec; + match ( + &"(address,address)", + &<(Address, Address) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "allowance" ), ), ); + } + } + }; + match ( + &"(address,uint256)", + &<(Address, U256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "approve" ), ), ); + } + } + }; + match (&"(address)", &<(Address,) as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "balance_of" ), ), ); + } + } + }; + match ( + &"(address,uint256)", + &<(Address, U256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "burn" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "clear_metadata" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "decimals" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "eip2612_domain_separator" ), ), ); + } + } + }; + match (&"(address)", &<(Address,) as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "eip2612_nonces" ), ), ); + } + } + }; + match ( + &"(address,address,uint256,uint256,uint8,bytes32,bytes32)", + &<(Address, Address, U256, U256, u8, H256, H256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "eip2612_permit" ), ), ); + } + } + }; + match (&"(address)", &<(Address,) as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "freeze" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "freeze_asset" ), ), ); + } + } + }; + match ( + &"(address,uint256)", + &<(Address, U256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "mint" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "name" ), ), ); + } + } + }; + match ( + &"(string,string,uint8)", + &<( + BoundedString>, + BoundedString>, + u8, + ) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "set_metadata" ), ), ); + } + } + }; + match ( + &"(address,address,address)", + &<(Address, Address, Address) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "set_team" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "symbol" ), ), ); + } + } + }; + match (&"(address)", &<(Address,) as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "thaw" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "thaw_asset" ), ), ); + } + } + }; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "total_supply" ), ), ); + } + } + }; + match ( + &"(address,uint256)", + &<(Address, U256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "transfer" ), ), ); + } + } + }; + match ( + &"(address,address,uint256)", + &<(Address, Address, U256) as Codec>::signature(), + ) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "transfer_from" ), ), ); + } + } + }; + match (&"(address)", &<(Address,) as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "transfer_ownership" ), ), ); + } + } + }; +} diff --git a/precompiles/macro/tests/expand/precompileset.rs b/precompiles/macro/tests/expand/precompileset.rs new file mode 100644 index 0000000000..e00880ec49 --- /dev/null +++ b/precompiles/macro/tests/expand/precompileset.rs @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use core::marker::PhantomData; +use precompile_utils::{prelude::*, testing::PrecompileTesterExt, EvmResult}; +use sp_core::H160; + +// Based on Erc20AssetsPrecompileSet with stripped code. + +struct PrecompileSet(PhantomData); + +type Discriminant = u32; +type GetAssetsStringLimit = R; +type MockRuntime = ConstU32<42>; + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +#[precompile::test_concrete_types(MockRuntime)] +impl PrecompileSet +where + Runtime: Get, +{ + /// PrecompileSet discrimiant. Allows to knows if the address maps to an asset id, + /// and if this is the case which one. + #[precompile::discriminant] + fn discriminant(address: H160) -> Option { + todo!("discriminant") + } + + #[precompile::public("totalSupply()")] + fn total_supply(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("total_supply") + } + + #[precompile::public("balanceOf(address)")] + fn balance_of( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + who: Address, + ) -> EvmResult { + todo!("balance_of") + } + + #[precompile::public("allowance(address,address)")] + fn allowance( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + spender: Address, + ) -> EvmResult { + todo!("allowance") + } + + #[precompile::public("approve(address,uint256)")] + fn approve( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + spender: Address, + value: U256, + ) -> EvmResult { + todo!("approve") + } + + fn approve_inner( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: H160, + spender: H160, + value: U256, + ) -> EvmResult { + todo!("approve_inner") + } + + #[precompile::public("transfer(address,uint256)")] + fn transfer( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + to: Address, + value: U256, + ) -> EvmResult { + todo!("transfer") + } + + #[precompile::public("transferFrom(address,address,uint256)")] + fn transfer_from( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + from: Address, + to: Address, + value: U256, + ) -> EvmResult { + todo!("transfer_from") + } + + #[precompile::public("name()")] + fn name( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + todo!("name") + } + + #[precompile::public("symbol()")] + fn symbol( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + todo!("symbol") + } + + #[precompile::public("decimals()")] + fn decimals(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("decimals") + } + + // From here: only for locals, we need to check whether we are in local assets otherwise fail + #[precompile::public("mint(address,uint256)")] + fn mint( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + to: Address, + value: U256, + ) -> EvmResult { + todo!("mint") + } + + #[precompile::public("burn(address,uint256)")] + fn burn( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + from: Address, + value: U256, + ) -> EvmResult { + todo!("burn") + } + + #[precompile::public("freeze(address)")] + fn freeze( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + account: Address, + ) -> EvmResult { + todo!("freeze") + } + + #[precompile::public("thaw(address)")] + fn thaw( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + account: Address, + ) -> EvmResult { + todo!("thaw") + } + + #[precompile::public("freezeAsset()")] + #[precompile::public("freeze_asset()")] + fn freeze_asset(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("freeze_asset") + } + + #[precompile::public("thawAsset()")] + #[precompile::public("thaw_asset()")] + fn thaw_asset(asset_id: Discriminant, handle: &mut impl PrecompileHandle) -> EvmResult { + todo!("thaw_asset") + } + + #[precompile::public("transferOwnership(address)")] + #[precompile::public("transfer_ownership(address)")] + fn transfer_ownership( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + ) -> EvmResult { + todo!("transfer_ownership") + } + + #[precompile::public("setTeam(address,address,address)")] + #[precompile::public("set_team(address,address,address)")] + fn set_team( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + issuer: Address, + admin: Address, + freezer: Address, + ) -> EvmResult { + todo!("set_team") + } + + #[precompile::public("setMetadata(string,string,uint8)")] + #[precompile::public("set_metadata(string,string,uint8)")] + fn set_metadata( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + name: BoundedString>, + symbol: BoundedString>, + decimals: u8, + ) -> EvmResult { + todo!("set_metadata") + } + + #[precompile::public("clearMetadata()")] + #[precompile::public("clear_metadata()")] + fn clear_metadata( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + todo!("clear_metadata") + } + + #[precompile::public("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")] + fn eip2612_permit( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + spender: Address, + value: U256, + deadline: U256, + v: u8, + r: H256, + s: H256, + ) -> EvmResult { + todo!("eip2612_permit") + } + + #[precompile::public("nonces(address)")] + #[precompile::view] + fn eip2612_nonces( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + owner: Address, + ) -> EvmResult { + todo!("eip2612_nonces") + } + + #[precompile::public("DOMAIN_SEPARATOR()")] + #[precompile::view] + fn eip2612_domain_separator( + asset_id: Discriminant, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { + todo!("eip2612_domain_separator") + } +} diff --git a/precompiles/macro/tests/expand/returns_tuple.expanded.rs b/precompiles/macro/tests/expand/returns_tuple.expanded.rs new file mode 100644 index 0000000000..6d11310310 --- /dev/null +++ b/precompiles/macro/tests/expand/returns_tuple.expanded.rs @@ -0,0 +1,108 @@ +use precompile_utils::{prelude::*, EvmResult}; +use sp_core::{H160, U256}; +struct ExamplePrecompile; +impl ExamplePrecompile { + fn example(handle: &mut impl PrecompileHandle) -> EvmResult<(Address, U256, UnboundedBytes)> { + ::core::panicking::panic_fmt(format_args!( + "not yet implemented: {0}", + format_args!("example") + )) + } +} +#[allow(non_camel_case_types)] +pub enum ExamplePrecompileCall { + example {}, + #[doc(hidden)] + __phantom(::core::marker::PhantomData<()>, ::core::convert::Infallible), +} +impl ExamplePrecompileCall { + pub fn parse_call_data( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult { + use precompile_utils::solidity::revert::RevertReason; + let input = handle.input(); + let selector = input.get(0..4).map(|s| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(s); + u32::from_be_bytes(buffer) + }); + match selector { + Some(1412775727u32) => Self::_parse_example(handle), + Some(_) => Err(RevertReason::UnknownSelector.into()), + None => Err(RevertReason::read_out_of_bounds("selector").into()), + } + } + fn _parse_example(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult { + use precompile_utils::{ + evm::handle::PrecompileHandleExt, + solidity::{modifier::FunctionModifier, revert::InjectBacktrace}, + }; + handle.check_function_modifier(FunctionModifier::NonPayable)?; + Ok(Self::example {}) + } + pub fn execute( + self, + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + use fp_evm::{ExitSucceed, PrecompileOutput}; + use precompile_utils::solidity::codec::Writer; + let output = match self { + Self::example {} => { + let output = ::example(handle); + ::precompile_utils::solidity::encode_return_value(output?) + } + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + }; + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output, + }) + } + pub fn supports_selector(selector: u32) -> bool { + match selector { + 1412775727u32 => true, + _ => false, + } + } + pub fn selectors() -> &'static [u32] { + &[1412775727u32] + } + pub fn example_selectors() -> &'static [u32] { + &[1412775727u32] + } + pub fn encode(self) -> ::sp_std::vec::Vec { + use precompile_utils::solidity::codec::Writer; + match self { + Self::example {} => Writer::new_with_selector(1412775727u32).build(), + Self::__phantom(_, _) => { + ::core::panicking::panic_fmt(format_args!("__phantom variant should not be used")) + } + } + } +} +impl From for ::sp_std::vec::Vec { + fn from(a: ExamplePrecompileCall) -> ::sp_std::vec::Vec { + a.encode() + } +} +impl ::fp_evm::Precompile for ExamplePrecompile { + fn execute( + handle: &mut impl PrecompileHandle, + ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> { + ::parse_call_data(handle)?.execute(handle) + } +} +#[allow(non_snake_case)] +pub(crate) fn __ExamplePrecompile_test_solidity_signatures_inner() { + use precompile_utils::solidity::Codec; + match (&"()", &<() as Codec>::signature()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( kind, &*left_val, &*right_val, ::core::option::Option::Some( format_args!( "{0} function signature doesn\'t match (left: attribute, right: computed from Rust types)", "example" ), ), ); + } + } + }; +} diff --git a/precompiles/macro/tests/expand/returns_tuple.rs b/precompiles/macro/tests/expand/returns_tuple.rs new file mode 100644 index 0000000000..dec1269329 --- /dev/null +++ b/precompiles/macro/tests/expand/returns_tuple.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use precompile_utils::{prelude::*, EvmResult}; +use sp_core::{H160, U256}; + +struct ExamplePrecompile; + +#[precompile_utils_macro::precompile] +impl ExamplePrecompile { + #[precompile::public("example()")] + fn example(handle: &mut impl PrecompileHandle) -> EvmResult<(Address, U256, UnboundedBytes)> { + todo!("example") + } +} diff --git a/precompiles/macro/tests/pass/derive_codec.rs b/precompiles/macro/tests/pass/derive_codec.rs new file mode 100644 index 0000000000..6ac9626de6 --- /dev/null +++ b/precompiles/macro/tests/pass/derive_codec.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use precompile_utils::solidity::codec::{Address, Codec, Reader, Writer}; +use sp_core::H160; + +#[derive(Debug, Clone, PartialEq, Eq, Codec)] +struct StaticSize { + id: u32, + address: Address, +} + +#[derive(Debug, Clone, PartialEq, Eq, Codec)] +struct DynamicSize { + id: u32, + array: Vec, +} + +fn main() { + // static + let static_size = StaticSize { + id: 5, + address: H160::repeat_byte(0x42).into(), + }; + + assert!(StaticSize::has_static_size()); + assert_eq!(&StaticSize::signature(), "(uint32,address)"); + + let bytes = Writer::new().write(static_size.clone()).build(); + assert_eq!( + bytes, + Writer::new() + .write(5u32) + .write(Address::from(H160::repeat_byte(0x42))) + .build() + ); + + let mut reader = Reader::new(&bytes); + let static_size_2: StaticSize = reader.read().expect("to decode properly"); + assert_eq!(static_size_2, static_size); + + // dynamic + let dynamic_size = DynamicSize { + id: 6, + array: vec![10u32, 15u32], + }; + assert!(!DynamicSize::::has_static_size()); + assert_eq!(DynamicSize::::signature(), "(uint32,uint32[])"); + + let bytes = Writer::new().write(dynamic_size.clone()).build(); + assert_eq!( + bytes, + Writer::new() + .write(0x20u32) // offset of struct + .write(6u32) // id + .write(0x40u32) // array offset + .write(2u32) // array size + .write(10u32) // array[0] + .write(15u32) // array[1] + .build() + ); + + let mut reader = Reader::new(&bytes); + let dynamic_size_2: DynamicSize = reader.read().expect("to decode properly"); + assert_eq!(dynamic_size_2, dynamic_size); +} diff --git a/precompiles/macro/tests/pass/precompile_fn_modifiers.rs b/precompiles/macro/tests/pass/precompile_fn_modifiers.rs new file mode 100644 index 0000000000..d684bb821e --- /dev/null +++ b/precompiles/macro/tests/pass/precompile_fn_modifiers.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonbeam is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use precompile_utils::{prelude::*, testing::PrecompileTesterExt, EvmResult}; +use sp_core::H160; + +pub struct PrecompileSet; + +#[precompile_utils_macro::precompile] +#[precompile::precompile_set] +impl PrecompileSet { + #[precompile::discriminant] + fn discriminant(_: H160) -> Option<()> { + Some(()) + } + + #[precompile::public("default()")] + fn default(_: (), _: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } + + #[precompile::public("view()")] + #[precompile::view] + fn view(_: (), _: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } + + #[precompile::public("payable()")] + #[precompile::payable] + fn payable(_: (), _: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } +} + +fn main() { + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::default {}) + .with_value(1) + .execute_reverts(|output| output == b"Function is not payable"); + + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::default {}) + .with_static_call(true) + .execute_reverts(|output| output == b"Can't call non-static function in static context"); + + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::view {}) + .with_value(1) + .execute_reverts(|output| output == b"Function is not payable"); + + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::view {}) + .with_static_call(true) + .execute_returns(()); + + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::payable {}) + .with_value(1) + .execute_returns(()); + + PrecompileSet + .prepare_test([0u8; 20], [0u8; 20], PrecompileSetCall::payable {}) + .with_static_call(true) + .execute_reverts(|output| output == b"Can't call non-static function in static context"); +} diff --git a/precompiles/macro/tests/tests.rs b/precompiles/macro/tests/tests.rs new file mode 100644 index 0000000000..ac700055bd --- /dev/null +++ b/precompiles/macro/tests/tests.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use sp_core_hashing::keccak_256; + +#[test] +fn test_keccak256() { + assert_eq!( + &precompile_utils_macro::keccak256!(""), + keccak_256(b"").as_slice(), + ); + assert_eq!( + &precompile_utils_macro::keccak256!("toto()"), + keccak_256(b"toto()").as_slice(), + ); + assert_ne!( + &precompile_utils_macro::keccak256!("toto()"), + keccak_256(b"tata()").as_slice(), + ); +} + +#[test] +#[ignore] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile-fail/**/*.rs"); + t.pass("tests/pass/**/*.rs"); +} + +// Cargo expand is not supported on stable rust +#[test] +#[ignore] +fn expand() { + // Use `expand` to update the expansions + // Replace it with `expand_without_refresh` afterward so that + // CI checks the expension don't change + + // macrotest::expand("tests/expand/**/*.rs"); + macrotest::expand_without_refresh("tests/expand/**/*.rs"); +} diff --git a/precompiles/src/evm/costs.rs b/precompiles/src/evm/costs.rs new file mode 100644 index 0000000000..2d91958460 --- /dev/null +++ b/precompiles/src/evm/costs.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Cost calculations. +//! TODO: PR EVM to make those cost calculations public. + +use crate::EvmResult; +use fp_evm::{ExitError, PrecompileFailure}; +use sp_core::U256; + +pub fn log_costs(topics: usize, data_len: usize) -> EvmResult { + // Cost calculation is copied from EVM code that is not publicly exposed by the crates. + // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 + + const G_LOG: u64 = 375; + const G_LOGDATA: u64 = 8; + const G_LOGTOPIC: u64 = 375; + + let topic_cost = G_LOGTOPIC + .checked_mul(topics as u64) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; + + let data_cost = G_LOGDATA + .checked_mul(data_len as u64) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; + + G_LOG + .checked_add(topic_cost) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })? + .checked_add(data_cost) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }) +} + +// Compute the cost of doing a subcall. +// Some parameters cannot be known in advance, so we estimate the worst possible cost. +pub fn call_cost(value: U256, config: &evm::Config) -> u64 { + // Copied from EVM code since not public. + pub const G_CALLVALUE: u64 = 9000; + pub const G_NEWACCOUNT: u64 = 25000; + + fn address_access_cost(is_cold: bool, regular_value: u64, config: &evm::Config) -> u64 { + if config.increase_state_access_gas { + if is_cold { + config.gas_account_access_cold + } else { + config.gas_storage_read_warm + } + } else { + regular_value + } + } + + fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { + if is_call_or_callcode && transfers_value { + G_CALLVALUE + } else { + 0 + } + } + + fn new_cost( + is_call_or_staticcall: bool, + new_account: bool, + transfers_value: bool, + config: &evm::Config, + ) -> u64 { + let eip161 = !config.empty_considered_exists; + if is_call_or_staticcall { + if eip161 { + if transfers_value && new_account { + G_NEWACCOUNT + } else { + 0 + } + } else if new_account { + G_NEWACCOUNT + } else { + 0 + } + } else { + 0 + } + } + + let transfers_value = value != U256::default(); + let is_cold = true; + let is_call_or_callcode = true; + let is_call_or_staticcall = true; + let new_account = true; + + address_access_cost(is_cold, config.gas_call, config) + + xfer_cost(is_call_or_callcode, transfers_value) + + new_cost(is_call_or_staticcall, new_account, transfers_value, config) +} diff --git a/precompiles/src/evm/handle.rs b/precompiles/src/evm/handle.rs new file mode 100644 index 0000000000..5fb51cd58f --- /dev/null +++ b/precompiles/src/evm/handle.rs @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + solidity::{ + codec::Reader, + modifier::FunctionModifier, + revert::{MayRevert, RevertReason}, + }, + EvmResult, +}; +use fp_evm::{Log, PrecompileHandle}; + +pub trait PrecompileHandleExt: PrecompileHandle { + /// Record cost of one DB read manually. + /// The max encoded lenght of the data that will be read should be provided. + fn record_db_read( + &mut self, + data_max_encoded_len: usize, + ) -> Result<(), evm::ExitError>; + + /// Record cost of a log manually. + /// This can be useful to record log costs early when their content have static size. + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult; + + /// Record cost of logs. + fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult; + + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert; + + /// Read the selector from the input data. + fn read_u32_selector(&self) -> MayRevert; + + /// Returns a reader of the input, skipping the selector. + fn read_after_selector(&self) -> MayRevert; +} + +impl PrecompileHandleExt for T { + fn record_db_read( + &mut self, + data_max_encoded_len: usize, + ) -> Result<(), evm::ExitError> { + self.record_cost(crate::prelude::RuntimeHelper::::db_read_gas_cost())?; + // TODO: record ref time when precompile will be benchmarked + self.record_external_cost(None, Some(data_max_encoded_len as u64)) + } + + /// Record cost of a log manualy. + /// This can be useful to record log costs early when their content have static size. + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { + self.record_cost(crate::evm::costs::log_costs(topics, data_len)?)?; + + Ok(()) + } + + /// Record cost of logs. + fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult { + for log in logs { + self.record_log_costs_manual(log.topics.len(), log.data.len())?; + } + + Ok(()) + } + + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert { + crate::solidity::modifier::check_function_modifier( + self.context(), + self.is_static(), + modifier, + ) + } + + /// Read the selector from the input data as u32. + fn read_u32_selector(&self) -> MayRevert { + crate::solidity::codec::selector(self.input()) + .ok_or(RevertReason::read_out_of_bounds("selector").into()) + } + + /// Returns a reader of the input, skipping the selector. + fn read_after_selector(&self) -> MayRevert { + Reader::new_skip_selector(self.input()) + } +} + +environmental::environmental!(EVM_CONTEXT: trait PrecompileHandle); + +pub fn using_precompile_handle<'a, R, F: FnOnce() -> R>( + precompile_handle: &'a mut dyn PrecompileHandle, + mutator: F, +) -> R { + // # Safety + // + // unsafe rust does not mean unsafe, but "the compiler cannot guarantee the safety of the + // memory". + // + // The only risk here is that the lifetime 'a comes to its end while the global variable + // `EVM_CONTEXT` still contains the reference to the precompile handle. + // The `using` method guarantee that it can't happen because the global variable is freed right + // after the execution of the `mutator` closure (whatever the result of the execution). + unsafe { + EVM_CONTEXT::using( + core::mem::transmute::<&'a mut dyn PrecompileHandle, &'static mut dyn PrecompileHandle>( + precompile_handle, + ), + mutator, + ) + } +} + +pub fn with_precompile_handle R>(f: F) -> Option { + EVM_CONTEXT::with(|precompile_handle| f(precompile_handle)) +} + +#[cfg(test)] +mod tests { + use super::*; + + struct MockPrecompileHandle; + impl PrecompileHandle for MockPrecompileHandle { + fn call( + &mut self, + _: sp_core::H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &evm::Context, + ) -> (evm::ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, _: u64) -> Result<(), evm::ExitError> { + unimplemented!() + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn log( + &mut self, + _: sp_core::H160, + _: Vec, + _: Vec, + ) -> Result<(), evm::ExitError> { + unimplemented!() + } + + fn code_address(&self) -> sp_core::H160 { + unimplemented!() + } + + fn input(&self) -> &[u8] { + unimplemented!() + } + + fn context(&self) -> &evm::Context { + unimplemented!() + } + + fn is_static(&self) -> bool { + true + } + + fn gas_limit(&self) -> Option { + unimplemented!() + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + ) -> Result<(), fp_evm::ExitError> { + Ok(()) + } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} + } + + #[test] + fn with_precompile_handle_without_context() { + assert_eq!(with_precompile_handle(|_| {}), None); + } + + #[test] + fn with_precompile_handle_with_context() { + let mut precompile_handle = MockPrecompileHandle; + + assert_eq!( + using_precompile_handle(&mut precompile_handle, || with_precompile_handle( + |handle| handle.is_static() + )), + Some(true) + ); + } +} diff --git a/precompiles/src/evm/logs.rs b/precompiles/src/evm/logs.rs new file mode 100644 index 0000000000..b1f68cb341 --- /dev/null +++ b/precompiles/src/evm/logs.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::EvmResult; +use pallet_evm::{Log, PrecompileHandle}; +use sp_core::{H160, H256}; +use sp_std::{vec, vec::Vec}; + +/// Create a 0-topic log. +#[must_use] +pub fn log0(address: impl Into, data: impl Into>) -> Log { + Log { + address: address.into(), + topics: vec![], + data: data.into(), + } +} + +/// Create a 1-topic log. +#[must_use] +pub fn log1(address: impl Into, topic0: impl Into, data: impl Into>) -> Log { + Log { + address: address.into(), + topics: vec![topic0.into()], + data: data.into(), + } +} + +/// Create a 2-topics log. +#[must_use] +pub fn log2( + address: impl Into, + topic0: impl Into, + topic1: impl Into, + data: impl Into>, +) -> Log { + Log { + address: address.into(), + topics: vec![topic0.into(), topic1.into()], + data: data.into(), + } +} + +/// Create a 3-topics log. +#[must_use] +pub fn log3( + address: impl Into, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + data: impl Into>, +) -> Log { + Log { + address: address.into(), + topics: vec![topic0.into(), topic1.into(), topic2.into()], + data: data.into(), + } +} + +/// Create a 4-topics log. +#[must_use] +pub fn log4( + address: impl Into, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + topic3: impl Into, + data: impl Into>, +) -> Log { + Log { + address: address.into(), + topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()], + data: data.into(), + } +} + +/// Extension trait allowing to record logs into a PrecompileHandle. +pub trait LogExt { + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult; + + fn compute_cost(&self) -> EvmResult; +} + +impl LogExt for Log { + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult { + handle.log(self.address, self.topics, self.data)?; + Ok(()) + } + + fn compute_cost(&self) -> EvmResult { + crate::evm::costs::log_costs(self.topics.len(), self.data.len()) + } +} diff --git a/precompiles/src/evm/mod.rs b/precompiles/src/evm/mod.rs new file mode 100644 index 0000000000..62e554ac79 --- /dev/null +++ b/precompiles/src/evm/mod.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub mod costs; +pub mod handle; +pub mod logs; diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs new file mode 100644 index 0000000000..df84db0a72 --- /dev/null +++ b/precompiles/src/lib.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +// Allows to use inside this crate `solidity::Codec` derive macro,which depends on +// `precompile_utils` being in the list of imported crates. +extern crate self as precompile_utils; + +pub mod evm; +pub mod precompile_set; +pub mod substrate; + +pub mod solidity; + +#[cfg(feature = "testing")] +pub mod testing; + +use fp_evm::PrecompileFailure; + +// pub mod data; + +// pub use data::{solidity::Codec, Reader, Writer}; +pub use fp_evm::Precompile; +pub use precompile_utils_macro::{keccak256, precompile, precompile_name_from_address}; + +/// Alias for Result returning an EVM precompile error. +pub type EvmResult = Result; + +pub mod prelude { + pub use { + crate::{ + evm::{ + handle::PrecompileHandleExt, + logs::{log0, log1, log2, log3, log4, LogExt}, + }, + precompile_set::DiscriminantResult, + solidity::{ + // We export solidity itself to encourage using `solidity::Codec` to avoid confusion + // with parity_scale_codec, + self, + codec::{ + Address, + BoundedBytes, + BoundedString, + BoundedVec, + // Allow usage of Codec methods while not exporting the name directly. + Codec as _, + Convert, + UnboundedBytes, + UnboundedString, + }, + revert::{ + revert, BacktraceExt, InjectBacktrace, MayRevert, Revert, RevertExt, + RevertReason, + }, + }, + substrate::{RuntimeHelper, TryDispatchError}, + EvmResult, + }, + alloc::string::String, + pallet_evm::{PrecompileHandle, PrecompileOutput}, + precompile_utils_macro::{keccak256, precompile}, + }; +} diff --git a/precompiles/src/precompile_set.rs b/precompiles/src/precompile_set.rs new file mode 100644 index 0000000000..378485b0d7 --- /dev/null +++ b/precompiles/src/precompile_set.rs @@ -0,0 +1,1099 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Provide utils to assemble precompiles and precompilesets into a +//! final precompile set with security checks. All security checks are enabled by +//! default and must be disabled explicely throught type annotations. + +use crate::{ + evm::handle::PrecompileHandleExt, + solidity::{codec::String, revert::revert}, + EvmResult, +}; +use fp_evm::{ + ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileResult, PrecompileSet, +}; +use frame_support::pallet_prelude::Get; +use impl_trait_for_tuples::impl_for_tuples; +use pallet_evm::AddressMapping; +use sp_core::{H160, H256}; +use sp_std::{ + cell::RefCell, collections::btree_map::BTreeMap, marker::PhantomData, ops::RangeInclusive, vec, + vec::Vec, +}; + +/// Trait representing checks that can be made on a precompile call. +/// Types implementing this trait are made to be chained in a tuple. +/// +/// For that reason every method returns an Option, None meaning that +/// the implementor have no constraint and the decision is left to +/// latter elements in the chain. If None is returned by all elements of +/// the chain then sensible defaults are used. +/// +/// Both `PrecompileAt` and `PrecompileSetStartingWith` have a type parameter that must +/// implement this trait to configure the checks of the precompile(set) it represents. +pub trait PrecompileChecks { + #[inline(always)] + /// Is there a limit to the amount of recursions this precompile + /// can make using subcalls? 0 means this specific precompile will not + /// be callable as a subcall of itself, 1 will allow one level of recursion, + /// etc... + /// + /// If all checks return None, defaults to `Some(0)` (no recursion allowed). + fn recursion_limit() -> Option> { + None + } + + #[inline(always)] + /// Does this precompile supports being called with DELEGATECALL or CALLCODE? + /// + /// If all checks return None, defaults to `false`. + fn accept_delegate_call() -> Option { + None + } + + #[inline(always)] + /// Is this precompile callable by a smart contract? + /// + /// If all checks return None, defaults to `false`. + fn callable_by_smart_contract(_caller: H160, _called_selector: Option) -> Option { + None + } + + #[inline(always)] + /// Is this precompile callable by a precompile? + /// + /// If all checks return None, defaults to `false`. + fn callable_by_precompile(_caller: H160, _called_selector: Option) -> Option { + None + } + + #[inline(always)] + /// Is this precompile able to do subcalls? + /// + /// If all checks return None, defaults to `false`. + fn allow_subcalls() -> Option { + None + } + + /// Summarize the checks when being called by a smart contract. + fn callable_by_smart_contract_summary() -> Option { + None + } + + /// Summarize the checks when being called by a precompile. + fn callable_by_precompile_summary() -> Option { + None + } +} + +#[derive(Debug, Clone)] +pub enum DiscriminantResult { + Some(T, u64), + None(u64), + OutOfGas, +} + +impl From> for IsPrecompileResult { + fn from(val: DiscriminantResult) -> Self { + match val { + DiscriminantResult::::Some(_, extra_cost) => IsPrecompileResult::Answer { + is_precompile: true, + extra_cost, + }, + DiscriminantResult::::None(extra_cost) => IsPrecompileResult::Answer { + is_precompile: false, + extra_cost, + }, + DiscriminantResult::::OutOfGas => IsPrecompileResult::OutOfGas, + } + } +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "testing", derive(serde::Serialize, serde::Deserialize))] +pub enum PrecompileKind { + Single(H160), + Prefixed(Vec), +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "testing", derive(serde::Serialize, serde::Deserialize))] +pub struct PrecompileCheckSummary { + pub name: Option, + pub precompile_kind: PrecompileKind, + pub recursion_limit: Option, + pub accept_delegate_call: bool, + pub callable_by_smart_contract: String, + pub callable_by_precompile: String, +} + +#[impl_for_tuples(0, 20)] +impl PrecompileChecks for Tuple { + #[inline(always)] + fn recursion_limit() -> Option> { + for_tuples!(#( + if let Some(check) = Tuple::recursion_limit() { + return Some(check); + } + )*); + + None + } + + #[inline(always)] + fn accept_delegate_call() -> Option { + for_tuples!(#( + if let Some(check) = Tuple::accept_delegate_call() { + return Some(check); + } + )*); + + None + } + + #[inline(always)] + fn callable_by_smart_contract(caller: H160, called_selector: Option) -> Option { + for_tuples!(#( + if let Some(check) = Tuple::callable_by_smart_contract(caller, called_selector) { + return Some(check); + } + )*); + + None + } + + #[inline(always)] + fn callable_by_precompile(caller: H160, called_selector: Option) -> Option { + for_tuples!(#( + if let Some(check) = Tuple::callable_by_precompile(caller, called_selector) { + return Some(check); + } + )*); + + None + } + + #[inline(always)] + fn allow_subcalls() -> Option { + for_tuples!(#( + if let Some(check) = Tuple::allow_subcalls() { + return Some(check); + } + )*); + + None + } + + fn callable_by_smart_contract_summary() -> Option { + for_tuples!(#( + if let Some(check) = Tuple::callable_by_smart_contract_summary() { + return Some(check); + } + )*); + + None + } + + fn callable_by_precompile_summary() -> Option { + for_tuples!(#( + if let Some(check) = Tuple::callable_by_precompile_summary() { + return Some(check); + } + )*); + + None + } +} + +/// Precompile can be called using DELEGATECALL/CALLCODE. +pub struct AcceptDelegateCall; + +impl PrecompileChecks for AcceptDelegateCall { + #[inline(always)] + fn accept_delegate_call() -> Option { + Some(true) + } +} + +/// Precompile is able to do subcalls with provided nesting limit. +pub struct SubcallWithMaxNesting; + +impl PrecompileChecks for SubcallWithMaxNesting { + #[inline(always)] + fn recursion_limit() -> Option> { + Some(Some(R)) + } + + #[inline(always)] + fn allow_subcalls() -> Option { + Some(true) + } +} + +pub trait SelectorFilter { + fn is_allowed(_caller: H160, _selector: Option) -> bool; + + fn description() -> String; +} +pub struct ForAllSelectors; +impl SelectorFilter for ForAllSelectors { + fn is_allowed(_caller: H160, _selector: Option) -> bool { + true + } + + fn description() -> String { + "Allowed for all selectors and callers".into() + } +} + +pub struct OnlyFrom(PhantomData); +impl> SelectorFilter for OnlyFrom { + fn is_allowed(caller: H160, _selector: Option) -> bool { + caller == T::get() + } + + fn description() -> String { + alloc::format!("Allowed for all selectors only if called from {}", T::get()) + } +} + +pub struct CallableByContract(PhantomData); + +impl PrecompileChecks for CallableByContract { + #[inline(always)] + fn callable_by_smart_contract(caller: H160, called_selector: Option) -> Option { + Some(T::is_allowed(caller, called_selector)) + } + + fn callable_by_smart_contract_summary() -> Option { + Some(T::description()) + } +} + +/// Precompiles are allowed to call this precompile. +pub struct CallableByPrecompile(PhantomData); + +impl PrecompileChecks for CallableByPrecompile { + #[inline(always)] + fn callable_by_precompile(caller: H160, called_selector: Option) -> Option { + Some(T::is_allowed(caller, called_selector)) + } + + fn callable_by_precompile_summary() -> Option { + Some(T::description()) + } +} + +/// The type of EVM address. +#[derive(PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum AddressType { + /// The code stored at the address is less than 5 bytes, but not well known. + Unknown, + /// No code is stored at the address, therefore is EOA. + EOA, + /// The 5-byte magic constant for a precompile is stored at the address. + Precompile, + /// The code is greater than 5-bytes, potentially a Smart Contract. + Contract, +} + +/// Retrieves the type of address demarcated by `AddressType`. +pub fn get_address_type( + handle: &mut impl PrecompileHandle, + address: H160, +) -> Result { + // AccountCodesMetadata: + // Blake2128(16) + H160(20) + CodeMetadata(40) + handle.record_db_read::(76)?; + let code_len = pallet_evm::Pallet::::account_code_metadata(address).size; + + // 0 => either EOA or precompile without dummy code + if code_len == 0 { + return Ok(AddressType::EOA); + } + + // dummy code is 5 bytes long, so any other len means it is a contract. + if code_len != 5 { + return Ok(AddressType::Contract); + } + + // check code matches dummy code + handle.record_db_read::(code_len as usize)?; + let code = pallet_evm::AccountCodes::::get(address); + if code == [0x60, 0x00, 0x60, 0x00, 0xfd] { + return Ok(AddressType::Precompile); + } + + Ok(AddressType::Unknown) +} + +fn is_address_eoa_or_precompile( + handle: &mut impl PrecompileHandle, + address: H160, +) -> Result { + match get_address_type::(handle, address)? { + AddressType::EOA | AddressType::Precompile => Ok(true), + _ => Ok(false), + } +} + +/// Common checks for precompile and precompile sets. +/// Don't contain recursion check as precompile sets have recursion check for each member. +fn common_checks( + handle: &mut impl PrecompileHandle, +) -> EvmResult<()> { + let code_address = handle.code_address(); + let caller = handle.context().caller; + + // Check DELEGATECALL config. + let accept_delegate_call = C::accept_delegate_call().unwrap_or(false); + if !accept_delegate_call && code_address != handle.context().address { + return Err(revert("Cannot be called with DELEGATECALL or CALLCODE")); + } + + // Extract which selector is called. + let selector = handle.input().get(0..4).map(|bytes| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(bytes); + u32::from_be_bytes(buffer) + }); + + // Is this selector callable from a smart contract? + let callable_by_smart_contract = + C::callable_by_smart_contract(caller, selector).unwrap_or(false); + if !callable_by_smart_contract && !is_address_eoa_or_precompile::(handle, caller)? { + return Err(revert("Function not callable by smart contracts")); + } + + // Is this selector callable from a precompile? + let callable_by_precompile = C::callable_by_precompile(caller, selector).unwrap_or(false); + if !callable_by_precompile && is_precompile_or_fail::(caller, handle.remaining_gas())? { + return Err(revert("Function not callable by precompiles")); + } + + Ok(()) +} + +pub fn is_precompile_or_fail(address: H160, gas: u64) -> EvmResult { + match ::PrecompilesValue::get().is_precompile(address, gas) { + IsPrecompileResult::Answer { is_precompile, .. } => Ok(is_precompile), + IsPrecompileResult::OutOfGas => Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }), + } +} + +pub struct AddressU64; +impl Get for AddressU64 { + #[inline(always)] + fn get() -> H160 { + H160::from_low_u64_be(N) + } +} + +pub struct RestrictiveHandle<'a, H> { + handle: &'a mut H, + allow_subcalls: bool, +} + +impl<'a, H: PrecompileHandle> PrecompileHandle for RestrictiveHandle<'a, H> { + fn call( + &mut self, + address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: &evm::Context, + ) -> (evm::ExitReason, Vec) { + if !self.allow_subcalls { + return ( + evm::ExitReason::Revert(evm::ExitRevert::Reverted), + crate::solidity::revert::revert_as_bytes("subcalls disabled for this precompile"), + ); + } + + self.handle + .call(address, transfer, input, target_gas, is_static, context) + } + + fn record_cost(&mut self, cost: u64) -> Result<(), evm::ExitError> { + self.handle.record_cost(cost) + } + + fn remaining_gas(&self) -> u64 { + self.handle.remaining_gas() + } + + fn log( + &mut self, + address: H160, + topics: Vec, + data: Vec, + ) -> Result<(), evm::ExitError> { + self.handle.log(address, topics, data) + } + + fn code_address(&self) -> H160 { + self.handle.code_address() + } + + fn input(&self) -> &[u8] { + self.handle.input() + } + + fn context(&self) -> &evm::Context { + self.handle.context() + } + + fn is_static(&self) -> bool { + self.handle.is_static() + } + + fn gas_limit(&self) -> Option { + self.handle.gas_limit() + } + + fn record_external_cost( + &mut self, + ref_time: Option, + proof_size: Option, + ) -> Result<(), ExitError> { + self.handle.record_external_cost(ref_time, proof_size) + } + + fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { + self.handle.refund_external_cost(ref_time, proof_size) + } +} + +/// Allows to know if a precompile is active or not. +/// This allows to detect deactivated precompile, that are still considered precompiles by +/// the EVM but that will always revert when called. +pub trait IsActivePrecompile { + /// Is the provided address an active precompile, a precompile that has + /// not be deactivated. Note that a deactivated precompile is still considered a precompile + /// for the EVM, but it will always revert when called. + fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult; +} + +// INDIVIDUAL PRECOMPILE(SET) + +/// A fragment of a PrecompileSet. Should be implemented as is it +/// was a PrecompileSet containing only the precompile(set) it wraps. +/// They can be combined into a real PrecompileSet using `PrecompileSetBuilder`. +pub trait PrecompileSetFragment { + /// Instanciate the fragment. + fn new() -> Self; + + /// Execute the fragment. + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option; + + /// Is the provided address a precompile in this fragment? + fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult; + + /// Return the list of addresses covered by this fragment. + fn used_addresses(&self) -> Vec; + + /// Summarize + fn summarize_checks(&self) -> Vec; +} + +/// Wraps a stateless precompile: a type implementing the `Precompile` trait. +/// Type parameters allow to define: +/// - A: The address of the precompile +/// - R: The recursion limit (defaults to 1) +/// - D: If DELEGATECALL is supported (default to no) +pub struct PrecompileAt { + current_recursion_level: RefCell, + _phantom: PhantomData<(A, P, C)>, +} + +impl PrecompileSetFragment for PrecompileAt +where + A: Get, + P: Precompile, + C: PrecompileChecks, +{ + #[inline(always)] + fn new() -> Self { + Self { + current_recursion_level: RefCell::new(0), + _phantom: PhantomData, + } + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + let code_address = handle.code_address(); + + // Check if this is the address of the precompile. + if A::get() != code_address { + return None; + } + + // Perform common checks. + if let Err(err) = common_checks::(handle) { + return Some(Err(err)); + } + + // Check and increase recursion level if needed. + let recursion_limit = C::recursion_limit().unwrap_or(Some(0)); + if let Some(max_recursion_level) = recursion_limit { + match self.current_recursion_level.try_borrow_mut() { + Ok(mut recursion_level) => { + if *recursion_level > max_recursion_level { + return Some(Err(revert("Precompile is called with too high nesting"))); + } + + *recursion_level += 1; + } + // We don't hold the borrow and are in single-threaded code, thus we should + // not be able to fail borrowing in nested calls. + Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))), + } + } + + // Subcall protection. + let allow_subcalls = C::allow_subcalls().unwrap_or(false); + let mut handle = RestrictiveHandle { + handle, + allow_subcalls, + }; + + let res = P::execute(&mut handle); + + // Decrease recursion level if needed. + if recursion_limit.is_some() { + match self.current_recursion_level.try_borrow_mut() { + Ok(mut recursion_level) => { + *recursion_level -= 1; + } + // We don't hold the borrow and are in single-threaded code, thus we should + // not be able to fail borrowing in nested calls. + Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))), + } + } + + Some(res) + } + + #[inline(always)] + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == A::get(), + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + vec![A::get()] + } + + fn summarize_checks(&self) -> Vec { + vec![PrecompileCheckSummary { + name: None, + precompile_kind: PrecompileKind::Single(A::get()), + recursion_limit: C::recursion_limit().unwrap_or(Some(0)), + accept_delegate_call: C::accept_delegate_call().unwrap_or(false), + callable_by_smart_contract: C::callable_by_smart_contract_summary() + .unwrap_or_else(|| "Not callable".into()), + callable_by_precompile: C::callable_by_precompile_summary() + .unwrap_or_else(|| "Not callable".into()), + }] + } +} + +impl IsActivePrecompile for PrecompileAt +where + A: Get, +{ + #[inline(always)] + fn is_active_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == A::get(), + extra_cost: 0, + } + } +} + +/// Wraps an inner PrecompileSet with all its addresses starting with +/// a common prefix. +/// Type parameters allow to define: +/// - A: The common prefix +/// - D: If DELEGATECALL is supported (default to no) +pub struct PrecompileSetStartingWith { + precompile_set: P, + current_recursion_level: RefCell>, + _phantom: PhantomData<(A, C)>, +} + +impl PrecompileSetFragment for PrecompileSetStartingWith +where + A: Get<&'static [u8]>, + P: PrecompileSet + Default, + C: PrecompileChecks, +{ + #[inline(always)] + fn new() -> Self { + Self { + precompile_set: P::default(), + current_recursion_level: RefCell::new(BTreeMap::new()), + _phantom: PhantomData, + } + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + let code_address = handle.code_address(); + if !is_precompile_or_fail::(code_address, handle.remaining_gas()).ok()? { + return None; + } + // Perform common checks. + if let Err(err) = common_checks::(handle) { + return Some(Err(err)); + } + + // Check and increase recursion level if needed. + let recursion_limit = C::recursion_limit().unwrap_or(Some(0)); + if let Some(max_recursion_level) = recursion_limit { + match self.current_recursion_level.try_borrow_mut() { + Ok(mut recursion_level_map) => { + let recursion_level = recursion_level_map.entry(code_address).or_insert(0); + + if *recursion_level > max_recursion_level { + return Some(Err(revert("Precompile is called with too high nesting"))); + } + + *recursion_level += 1; + } + // We don't hold the borrow and are in single-threaded code, thus we should + // not be able to fail borrowing in nested calls. + Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))), + } + } + + // Subcall protection. + let allow_subcalls = C::allow_subcalls().unwrap_or(false); + let mut handle = RestrictiveHandle { + handle, + allow_subcalls, + }; + + let res = self.precompile_set.execute(&mut handle); + + // Decrease recursion level if needed. + if recursion_limit.is_some() { + match self.current_recursion_level.try_borrow_mut() { + Ok(mut recursion_level_map) => { + let recursion_level = match recursion_level_map.get_mut(&code_address) { + Some(recursion_level) => recursion_level, + None => return Some(Err(revert("Couldn't retreive precompile nesting"))), + }; + + *recursion_level -= 1; + } + // We don't hold the borrow and are in single-threaded code, thus we should + // not be able to fail borrowing in nested calls. + Err(_) => return Some(Err(revert("Couldn't check precompile nesting"))), + } + } + + res + } + + #[inline(always)] + fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + if address.as_bytes().starts_with(A::get()) { + return self.precompile_set.is_precompile(address, gas); + } + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + // TODO: We currently can't get the list of used addresses. + vec![] + } + + fn summarize_checks(&self) -> Vec { + let prefix = A::get(); + + vec![PrecompileCheckSummary { + name: None, + precompile_kind: PrecompileKind::Prefixed(prefix.to_vec()), + recursion_limit: C::recursion_limit().unwrap_or(Some(0)), + accept_delegate_call: C::accept_delegate_call().unwrap_or(false), + callable_by_smart_contract: C::callable_by_smart_contract_summary() + .unwrap_or_else(|| "Not callable".into()), + callable_by_precompile: C::callable_by_precompile_summary() + .unwrap_or_else(|| "Not callable".into()), + }] + } +} + +impl IsActivePrecompile for PrecompileSetStartingWith +where + Self: PrecompileSetFragment, +{ + #[inline(always)] + fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + self.is_precompile(address, gas) + } +} + +/// Make a precompile that always revert. +/// Can be useful when writing tests. +pub struct RevertPrecompile(PhantomData); + +impl PrecompileSetFragment for RevertPrecompile +where + A: Get, +{ + #[inline(always)] + fn new() -> Self { + Self(PhantomData) + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + if A::get() == handle.code_address() { + Some(Err(revert("revert"))) + } else { + None + } + } + + #[inline(always)] + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == A::get(), + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + vec![A::get()] + } + + fn summarize_checks(&self) -> Vec { + vec![PrecompileCheckSummary { + name: None, + precompile_kind: PrecompileKind::Single(A::get()), + recursion_limit: Some(0), + accept_delegate_call: true, + callable_by_smart_contract: "Reverts in all cases".into(), + callable_by_precompile: "Reverts in all cases".into(), + }] + } +} + +impl IsActivePrecompile for RevertPrecompile { + #[inline(always)] + fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: true, + extra_cost: 0, + } + } +} + +/// A precompile that was removed from a precompile set. +/// Still considered a precompile but is inactive and always revert. +pub struct RemovedPrecompileAt(PhantomData); +impl PrecompileSetFragment for RemovedPrecompileAt +where + A: Get, +{ + #[inline(always)] + fn new() -> Self { + Self(PhantomData) + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + if A::get() == handle.code_address() { + Some(Err(revert("Removed precompile"))) + } else { + None + } + } + + #[inline(always)] + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == A::get(), + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + vec![A::get()] + } + + fn summarize_checks(&self) -> Vec { + vec![PrecompileCheckSummary { + name: None, + precompile_kind: PrecompileKind::Single(A::get()), + recursion_limit: Some(0), + accept_delegate_call: true, + callable_by_smart_contract: "Reverts in all cases".into(), + callable_by_precompile: "Reverts in all cases".into(), + }] + } +} + +impl IsActivePrecompile for RemovedPrecompileAt { + #[inline(always)] + fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } +} + +// COMPOSITION OF PARTS +#[impl_for_tuples(1, 100)] +impl PrecompileSetFragment for Tuple { + #[inline(always)] + fn new() -> Self { + (for_tuples!(#( + Tuple::new() + ),*)) + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + for_tuples!(#( + if let Some(res) = self.Tuple.execute::(handle) { + return Some(res); + } + )*); + + None + } + + #[inline(always)] + fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + for_tuples!(#( + if let IsPrecompileResult::Answer { + is_precompile: true, + .. + } = self.Tuple.is_precompile(address, gas) { return IsPrecompileResult::Answer { + is_precompile: true, + extra_cost: 0, + } + }; + )*); + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + let mut used_addresses = vec![]; + + for_tuples!(#( + let mut inner = self.Tuple.used_addresses(); + used_addresses.append(&mut inner); + )*); + + used_addresses + } + + fn summarize_checks(&self) -> Vec { + let mut checks = Vec::new(); + + for_tuples!(#( + let mut inner = self.Tuple.summarize_checks(); + checks.append(&mut inner); + )*); + + checks + } +} + +#[impl_for_tuples(1, 100)] +impl IsActivePrecompile for Tuple { + #[inline(always)] + fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + for_tuples!(#( + if let IsPrecompileResult::Answer { + is_precompile: true, + .. + } = self.Tuple.is_active_precompile(address, gas) { return IsPrecompileResult::Answer { + is_precompile: true, + extra_cost: 0, + } }; + )*); + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } +} + +/// Wraps a precompileset fragment into a range, and will skip processing it if the address +/// is out of the range. +pub struct PrecompilesInRangeInclusive { + inner: P, + range: RangeInclusive, + _phantom: PhantomData, +} + +impl PrecompileSetFragment for PrecompilesInRangeInclusive<(S, E), P> +where + S: Get, + E: Get, + P: PrecompileSetFragment, +{ + fn new() -> Self { + Self { + inner: P::new(), + range: RangeInclusive::new(S::get(), E::get()), + _phantom: PhantomData, + } + } + + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + if self.range.contains(&handle.code_address()) { + self.inner.execute::(handle) + } else { + None + } + } + + fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + if self.range.contains(&address) { + self.inner.is_precompile(address, gas) + } else { + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } + } + + fn used_addresses(&self) -> Vec { + self.inner.used_addresses() + } + + fn summarize_checks(&self) -> Vec { + self.inner.summarize_checks() + } +} + +impl IsActivePrecompile for PrecompilesInRangeInclusive<(S, E), P> +where + P: IsActivePrecompile, +{ + fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + if self.range.contains(&address) { + self.inner.is_active_precompile(address, gas) + } else { + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } + } +} + +/// Wraps a tuple of `PrecompileSetFragment` to make a real `PrecompileSet`. +pub struct PrecompileSetBuilder { + inner: P, + _phantom: PhantomData, +} + +impl PrecompileSet for PrecompileSetBuilder { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + self.inner.execute::(handle) + } + + fn is_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + self.inner.is_precompile(address, gas) + } +} + +impl IsActivePrecompile for PrecompileSetBuilder { + fn is_active_precompile(&self, address: H160, gas: u64) -> IsPrecompileResult { + self.inner.is_active_precompile(address, gas) + } +} + +impl Default for PrecompileSetBuilder { + fn default() -> Self { + Self::new() + } +} + +impl PrecompileSetBuilder { + /// Create a new instance of the PrecompileSet. + pub fn new() -> Self { + Self { + inner: P::new(), + _phantom: PhantomData, + } + } + + /// Return the list of addresses contained in this PrecompileSet. + pub fn used_addresses() -> impl Iterator { + Self::new() + .inner + .used_addresses() + .into_iter() + .map(R::AddressMapping::into_account_id) + } + + pub fn summarize_checks(&self) -> Vec { + self.inner.summarize_checks() + } +} diff --git a/precompiles/src/solidity/codec/bytes.rs b/precompiles/src/solidity/codec/bytes.rs new file mode 100644 index 0000000000..7f5e51cd16 --- /dev/null +++ b/precompiles/src/solidity/codec/bytes.rs @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; +use alloc::borrow::ToOwned; +use sp_core::{ConstU32, Get}; + +type ConstU32Max = ConstU32<{ u32::MAX }>; + +pub type UnboundedBytes = BoundedBytesString; +pub type BoundedBytes = BoundedBytesString; + +pub type UnboundedString = BoundedBytesString; +pub type BoundedString = BoundedBytesString; + +trait Kind { + fn signature() -> String; +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BytesKind; + +impl Kind for BytesKind { + fn signature() -> String { + String::from("bytes") + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct StringKind; + +impl Kind for StringKind { + fn signature() -> String { + String::from("string") + } +} + +/// The `bytes/string` type of Solidity. +/// It is different from `Vec` which will be serialized with padding for each `u8` element +/// of the array, while `Bytes` is tightly packed. +#[derive(Debug)] +pub struct BoundedBytesString { + data: Vec, + _phantom: PhantomData<(K, S)>, +} + +impl> Clone for BoundedBytesString { + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + _phantom: PhantomData, + } + } +} + +impl PartialEq> for BoundedBytesString { + fn eq(&self, other: &BoundedBytesString) -> bool { + self.data.eq(&other.data) + } +} + +impl Eq for BoundedBytesString {} + +impl> BoundedBytesString { + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> { + sp_std::str::from_utf8(&self.data) + } +} + +impl> Codec for BoundedBytesString { + fn read(reader: &mut Reader) -> MayRevert { + let mut inner_reader = reader.read_pointer()?; + + // Read bytes/string size. + let array_size: usize = inner_reader + .read::() + .map_err(|_| RevertReason::read_out_of_bounds("length"))? + .try_into() + .map_err(|_| RevertReason::value_is_too_large("length"))?; + + if array_size > S::get() as usize { + return Err(RevertReason::value_is_too_large("length").into()); + } + + // Get valid range over the bytes data. + let range = inner_reader.move_cursor(array_size)?; + + let data = inner_reader + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds(K::signature()))?; + + let bytes = Self { + data: data.to_owned(), + _phantom: PhantomData, + }; + + Ok(bytes) + } + + fn write(writer: &mut Writer, value: Self) { + let value: Vec<_> = value.into(); + let length = value.len(); + + // Pad the data. + // Leave it as is if a multiple of 32, otherwise pad to next + // multiple or 32. + let chunks = length / 32; + let padded_size = match length % 32 { + 0 => chunks * 32, + _ => (chunks + 1) * 32, + }; + + let mut value = value.to_vec(); + value.resize(padded_size, 0); + + writer.write_pointer( + Writer::new() + .write(U256::from(length)) + .write_raw_bytes(&value) + .build(), + ); + } + + fn has_static_size() -> bool { + false + } + + fn signature() -> String { + K::signature() + } +} + +// BytesString <=> Vec/&[u8] + +impl From> for Vec { + fn from(value: BoundedBytesString) -> Self { + value.data + } +} + +impl From> for BoundedBytesString { + fn from(value: Vec) -> Self { + Self { + data: value, + _phantom: PhantomData, + } + } +} + +impl From<&[u8]> for BoundedBytesString { + fn from(value: &[u8]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<[u8; N]> for BoundedBytesString { + fn from(value: [u8; N]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<&[u8; N]> for BoundedBytesString { + fn from(value: &[u8; N]) -> Self { + Self { + data: value.to_vec(), + _phantom: PhantomData, + } + } +} + +// BytesString <=> String/str + +impl TryFrom> for String { + type Error = alloc::string::FromUtf8Error; + + fn try_from(value: BoundedBytesString) -> Result { + alloc::string::String::from_utf8(value.data) + } +} + +impl From<&str> for BoundedBytesString { + fn from(value: &str) -> Self { + Self { + data: value.as_bytes().into(), + _phantom: PhantomData, + } + } +} + +impl From for BoundedBytesString { + fn from(value: String) -> Self { + Self { + data: value.as_bytes().into(), + _phantom: PhantomData, + } + } +} diff --git a/precompiles/src/solidity/codec/mod.rs b/precompiles/src/solidity/codec/mod.rs new file mode 100644 index 0000000000..26cf633c65 --- /dev/null +++ b/precompiles/src/solidity/codec/mod.rs @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Solidity encoding following the +//! [Contract ABI Specification](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#abi) + +pub mod bytes; +pub mod native; + +use crate::solidity::revert::{MayRevert, RevertReason}; +use core::{marker::PhantomData, ops::Range}; +use sp_core::{H256, U256}; +use sp_std::{convert::TryInto, vec, vec::Vec}; + +pub use alloc::string::String; +pub use bytes::{BoundedBytes, BoundedString, UnboundedBytes, UnboundedString}; +pub use native::{Address, BoundedVec}; + +// derive macro +pub use precompile_utils_macro::Codec; + +/// Data that can be encoded/encoded followiong the Solidity ABI Specification. +pub trait Codec: Sized { + fn read(reader: &mut Reader) -> MayRevert; + fn write(writer: &mut Writer, value: Self); + fn has_static_size() -> bool; + fn signature() -> String; + fn is_explicit_tuple() -> bool { + false + } +} + +/// Encode the value into its Solidity ABI format. +/// If `T` is a tuple it is encoded as a Solidity tuple with dynamic-size offset. +fn encode(value: T) -> Vec { + Writer::new().write(value).build() +} + +/// Encode the value into its Solidity ABI format. +/// If `T` is a tuple every element is encoded without a prefixed offset. +/// It matches the encoding of Solidity function arguments and return value, or event data. +pub fn encode_arguments(value: T) -> Vec { + let output = encode(value); + if T::is_explicit_tuple() && !T::has_static_size() { + output[32..].to_vec() + } else { + output + } +} + +pub use self::{encode_arguments as encode_return_value, encode_arguments as encode_event_data}; + +/// Encode the value as the arguments of a Solidity function with given selector. +/// If `T` is a tuple each member represents an argument of the function. +pub fn encode_with_selector(selector: u32, value: T) -> Vec { + Writer::new_with_selector(selector) + .write_raw_bytes(&encode_arguments(value)) + .build() +} + +/// Decode the value from its Solidity ABI format. +/// If `T` is a tuple it is decoded as a Solidity tuple with dynamic-size offset. +fn decode(input: &[u8]) -> MayRevert { + Reader::new(input).read() +} + +/// Decode the value from its Solidity ABI format. +/// If `T` is a tuple every element is decoded without a prefixed offset. +/// It matches the encoding of Solidity function arguments and return value, or event data. +pub fn decode_arguments(input: &[u8]) -> MayRevert { + if T::is_explicit_tuple() && !T::has_static_size() { + let writer = Writer::new(); + let mut writer = writer.write(U256::from(32)); + writer.write_pointer(input.to_vec()); + let input = writer.build(); + decode(&input) + } else { + decode(input) + } +} + +pub use self::{decode_arguments as decode_return_value, decode_arguments as decode_event_data}; + +/// Extracts the selector from the start of the input, or returns `None` if the input is too short. +pub fn selector(input: &[u8]) -> Option { + input.get(0..4).map(|s| { + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(s); + u32::from_be_bytes(buffer) + }) +} + +/// Wrapper around an EVM input slice. +#[derive(Clone, Copy, Debug)] +pub struct Reader<'inner> { + input: &'inner [u8], + cursor: usize, +} + +impl<'inner> Reader<'inner> { + /// Create a Reader. + pub fn new(input: &'inner [u8]) -> Self { + Self { input, cursor: 0 } + } + + /// Create a Reader while skipping an initial selector. + pub fn new_skip_selector(input: &'inner [u8]) -> MayRevert { + if input.len() < 4 { + return Err(RevertReason::read_out_of_bounds("selector").into()); + } + + Ok(Self::new(&input[4..])) + } + + /// Check the input has at least the correct amount of arguments before the end (32 bytes values). + pub fn expect_arguments(&self, args: usize) -> MayRevert { + if self.input.len() >= self.cursor + args * 32 { + Ok(()) + } else { + Err(RevertReason::ExpectedAtLeastNArguments(args).into()) + } + } + + /// Read data from the input. + pub fn read(&mut self) -> MayRevert { + T::read(self) + } + + /// Read raw bytes from the input. + /// Doesn't handle any alignment checks, prefer using `read` instead of possible. + /// Returns an error if trying to parse out of bounds. + pub fn read_raw_bytes(&mut self, len: usize) -> MayRevert<&[u8]> { + let range = self.move_cursor(len)?; + + let data = self + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds("raw bytes"))?; + + Ok(data) + } + + /// Reads a pointer, returning a reader targetting the pointed location. + pub fn read_pointer(&mut self) -> MayRevert { + let offset: usize = self + .read::() + .map_err(|_| RevertReason::read_out_of_bounds("pointer"))? + .try_into() + .map_err(|_| RevertReason::value_is_too_large("pointer"))?; + + if offset >= self.input.len() { + return Err(RevertReason::PointerToOutofBound.into()); + } + + Ok(Self { + input: &self.input[offset..], + cursor: 0, + }) + } + + /// Read remaining bytes + pub fn read_till_end(&mut self) -> MayRevert<&[u8]> { + let range = self.move_cursor(self.input.len() - self.cursor)?; + + let data = self + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds("raw bytes"))?; + + Ok(data) + } + + /// Move the reading cursor with provided length, and return a range from the previous cursor + /// location to the new one. + /// Checks cursor overflows. + fn move_cursor(&mut self, len: usize) -> MayRevert> { + let start = self.cursor; + let end = self + .cursor + .checked_add(len) + .ok_or(RevertReason::CursorOverflow)?; + + self.cursor = end; + + Ok(start..end) + } +} + +/// Help build an EVM input/output data. +/// +/// Functions takes `self` to allow chaining all calls like +/// `Writer::new().write(...).write(...).build()`. +/// While it could be more ergonomic to take &mut self, this would +/// prevent to have a `build` function that don't clone the output. +#[derive(Clone, Debug, Default)] +pub struct Writer { + pub(crate) data: Vec, + offset_data: Vec, + selector: Option, +} + +#[derive(Clone, Debug)] +struct OffsetChunk { + // Offset location in the container data. + offset_position: usize, + // Data pointed by the offset that must be inserted at the end of container data. + data: Vec, + // Inside of arrays, the offset is not from the start of array data (length), but from the start + // of the item. This shift allow to correct this. + offset_shift: usize, +} + +impl Writer { + /// Creates a new empty output builder (without selector). + pub fn new() -> Self { + Default::default() + } + + /// Creates a new empty output builder with provided selector. + /// Selector will only be appended before the data when calling + /// `build` to not mess with the offsets. + pub fn new_with_selector(selector: impl Into) -> Self { + Self { + data: vec![], + offset_data: vec![], + selector: Some(selector.into()), + } + } + + // Return the built data. + pub fn build(mut self) -> Vec { + Self::bake_offsets(&mut self.data, self.offset_data); + + if let Some(selector) = self.selector { + let mut output = selector.to_be_bytes().to_vec(); + output.append(&mut self.data); + output + } else { + self.data + } + } + + /// Add offseted data at the end of this writer's data, updating the offsets. + fn bake_offsets(output: &mut Vec, offsets: Vec) { + for mut offset_chunk in offsets { + let offset_position = offset_chunk.offset_position; + let offset_position_end = offset_position + 32; + + // The offset is the distance between the start of the data and the + // start of the pointed data (start of a struct, length of an array). + // Offsets in inner data are relative to the start of their respective "container". + // However in arrays the "container" is actually the item itself instead of the whole + // array, which is corrected by `offset_shift`. + let free_space_offset = output.len() - offset_chunk.offset_shift; + + // Override dummy offset to the offset it will be in the final output. + U256::from(free_space_offset) + .to_big_endian(&mut output[offset_position..offset_position_end]); + + // Append this data at the end of the current output. + output.append(&mut offset_chunk.data); + } + } + + /// Write arbitrary bytes. + /// Doesn't handle any alignement checks, prefer using `write` instead if possible. + fn write_raw_bytes(mut self, value: &[u8]) -> Self { + self.data.extend_from_slice(value); + self + } + + /// Write data of requested type. + pub fn write(mut self, value: T) -> Self { + T::write(&mut self, value); + self + } + + /// Writes a pointer to given data. + /// The data will be appended when calling `build`. + /// Initially write a dummy value as offset in this writer's data, which will be replaced by + /// the correct offset once the pointed data is appended. + /// + /// Takes `&mut self` since its goal is to be used inside `solidity::Codec` impl and not in chains. + pub fn write_pointer(&mut self, data: Vec) { + let offset_position = self.data.len(); + H256::write(self, H256::repeat_byte(0xff)); + + self.offset_data.push(OffsetChunk { + offset_position, + data, + offset_shift: 0, + }); + } +} + +/// Adapter to parse data as a first type then convert it to another one. +/// Useful for old precompiles in which Solidity arguments where set larger than +/// the needed Rust type. +#[derive(Clone, Copy, Debug)] +pub struct Convert { + inner: C, + _phantom: PhantomData

, +} + +impl From for Convert { + fn from(value: C) -> Self { + Self { + inner: value, + _phantom: PhantomData, + } + } +} + +impl Convert { + pub fn converted(self) -> C { + self.inner + } +} + +impl Codec for Convert +where + P: Codec + TryInto, + C: Codec + Into

, +{ + fn read(reader: &mut Reader) -> MayRevert { + let c = P::read(reader)? + .try_into() + .map_err(|_| RevertReason::value_is_too_large(C::signature()))?; + + Ok(Self { + inner: c, + _phantom: PhantomData, + }) + } + + fn write(writer: &mut Writer, value: Self) { + P::write(writer, value.inner.into()) + } + + fn has_static_size() -> bool { + P::has_static_size() + } + + fn signature() -> String { + P::signature() + } +} diff --git a/precompiles/src/solidity/codec/native.rs b/precompiles/src/solidity/codec/native.rs new file mode 100644 index 0000000000..4e02632a1f --- /dev/null +++ b/precompiles/src/solidity/codec/native.rs @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use super::*; +use crate::solidity::revert::InjectBacktrace; +use impl_trait_for_tuples::impl_for_tuples; +use sp_core::{ConstU32, Get, H160}; + +impl Codec for () { + fn read(_reader: &mut Reader) -> MayRevert { + Ok(()) + } + + fn write(_writer: &mut Writer, _value: Self) {} + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + String::from("()") + } +} + +#[impl_for_tuples(1, 18)] +impl Codec for Tuple { + fn has_static_size() -> bool { + for_tuples!(#( Tuple::has_static_size() )&*) + } + + fn read(reader: &mut Reader) -> MayRevert { + if Self::has_static_size() { + let mut index = 0; + Ok(for_tuples!( ( #( { + let elem = reader.read::().in_tuple(index)?; + index +=1; + elem + } ),* ) )) + } else { + let reader = &mut reader.read_pointer()?; + let mut index = 0; + Ok(for_tuples!( ( #( { + let elem = reader.read::().in_tuple(index)?; + index +=1; + elem + } ),* ) )) + } + } + + fn write(writer: &mut Writer, value: Self) { + if Self::has_static_size() { + for_tuples!( #( Tuple::write(writer, value.Tuple); )* ); + } else { + let mut inner_writer = Writer::new(); + for_tuples!( #( Tuple::write(&mut inner_writer, value.Tuple); )* ); + writer.write_pointer(inner_writer.build()); + } + } + + fn signature() -> String { + let mut subtypes = Vec::new(); + for_tuples!( #( subtypes.push(Tuple::signature()); )* ); + alloc::format!("({})", subtypes.join(",")) + } + + fn is_explicit_tuple() -> bool { + true + } +} + +impl Codec for H256 { + fn read(reader: &mut Reader) -> MayRevert { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds("bytes32"))?; + + Ok(H256::from_slice(data)) + } + + fn write(writer: &mut Writer, value: Self) { + writer.data.extend_from_slice(value.as_bytes()); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + String::from("bytes32") + } +} + +/// The `address` type of Solidity. +/// H160 could represent 2 types of data (bytes20 and address) that are not encoded the same way. +/// To avoid issues writing H160 is thus not supported. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct Address(pub H160); + +impl From for Address { + fn from(a: H160) -> Address { + Address(a) + } +} + +impl From

for H160 { + fn from(a: Address) -> H160 { + a.0 + } +} + +impl Address { + pub fn as_u64(&self) -> Option { + let _u64 = self.0.to_low_u64_be(); + if self.0 == H160::from_low_u64_be(_u64) { + Some(_u64) + } else { + None + } + } +} + +impl Codec for Address { + fn read(reader: &mut Reader) -> MayRevert { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds("address"))?; + + Ok(H160::from_slice(&data[12..32]).into()) + } + + fn write(writer: &mut Writer, value: Self) { + H256::write(writer, value.0.into()); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + String::from("address") + } +} + +impl Codec for U256 { + fn read(reader: &mut Reader) -> MayRevert { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| RevertReason::read_out_of_bounds("uint256"))?; + + Ok(U256::from_big_endian(data)) + } + + fn write(writer: &mut Writer, value: Self) { + let mut buffer = [0u8; 32]; + value.to_big_endian(&mut buffer); + writer.data.extend_from_slice(&buffer); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + String::from("uint256") + } +} + +macro_rules! impl_evmdata_for_uints { + ($($uint:ty, )*) => { + $( + impl Codec for $uint { + fn read(reader: &mut Reader) -> MayRevert { + let value256: U256 = reader.read() + .map_err(|_| RevertReason::read_out_of_bounds( + Self::signature() + ))?; + + value256 + .try_into() + .map_err(|_| RevertReason::value_is_too_large( + Self::signature() + ).into()) + } + + fn write(writer: &mut Writer, value: Self) { + U256::write(writer, value.into()); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + alloc::format!("uint{}", core::mem::size_of::() * 8) + } + } + )* + }; +} + +impl_evmdata_for_uints!(u8, u16, u32, u64, u128,); + +impl Codec for bool { + fn read(reader: &mut Reader) -> MayRevert { + let h256 = H256::read(reader).map_err(|_| RevertReason::read_out_of_bounds("bool"))?; + + Ok(!h256.is_zero()) + } + + fn write(writer: &mut Writer, value: Self) { + let mut buffer = [0u8; 32]; + if value { + buffer[31] = 1; + } + + writer.data.extend_from_slice(&buffer); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> String { + String::from("bool") + } +} + +type ConstU32Max = ConstU32<{ u32::MAX }>; + +impl Codec for Vec { + fn read(reader: &mut Reader) -> MayRevert { + BoundedVec::::read(reader).map(|x| x.into()) + } + + fn write(writer: &mut Writer, value: Self) { + BoundedVec::::write( + writer, + BoundedVec { + inner: value, + _phantom: PhantomData, + }, + ) + } + + fn has_static_size() -> bool { + false + } + + fn signature() -> String { + alloc::format!("{}[]", T::signature()) + } +} + +/// Wrapper around a Vec that provides a max length bound on read. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BoundedVec { + inner: Vec, + _phantom: PhantomData, +} + +impl> Codec for BoundedVec { + fn read(reader: &mut Reader) -> MayRevert { + let mut inner_reader = reader.read_pointer()?; + + let array_size: usize = inner_reader + .read::() + .map_err(|_| RevertReason::read_out_of_bounds("length"))? + .try_into() + .map_err(|_| RevertReason::value_is_too_large("length"))?; + + if array_size > S::get() as usize { + return Err(RevertReason::value_is_too_large("length").into()); + } + + let mut array = vec![]; + + let mut item_reader = Reader { + input: inner_reader + .input + .get(32..) + .ok_or_else(|| RevertReason::read_out_of_bounds("array content"))?, + cursor: 0, + }; + + for i in 0..array_size { + array.push(item_reader.read().in_array(i)?); + } + + Ok(BoundedVec { + inner: array, + _phantom: PhantomData, + }) + } + + fn write(writer: &mut Writer, value: Self) { + let value: Vec<_> = value.into(); + let mut inner_writer = Writer::new().write(U256::from(value.len())); + + for inner in value { + // Any offset in items are relative to the start of the item instead of the + // start of the array. However if there is offseted data it must but appended after + // all items (offsets) are written. We thus need to rely on `compute_offsets` to do + // that, and must store a "shift" to correct the offsets. + let shift = inner_writer.data.len(); + let item_writer = Writer::new().write(inner); + + inner_writer = inner_writer.write_raw_bytes(&item_writer.data); + for mut offset_datum in item_writer.offset_data { + offset_datum.offset_shift += 32; + offset_datum.offset_position += shift; + inner_writer.offset_data.push(offset_datum); + } + } + + writer.write_pointer(inner_writer.build()); + } + + fn has_static_size() -> bool { + false + } + + fn signature() -> String { + alloc::format!("{}[]", T::signature()) + } +} + +impl From> for BoundedVec { + fn from(value: Vec) -> Self { + BoundedVec { + inner: value, + _phantom: PhantomData, + } + } +} + +impl From<&[T]> for BoundedVec { + fn from(value: &[T]) -> Self { + BoundedVec { + inner: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From<[T; N]> for BoundedVec { + fn from(value: [T; N]) -> Self { + BoundedVec { + inner: value.to_vec(), + _phantom: PhantomData, + } + } +} + +impl From> for Vec { + fn from(value: BoundedVec) -> Self { + value.inner + } +} diff --git a/precompiles/src/solidity/mod.rs b/precompiles/src/solidity/mod.rs new file mode 100644 index 0000000000..1f271b2a4d --- /dev/null +++ b/precompiles/src/solidity/mod.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Provides utilities for compatibility with Solidity tooling. + +pub mod codec; +pub mod modifier; +pub mod revert; + +pub use codec::{ + decode_arguments, decode_event_data, decode_return_value, encode_arguments, encode_event_data, + encode_return_value, encode_with_selector, Codec, +}; diff --git a/precompiles/src/solidity/modifier.rs b/precompiles/src/solidity/modifier.rs new file mode 100644 index 0000000000..cf04ab17d3 --- /dev/null +++ b/precompiles/src/solidity/modifier.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Provide checks related to function modifiers (view/payable). + +use crate::solidity::revert::{MayRevert, RevertReason}; +use fp_evm::Context; +use sp_core::U256; + +/// Represents modifiers a Solidity function can be annotated with. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum FunctionModifier { + /// Function that doesn't modify the state. + View, + /// Function that modifies the state but refuse receiving funds. + /// Correspond to a Solidity function with no modifiers. + NonPayable, + /// Function that modifies the state and accept funds. + Payable, +} + +/// Check that a function call is compatible with the context it is +/// called into. +pub fn check_function_modifier( + context: &Context, + is_static: bool, + modifier: FunctionModifier, +) -> MayRevert { + if is_static && modifier != FunctionModifier::View { + return Err( + RevertReason::custom("Can't call non-static function in static context").into(), + ); + } + + if modifier != FunctionModifier::Payable && context.apparent_value > U256::zero() { + return Err(RevertReason::custom("Function is not payable").into()); + } + + Ok(()) +} diff --git a/precompiles/src/solidity/revert.rs b/precompiles/src/solidity/revert.rs new file mode 100644 index 0000000000..01b4d7b956 --- /dev/null +++ b/precompiles/src/solidity/revert.rs @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Utilities to work with revert messages with support for backtraces and +//! consistent formatting. + +use crate::solidity::{self, codec::bytes::UnboundedBytes}; +use alloc::string::{String, ToString}; +use fp_evm::{ExitRevert, PrecompileFailure}; +use sp_std::vec::Vec; + +/// Represent the result of a computation that can revert. +pub type MayRevert = Result; + +/// Generate an encoded revert from a simple String. +/// Returns a `PrecompileFailure` that fits in an `EvmResult::Err`. +pub fn revert(msg: impl Into) -> PrecompileFailure { + RevertReason::custom(msg).into() +} + +/// Generate an encoded revert from a simple String. +/// Returns a `Vec` in case `PrecompileFailure` is too high level. +pub fn revert_as_bytes(msg: impl Into) -> Vec { + Revert::new(RevertReason::custom(msg)).to_encoded_bytes() +} + +/// Generic error to build abi-encoded revert output. +/// See: https://docs.soliditylang.org/en/latest/control-structures.html?highlight=revert#revert +pub const ERROR_SELECTOR: u32 = 0x08c379a0; + +#[derive(Clone, PartialEq, Eq)] +enum BacktracePart { + Field(String), + Tuple(usize), + Array(usize), +} + +/// Backtrace of an revert. +/// Built depth-first. +/// Implement `Display` to render the backtrace as a string. +#[derive(Default, PartialEq, Eq)] +pub struct Backtrace(Vec); + +impl Backtrace { + /// Create a new empty backtrace. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Check if the backtrace is empty. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl core::fmt::Display for Backtrace { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + for (i, part) in self.0.iter().rev().enumerate() { + match (i, part) { + (0, BacktracePart::Field(field)) => write!(f, "{field}")?, + (_, BacktracePart::Field(field)) => write!(f, ".{field}")?, + (_, BacktracePart::Tuple(index)) => write!(f, ".{index}")?, + (_, BacktracePart::Array(index)) => write!(f, "[{index}]")?, + } + } + Ok(()) + } +} + +/// Possible revert reasons. +#[non_exhaustive] +#[derive(PartialEq, Eq)] +pub enum RevertReason { + /// A custom revert reason if other variants are not appropriate. + Custom(String), + /// Tried to read data out of bounds. + ReadOutOfBounds { + /// What was being read? + what: String, + }, + /// An unknown selector has been provided. + UnknownSelector, + /// A value is too large to fit in the wanted type. + /// For security reasons integers are always parsed as `uint256` then + /// casted to the wanted type. If the value overflows this type then this + /// revert is used. + ValueIsTooLarge { + /// What was being read? + what: String, + }, + /// A pointer (used for structs and arrays) points out of bounds. + PointerToOutofBound, + /// The reading cursor overflowed. + /// This should realistically never happen as it would require an input + /// of length larger than 2^64, which would cost too much to be included + /// in a block. + CursorOverflow, + /// Used by a check that the input contains at least N static arguments. + /// Often use to return early if the input is too short. + ExpectedAtLeastNArguments(usize), +} + +impl RevertReason { + /// Create a `RevertReason::Custom` from anything that can be converted to a `String`. + /// Argument is the custom revert message. + pub fn custom(s: impl Into) -> Self { + RevertReason::Custom(s.into()) + } + + /// Create a `RevertReason::ReadOutOfBounds` from anything that can be converted to a `String`. + /// Argument names what was expected to be read. + pub fn read_out_of_bounds(what: impl Into) -> Self { + RevertReason::ReadOutOfBounds { what: what.into() } + } + + /// Create a `RevertReason::ValueIsTooLarge` from anything that can be converted to a `String`. + /// Argument names what was expected to be read. + pub fn value_is_too_large(what: impl Into) -> Self { + RevertReason::ValueIsTooLarge { what: what.into() } + } +} + +impl core::fmt::Display for RevertReason { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + match self { + RevertReason::Custom(s) => write!(f, "{s}"), + RevertReason::ReadOutOfBounds { what } => { + write!(f, "Tried to read {what} out of bounds") + } + RevertReason::UnknownSelector => write!(f, "Unknown selector"), + RevertReason::ValueIsTooLarge { what } => write!(f, "Value is too large for {what}"), + RevertReason::PointerToOutofBound => write!(f, "Pointer points to out of bound"), + RevertReason::CursorOverflow => write!(f, "Reading cursor overflowed"), + RevertReason::ExpectedAtLeastNArguments(n) => { + write!(f, "Expected at least {n} arguments") + } + } + } +} + +/// An revert returned by various functions in precompile-utils. +/// Allows to dynamically construct the backtrace (backtrace) of the revert +/// and manage it in a typed way. +/// Can be transformed into a `PrecompileFailure::Revert` and `String`, and +/// implement `Display` and `Debug`. +#[derive(PartialEq, Eq)] +pub struct Revert { + reason: RevertReason, + backtrace: Backtrace, +} + +impl Revert { + /// Create a new `Revert` with a `RevertReason` and + /// an empty backtrace. + pub fn new(reason: RevertReason) -> Self { + Self { + reason, + backtrace: Backtrace::new(), + } + } + + /// For all `RevertReason` variants that have a `what` field, change its value. + /// Otherwise do nothing. + /// It is useful when writing custom types `solidity::Codec` implementations using + /// simpler types. + pub fn change_what(mut self, what: impl Into) -> Self { + let what = what.into(); + + self.reason = match self.reason { + RevertReason::ReadOutOfBounds { .. } => RevertReason::ReadOutOfBounds { what }, + RevertReason::ValueIsTooLarge { .. } => RevertReason::ValueIsTooLarge { what }, + other => other, + }; + + self + } + + /// Transforms the revert into its bytes representation (from a String). + pub fn to_encoded_bytes(self) -> Vec { + let bytes: Vec = self.into(); + solidity::encode_with_selector(ERROR_SELECTOR, UnboundedBytes::from(bytes)) + } +} + +impl From for Revert { + fn from(a: RevertReason) -> Revert { + Revert::new(a) + } +} + +impl core::fmt::Display for Revert { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + if !self.backtrace.is_empty() { + write!(f, "{}: ", self.backtrace)?; + } + + write!(f, "{}", self.reason) + } +} + +impl core::fmt::Debug for Revert { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!(f, "{}", self) + } +} + +impl From for Vec { + fn from(val: Revert) -> Self { + val.to_string().into() + } +} + +/// Allows to inject backtrace data. +pub trait InjectBacktrace { + /// Output type of the injection. + /// Should be a type that can hold a backtrace. + type Output; + + /// Occurs in a field. + fn in_field(self, field: impl Into) -> Self::Output; + + /// Occurs in a tuple. + fn in_tuple(self, index: usize) -> Self::Output; + + /// Occurs in an array at provided index. + fn in_array(self, index: usize) -> Self::Output; +} + +/// Additional function for everything having a Backtrace. +pub trait BacktraceExt { + /// Map last tuple entry into a field. + /// Does nothing if last entry is not a tuple. + /// As in Solidity structs are equivalent to tuples and are tricky to parse correctly, + /// it allows to parse any struct as a tuple (with the correct implementation in this crate) and + /// then map tuple indices to struct fields. + fn map_in_tuple_to_field(self, fields: &[&'static str]) -> Self; +} + +/// Additional functions for Revert and MayRevert. +pub trait RevertExt { + /// Map the reason while keeping the same backtrace. + fn map_reason(self, f: impl FnOnce(RevertReason) -> RevertReason) -> Self; +} + +impl InjectBacktrace for RevertReason { + // `RevertReason` cannot hold a backtrace, thus it wraps + // it into a `Revert`. + type Output = Revert; + + fn in_field(self, field: impl Into) -> Revert { + Revert::new(self).in_field(field) + } + + fn in_array(self, index: usize) -> Revert { + Revert::new(self).in_array(index) + } + + fn in_tuple(self, index: usize) -> Revert { + Revert::new(self).in_tuple(index) + } +} + +impl InjectBacktrace for Backtrace { + type Output = Self; + + fn in_field(mut self, field: impl Into) -> Self { + self.0.push(BacktracePart::Field(field.into())); + self + } + + fn in_array(mut self, index: usize) -> Self { + self.0.push(BacktracePart::Array(index)); + self + } + + fn in_tuple(mut self, index: usize) -> Self { + self.0.push(BacktracePart::Tuple(index)); + self + } +} + +impl BacktraceExt for Backtrace { + fn map_in_tuple_to_field(mut self, fields: &[&'static str]) -> Self { + if let Some(entry) = self.0.last_mut() { + if let BacktracePart::Tuple(index) = *entry { + if let Some(field) = fields.get(index) { + *entry = BacktracePart::Field(field.to_string()) + } + } + } + self + } +} + +impl InjectBacktrace for Revert { + type Output = Self; + + fn in_field(mut self, field: impl Into) -> Self { + self.backtrace = self.backtrace.in_field(field); + self + } + + fn in_array(mut self, index: usize) -> Self { + self.backtrace = self.backtrace.in_array(index); + self + } + + fn in_tuple(mut self, index: usize) -> Self { + self.backtrace = self.backtrace.in_tuple(index); + self + } +} + +impl RevertExt for Revert { + fn map_reason(mut self, f: impl FnOnce(RevertReason) -> RevertReason) -> Self { + self.reason = f(self.reason); + self + } +} + +impl BacktraceExt for Revert { + fn map_in_tuple_to_field(mut self, fields: &[&'static str]) -> Self { + self.backtrace = self.backtrace.map_in_tuple_to_field(fields); + self + } +} + +impl InjectBacktrace for MayRevert { + type Output = Self; + + fn in_field(self, field: impl Into) -> Self { + self.map_err(|e| e.in_field(field)) + } + + fn in_array(self, index: usize) -> Self { + self.map_err(|e| e.in_array(index)) + } + + fn in_tuple(self, index: usize) -> Self { + self.map_err(|e| e.in_tuple(index)) + } +} + +impl RevertExt for MayRevert { + fn map_reason(self, f: impl FnOnce(RevertReason) -> RevertReason) -> Self { + self.map_err(|e| e.map_reason(f)) + } +} + +impl BacktraceExt for MayRevert { + fn map_in_tuple_to_field(self, fields: &[&'static str]) -> Self { + self.map_err(|e| e.map_in_tuple_to_field(fields)) + } +} + +impl From for PrecompileFailure { + fn from(err: Revert) -> Self { + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: err.to_encoded_bytes(), + } + } +} + +impl From for PrecompileFailure { + fn from(err: RevertReason) -> Self { + Revert::new(err).into() + } +} diff --git a/precompiles/src/substrate.rs b/precompiles/src/substrate.rs new file mode 100644 index 0000000000..b54c71aa07 --- /dev/null +++ b/precompiles/src/substrate.rs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Utils related to Substrate features: +//! - Substrate call dispatch. +//! - Substrate DB read and write costs + +use crate::{evm::handle::using_precompile_handle, solidity::revert::revert}; +use core::marker::PhantomData; +use fp_evm::{ExitError, PrecompileFailure, PrecompileHandle}; +use frame_support::{ + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::Get, +}; +use pallet_evm::GasWeightMapping; + +#[derive(Debug)] +pub enum TryDispatchError { + Evm(ExitError), + Substrate(DispatchError), +} + +impl From for PrecompileFailure { + fn from(f: TryDispatchError) -> PrecompileFailure { + match f { + TryDispatchError::Evm(e) => PrecompileFailure::Error { exit_status: e }, + TryDispatchError::Substrate(e) => { + revert(alloc::format!("Dispatched call failed with error: {e:?}")) + } + } + } +} + +/// Helper functions requiring a Substrate runtime. +/// This runtime must of course implement `pallet_evm::Config`. +#[derive(Clone, Copy, Debug)] +pub struct RuntimeHelper(PhantomData); + +impl RuntimeHelper +where + Runtime: pallet_evm::Config, + Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, +{ + #[inline(always)] + pub fn record_weight_v2_cost( + handle: &mut impl PrecompileHandle, + weight: Weight, + ) -> Result<(), ExitError> { + // Make sure there is enough gas. + let remaining_gas = handle.remaining_gas(); + let required_gas = Runtime::GasWeightMapping::weight_to_gas(weight); + if required_gas > remaining_gas { + return Err(ExitError::OutOfGas); + } + + // Make sure there is enough remaining weight + // TODO: record ref time when precompile will be benchmarked + handle.record_external_cost(None, Some(weight.proof_size())) + } + + #[inline(always)] + pub fn refund_weight_v2_cost( + handle: &mut impl PrecompileHandle, + weight: Weight, + maybe_actual_weight: Option, + ) -> Result { + // Refund weights and compute used weight them record used gas + // TODO: refund ref time when precompile will be benchmarked + let used_weight = if let Some(actual_weight) = maybe_actual_weight { + let refund_weight = weight.checked_sub(&actual_weight).unwrap_or_default(); + handle.refund_external_cost(None, Some(refund_weight.proof_size())); + actual_weight + } else { + weight + }; + let used_gas = Runtime::GasWeightMapping::weight_to_gas(used_weight); + handle.record_cost(used_gas)?; + Ok(used_gas) + } + + /// Try to dispatch a Substrate call. + /// Return an error if there are not enough gas, or if the call fails. + /// If successful returns the used gas using the Runtime GasWeightMapping. + pub fn try_dispatch( + handle: &mut impl PrecompileHandle, + origin: ::RuntimeOrigin, + call: Call, + ) -> Result + where + Runtime::RuntimeCall: From, + { + let call = Runtime::RuntimeCall::from(call); + let dispatch_info = call.get_dispatch_info(); + + Self::record_weight_v2_cost(handle, dispatch_info.weight).map_err(TryDispatchError::Evm)?; + + // Dispatch call. + // It may be possible to not record gas cost if the call returns Pays::No. + // However while Substrate handle checking weight while not making the sender pay for it, + // the EVM doesn't. It seems this safer to always record the costs to avoid unmetered + // computations. + let post_dispatch_info = using_precompile_handle(handle, || call.dispatch(origin)) + .map_err(|e| TryDispatchError::Substrate(e.error))?; + + Self::refund_weight_v2_cost( + handle, + dispatch_info.weight, + post_dispatch_info.actual_weight, + ) + .map_err(TryDispatchError::Evm)?; + + Ok(post_dispatch_info) + } +} + +impl RuntimeHelper +where + Runtime: pallet_evm::Config, +{ + /// Cost of a Substrate DB write in gas. + pub fn db_write_gas_cost() -> u64 { + ::GasWeightMapping::weight_to_gas( + ::DbWeight::get().writes(1), + ) + } + + /// Cost of a Substrate DB read in gas. + pub fn db_read_gas_cost() -> u64 { + ::GasWeightMapping::weight_to_gas( + ::DbWeight::get().reads(1), + ) + } +} diff --git a/precompiles/src/testing/account.rs b/precompiles/src/testing/account.rs new file mode 100644 index 0000000000..07c91e07b2 --- /dev/null +++ b/precompiles/src/testing/account.rs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use pallet_evm::AddressMapping; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::{Decode, Encode, MaxEncodedLen, H160, H256}; + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Encode, + Decode, + Debug, + MaxEncodedLen, + TypeInfo, + Serialize, + Deserialize, + derive_more::Display +)] +pub struct MockAccount(pub H160); + +impl MockAccount { + pub fn from_u64(v: u64) -> Self { + H160::from_low_u64_be(v).into() + } + + pub fn zero() -> Self { + H160::zero().into() + } + + pub fn has_prefix(&self, prefix: &[u8]) -> bool { + &self.0[0..4] == prefix + } + + pub fn has_prefix_u32(&self, prefix: u32) -> bool { + self.0[0..4] == prefix.to_be_bytes() + } + + pub fn without_prefix(&self) -> u128 { + u128::from_be_bytes(<[u8; 16]>::try_from(&self.0[4..20]).expect("slice have len 16")) + } +} + +impl From for H160 { + fn from(account: MockAccount) -> H160 { + account.0 + } +} + +impl From for [u8; 20] { + fn from(account: MockAccount) -> [u8; 20] { + let x: H160 = account.into(); + x.into() + } +} + +impl From for H256 { + fn from(x: MockAccount) -> H256 { + let x: H160 = x.into(); + x.into() + } +} + +impl From for MockAccount { + fn from(address: H160) -> MockAccount { + MockAccount(address) + } +} + +impl From<[u8; 20]> for MockAccount { + fn from(address: [u8; 20]) -> MockAccount { + let x: H160 = address.into(); + MockAccount(x) + } +} + +impl AddressMapping for MockAccount { + fn into_account_id(address: H160) -> MockAccount { + address.into() + } +} + +impl sp_runtime::traits::Convert for MockAccount { + fn convert(address: H160) -> MockAccount { + address.into() + } +} + +#[macro_export] +macro_rules! mock_account { + ($name:ident, $convert:expr) => { + pub struct $name; + mock_account!(# $name, $convert); + }; + ($name:ident ( $($field:ty),* ), $convert:expr) => { + pub struct $name($(pub $field),*); + mock_account!(# $name, $convert); + }; + (# $name:ident, $convert:expr) => { + impl From<$name> for MockAccount { + fn from(value: $name) -> MockAccount { + $convert(value) + } + } + + impl From<$name> for sp_core::H160 { + fn from(value: $name) -> sp_core::H160 { + MockAccount::from(value).into() + } + } + + impl From<$name> for sp_core::H256 { + fn from(value: $name) -> sp_core::H256 { + MockAccount::from(value).into() + } + } + }; +} + +mock_account!(Zero, |_| MockAccount::zero()); +mock_account!(Alice, |_| H160::repeat_byte(0xAA).into()); +mock_account!(Bob, |_| H160::repeat_byte(0xBB).into()); +mock_account!(Charlie, |_| H160::repeat_byte(0xCC).into()); +mock_account!(David, |_| H160::repeat_byte(0xDD).into()); + +mock_account!(Precompile1, |_| MockAccount::from_u64(1)); + +mock_account!(CryptoAlith, |_| H160::from(hex_literal::hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" +)) +.into()); +mock_account!(CryptoBaltathar, |_| H160::from(hex_literal::hex!( + "3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0" +)) +.into()); +mock_account!(CryptoCarleth, |_| H160::from(hex_literal::hex!( + "798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc" +)) +.into()); + +mock_account!( + AddressInPrefixedSet(u32, u128), + |value: AddressInPrefixedSet| { + let prefix: u32 = value.0; + let index: u128 = value.1; + + let mut buffer = Vec::with_capacity(20); // 160 bits + + buffer.extend_from_slice(&prefix.to_be_bytes()); + buffer.extend_from_slice(&index.to_be_bytes()); + + assert_eq!(buffer.len(), 20, "address buffer should have len of 20"); + + H160::from_slice(&buffer).into() + } +); + +pub fn alith_secret_key() -> [u8; 32] { + hex_literal::hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133") +} + +pub fn baltathar_secret_key() -> [u8; 32] { + hex_literal::hex!("8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b") +} + +pub fn charleth_secret_key() -> [u8; 32] { + hex_literal::hex!("0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b") +} diff --git a/precompiles/src/testing/execution.rs b/precompiles/src/testing/execution.rs new file mode 100644 index 0000000000..9f643e9dc1 --- /dev/null +++ b/precompiles/src/testing/execution.rs @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + solidity::codec::Codec, + testing::{decode_revert_message, MockHandle, PrettyLog, SubcallHandle, SubcallTrait}, +}; +use fp_evm::{ + Context, ExitError, ExitSucceed, Log, PrecompileFailure, PrecompileOutput, PrecompileResult, + PrecompileSet, +}; +use sp_core::{H160, U256}; +use sp_std::boxed::Box; + +#[must_use] +pub struct PrecompilesTester<'p, P> { + precompiles: &'p P, + handle: MockHandle, + + target_gas: Option, + subcall_handle: Option, + + expected_cost: Option, + expected_logs: Option>, + static_call: bool, +} + +impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { + pub fn new( + precompiles: &'p P, + from: impl Into, + to: impl Into, + data: Vec, + ) -> Self { + let to = to.into(); + let mut handle = MockHandle::new( + to, + Context { + address: to, + caller: from.into(), + apparent_value: U256::zero(), + }, + ); + + handle.input = data; + + Self { + precompiles, + handle, + + target_gas: None, + subcall_handle: None, + + expected_cost: None, + expected_logs: None, + static_call: false, + } + } + + pub fn with_value(mut self, value: impl Into) -> Self { + self.handle.context.apparent_value = value.into(); + self + } + + pub fn with_subcall_handle(mut self, subcall_handle: impl SubcallTrait) -> Self { + self.subcall_handle = Some(Box::new(subcall_handle)); + self + } + + pub fn with_target_gas(mut self, target_gas: Option) -> Self { + self.target_gas = target_gas; + self + } + + pub fn with_static_call(mut self, static_call: bool) -> Self { + self.static_call = static_call; + self + } + + pub fn expect_cost(mut self, cost: u64) -> Self { + self.expected_cost = Some(cost); + self + } + + pub fn expect_no_logs(mut self) -> Self { + self.expected_logs = Some(vec![]); + self + } + + pub fn expect_log(mut self, log: Log) -> Self { + self.expected_logs = Some({ + let mut logs = self.expected_logs.unwrap_or_default(); + logs.push(PrettyLog(log)); + logs + }); + self + } + + fn assert_optionals(&self) { + if let Some(cost) = &self.expected_cost { + assert_eq!(&self.handle.gas_used, cost); + } + + if let Some(logs) = &self.expected_logs { + similar_asserts::assert_eq!(&self.handle.logs, logs); + } + } + + fn execute(&mut self) -> Option { + let handle = &mut self.handle; + handle.subcall_handle = self.subcall_handle.take(); + handle.is_static = self.static_call; + + if let Some(gas_limit) = self.target_gas { + handle.gas_limit = gas_limit; + } + + let res = self.precompiles.execute(handle); + + self.subcall_handle = handle.subcall_handle.take(); + + res + } + + /// Execute the precompile set and expect some precompile to have been executed, regardless of the + /// result. + pub fn execute_some(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and expect no precompile to have been executed. + pub fn execute_none(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_returns_raw(mut self, output: Vec) { + let res = self.execute(); + + match res { + Some(Err(PrecompileFailure::Revert { output, .. })) => { + let decoded = decode_revert_message(&output); + eprintln!( + "Revert message (bytes): {:?}", + sp_core::hexdisplay::HexDisplay::from(&decoded) + ); + eprintln!( + "Revert message (string): {:?}", + core::str::from_utf8(decoded).ok() + ); + panic!("Shouldn't have reverted"); + } + Some(Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: execution_output, + })) => { + if execution_output != output { + eprintln!( + "Output (bytes): {:?}", + sp_core::hexdisplay::HexDisplay::from(&execution_output) + ); + eprintln!( + "Output (string): {:?}", + core::str::from_utf8(&execution_output).ok() + ); + panic!("Output doesn't match"); + } + } + other => panic!("Unexpected result: {:?}", other), + } + + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided Solidity encoded output. + pub fn execute_returns(self, output: impl Codec) { + self.execute_returns_raw(crate::solidity::encode_return_value(output)) + } + + /// Execute the precompile set and check if it reverts. + /// Take a closure allowing to perform custom matching on the output. + pub fn execute_reverts(mut self, check: impl Fn(&[u8]) -> bool) { + let res = self.execute(); + + match res { + Some(Err(PrecompileFailure::Revert { output, .. })) => { + let decoded = decode_revert_message(&output); + if !check(decoded) { + eprintln!( + "Revert message (bytes): {:?}", + sp_core::hexdisplay::HexDisplay::from(&decoded) + ); + eprintln!( + "Revert message (string): {:?}", + core::str::from_utf8(decoded).ok() + ); + panic!("Revert reason doesn't match !"); + } + } + other => panic!("Didn't revert, instead returned {:?}", other), + } + + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_error(mut self, error: ExitError) { + let res = self.execute(); + assert_eq!( + res, + Some(Err(PrecompileFailure::Error { exit_status: error })) + ); + self.assert_optionals(); + } +} + +pub trait PrecompileTesterExt: PrecompileSet + Sized { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: impl Into>, + ) -> PrecompilesTester; +} + +impl PrecompileTesterExt for T { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: impl Into>, + ) -> PrecompilesTester { + PrecompilesTester::new(self, from, to, data.into()) + } +} diff --git a/precompiles/src/testing/handle.rs b/precompiles/src/testing/handle.rs new file mode 100644 index 0000000000..d070cc5e52 --- /dev/null +++ b/precompiles/src/testing/handle.rs @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::testing::PrettyLog; +use evm::{ExitRevert, ExitSucceed}; +use fp_evm::{Context, ExitError, ExitReason, Log, PrecompileHandle, Transfer}; +use sp_core::{H160, H256}; +use sp_std::boxed::Box; + +#[derive(Debug, Clone)] +pub struct Subcall { + pub address: H160, + pub transfer: Option, + pub input: Vec, + pub target_gas: Option, + pub is_static: bool, + pub context: Context, +} + +#[derive(Debug, Clone)] +pub struct SubcallOutput { + pub reason: ExitReason, + pub output: Vec, + pub cost: u64, + pub logs: Vec, +} + +impl SubcallOutput { + pub fn revert() -> Self { + Self { + reason: ExitReason::Revert(ExitRevert::Reverted), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } + + pub fn succeed() -> Self { + Self { + reason: ExitReason::Succeed(ExitSucceed::Returned), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } + + pub fn out_of_gas() -> Self { + Self { + reason: ExitReason::Error(ExitError::OutOfGas), + output: Vec::new(), + cost: 0, + logs: Vec::new(), + } + } +} + +pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {} + +impl SubcallOutput + 'static> SubcallTrait for T {} + +pub type SubcallHandle = Box; + +/// Mock handle to write tests for precompiles. +pub struct MockHandle { + pub gas_limit: u64, + pub gas_used: u64, + pub logs: Vec, + pub subcall_handle: Option, + pub code_address: H160, + pub input: Vec, + pub context: Context, + pub is_static: bool, +} + +impl MockHandle { + pub fn new(code_address: H160, context: Context) -> Self { + Self { + gas_limit: u64::MAX, + gas_used: 0, + logs: vec![], + subcall_handle: None, + code_address, + input: Vec::new(), + context, + is_static: false, + } + } +} + +impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec) { + if self + .record_cost(crate::evm::costs::call_cost( + context.apparent_value, + &evm::Config::london(), + )) + .is_err() + { + return (ExitReason::Error(ExitError::OutOfGas), vec![]); + } + + match &mut self.subcall_handle { + Some(handle) => { + let SubcallOutput { + reason, + output, + cost, + logs, + } = handle(Subcall { + address, + transfer, + input, + target_gas, + is_static, + context: context.clone(), + }); + + if self.record_cost(cost).is_err() { + return (ExitReason::Error(ExitError::OutOfGas), vec![]); + } + + for log in logs { + self.log(log.address, log.topics, log.data) + .expect("cannot fail"); + } + + (reason, output) + } + None => panic!("no subcall handle registered"), + } + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + + if self.gas_used > self.gas_limit { + Err(ExitError::OutOfGas) + } else { + Ok(()) + } + } + + fn remaining_gas(&self) -> u64 { + self.gas_limit - self.gas_used + } + + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + self.logs.push(PrettyLog(Log { + address, + topics, + data, + })); + Ok(()) + } + + /// Retreive the code address (what is the address of the precompile being called). + fn code_address(&self) -> H160 { + self.code_address + } + + /// Retreive the input data the precompile is called with. + fn input(&self) -> &[u8] { + &self.input + } + + /// Retreive the context in which the precompile is executed. + fn context(&self) -> &Context { + &self.context + } + + /// Is the precompile call is done statically. + fn is_static(&self) -> bool { + self.is_static + } + + /// Retreive the gas limit of this call. + fn gas_limit(&self) -> Option { + Some(self.gas_limit) + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + ) -> Result<(), ExitError> { + Ok(()) + } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} +} diff --git a/precompiles/src/testing/mod.rs b/precompiles/src/testing/mod.rs new file mode 100644 index 0000000000..6ebcc451b7 --- /dev/null +++ b/precompiles/src/testing/mod.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub mod account; +pub mod execution; +pub mod handle; +pub mod modifier; +mod solidity; + +pub use account::*; +pub use execution::*; +pub use handle::*; +pub use modifier::*; +pub use solidity::{check_precompile_implements_solidity_interfaces, compute_selector}; + +use fp_evm::Log; + +pub fn decode_revert_message(encoded: &[u8]) -> &[u8] { + let encoded_len = encoded.len(); + // selector 4 + offset 32 + string length 32 + if encoded_len > 68 { + let message_len = encoded[36..68].iter().sum::(); + if encoded_len >= 68 + message_len as usize { + return &encoded[68..68 + message_len as usize]; + } + } + b"decode_revert_message: error" +} + +#[derive(Clone, PartialEq, Eq)] +pub struct PrettyLog(Log); + +impl core::fmt::Debug for PrettyLog { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + let bytes = self + .0 + .data + .iter() + .map(|b| format!("{:02X}", b)) + .collect::>() + .join(""); + + let message = String::from_utf8(self.0.data.clone()).ok(); + + f.debug_struct("Log") + .field("address", &self.0.address) + .field("topics", &self.0.topics) + .field("data", &bytes) + .field("data_utf8", &message) + .finish() + } +} + +/// Panics if an event is not found in the system log of events +#[macro_export] +macro_rules! assert_event_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + $crate::mock::events().iter().find(|x| *x == e).is_some(), + "Event {:?} was not found in events: \n {:?}", + e, + $crate::mock::events() + ); + } + } + }; +} + +// Panics if an event is found in the system log of events +#[macro_export] +macro_rules! assert_event_not_emitted { + ($event:expr) => { + match &$event { + e => { + assert!( + $crate::mock::events().iter().find(|x| *x == e).is_none(), + "Event {:?} was found in events: \n {:?}", + e, + $crate::mock::events() + ); + } + } + }; +} diff --git a/precompiles/src/testing/modifier.rs b/precompiles/src/testing/modifier.rs new file mode 100644 index 0000000000..7b1aa4241e --- /dev/null +++ b/precompiles/src/testing/modifier.rs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{ + solidity::codec::Writer, + testing::{decode_revert_message, MockHandle}, +}; +use fp_evm::{Context, PrecompileFailure, PrecompileSet}; +use sp_core::{H160, U256}; + +pub struct PrecompilesModifierTester

{ + precompiles: P, + handle: MockHandle, +} + +impl PrecompilesModifierTester

{ + pub fn new(precompiles: P, from: impl Into, to: impl Into) -> Self { + let to = to.into(); + let mut handle = MockHandle::new( + to, + Context { + address: to, + caller: from.into(), + apparent_value: U256::zero(), + }, + ); + + handle.gas_limit = u64::MAX; + + Self { + precompiles, + handle, + } + } + + fn is_view(&mut self, selector: u32) -> bool { + // View: calling with static should not revert with static-related message. + let handle = &mut self.handle; + handle.is_static = true; + handle.context.apparent_value = U256::zero(); + handle.input = Writer::new_with_selector(selector).build(); + + let res = self.precompiles.execute(handle); + + match res { + Some(Err(PrecompileFailure::Revert { output, .. })) => { + let decoded = decode_revert_message(&output); + + dbg!(decoded) != b"Can't call non-static function in static context" + } + Some(_) => true, + None => panic!("tried to check view modifier on unknown precompile"), + } + } + + fn is_payable(&mut self, selector: u32) -> bool { + // Payable: calling with value should not revert with payable-related message. + let handle = &mut self.handle; + handle.is_static = false; + handle.context.apparent_value = U256::one(); + handle.input = Writer::new_with_selector(selector).build(); + + let res = self.precompiles.execute(handle); + + match res { + Some(Err(PrecompileFailure::Revert { output, .. })) => { + let decoded = decode_revert_message(&output); + + decoded != b"Function is not payable" + } + Some(_) => true, + None => panic!("tried to check payable modifier on unknown precompile"), + } + } + + pub fn test_view_modifier(&mut self, selectors: &[u32]) { + for &s in selectors { + assert!( + self.is_view(s), + "Function doesn't behave like a view function." + ); + assert!( + !self.is_payable(s), + "Function doesn't behave like a non-payable function." + ) + } + } + + pub fn test_payable_modifier(&mut self, selectors: &[u32]) { + for &s in selectors { + assert!( + !self.is_view(s), + "Function doesn't behave like a non-view function." + ); + assert!( + self.is_payable(s), + "Function doesn't behave like a payable function." + ); + } + } + + pub fn test_default_modifier(&mut self, selectors: &[u32]) { + for &s in selectors { + assert!( + !self.is_view(s), + "Function doesn't behave like a non-view function." + ); + assert!( + !self.is_payable(s), + "Function doesn't behave like a non-payable function." + ); + } + } +} diff --git a/precompiles/src/testing/solidity.rs b/precompiles/src/testing/solidity.rs new file mode 100644 index 0000000000..7fd8bad58a --- /dev/null +++ b/precompiles/src/testing/solidity.rs @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Utility module to interact with solidity file. + +use sp_io::hashing::keccak_256; +use std::{ + collections::HashMap, + fs::File, + io::{BufRead, BufReader, Read}, +}; + +pub fn check_precompile_implements_solidity_interfaces( + files: &[&'static str], + supports_selector: F, +) where + F: Fn(u32) -> bool, +{ + for file in files { + for solidity_fn in get_selectors(file) { + assert_eq!( + solidity_fn.compute_selector_hex(), + solidity_fn.docs_selector, + "documented selector for '{}' did not match in file '{}'", + solidity_fn.signature(), + file, + ); + + let selector = solidity_fn.compute_selector(); + if !supports_selector(selector) { + panic!( + "precompile don't support selector {selector:x} for function '{}' listed in file\ + {file}", + solidity_fn.signature(), + ) + } + } + } +} + +/// Represents a declared custom type struct within a solidity file +#[derive(Clone, Default, Debug)] +pub struct SolidityStruct { + /// Struct name + pub name: String, + /// List of parameter types + pub params: Vec, + /// Is struct an enum + pub is_enum: bool, +} + +impl SolidityStruct { + /// Returns the representative signature for the solidity struct + pub fn signature(&self) -> String { + if self.is_enum { + "uint8".to_string() + } else { + format!("({})", self.params.join(",")) + } + } +} + +/// Represents a declared function within a solidity file +#[derive(Clone, Default)] +pub struct SolidityFunction { + /// Function name + pub name: String, + /// List of function parameter types + pub args: Vec, + /// The declared selector in the file + pub docs_selector: String, +} + +impl SolidityFunction { + /// Returns the representative signature for the solidity function + pub fn signature(&self) -> String { + format!("{}({})", self.name, self.args.join(",")) + } + + /// Computes the selector code for the solidity function + pub fn compute_selector(&self) -> u32 { + compute_selector(&self.signature()) + } + + /// Computes the selector code as a hex string for the solidity function + pub fn compute_selector_hex(&self) -> String { + format!("{:0>8x}", self.compute_selector()) + } +} + +/// Computes a solidity selector from a given string +pub fn compute_selector(v: &str) -> u32 { + let output = keccak_256(v.as_bytes()); + let mut buf = [0u8; 4]; + buf.clone_from_slice(&output[..4]); + u32::from_be_bytes(buf) +} + +/// Returns a list of [SolidityFunction] defined in a solidity file +pub fn get_selectors(filename: &str) -> Vec { + let file = File::open(filename) + .unwrap_or_else(|e| panic!("failed opening file '{}': {}", filename, e)); + get_selectors_from_reader(file) +} + +/// Attempts to lookup a custom struct and returns its primitive signature +fn try_lookup_custom_type(word: &str, custom_types: &HashMap) -> String { + match word.strip_suffix("[]") { + Some(word) => { + if let Some(t) = custom_types.get(word) { + return format!("{}[]", t.signature()); + } + } + None => { + if let Some(t) = custom_types.get(word) { + return t.signature(); + } + } + }; + + word.to_string() +} + +fn get_selectors_from_reader(reader: R) -> Vec { + #[derive(Clone, Copy)] + enum Stage { + Start, + Enum, + Struct, + StructParams, + FnName, + Args, + } + #[derive(Clone, Copy)] + enum Pair { + First, + Second, + } + impl Pair { + fn next(&mut self) { + *self = match self { + Pair::First => Pair::Second, + Pair::Second => Pair::First, + } + } + } + + let reader = BufReader::new(reader); + let mut functions = vec![]; + let mut custom_types = HashMap::new(); + let mut solidity_struct = SolidityStruct::default(); + + let mut stage = Stage::Start; + let mut pair = Pair::First; + let mut solidity_fn = SolidityFunction::default(); + for line in reader.lines() { + let line = line.expect("failed unwrapping line").trim().to_string(); + // identify declared selector + if line.starts_with("/// @custom:selector ") && matches!(stage, Stage::Start) { + solidity_fn.docs_selector = line.replace("/// @custom:selector ", "").to_string(); + } + + // skip comments + if line.starts_with("//") { + continue; + } + + for word in line.split(&[';', ',', '(', ')', ' ']) { + // skip whitespace + if word.trim().is_empty() { + continue; + } + match (stage, pair, word) { + // parse custom type enums + (Stage::Start, Pair::First, "enum") => { + stage = Stage::Enum; + pair.next(); + } + (Stage::Enum, Pair::Second, _) => { + custom_types.insert( + word.to_string(), + SolidityStruct { + name: word.to_string(), + is_enum: true, + params: vec![], + }, + ); + stage = Stage::Start; + pair = Pair::First; + } + + // parse custom type structs + (Stage::Start, Pair::First, "struct") => { + stage = Stage::Struct; + pair.next(); + } + (Stage::Struct, Pair::Second, _) => { + solidity_struct.name = word.to_string(); + stage = Stage::StructParams; + pair.next(); + } + (Stage::StructParams, Pair::First, "{") => (), + (Stage::StructParams, Pair::First, "}") => { + custom_types.insert(solidity_struct.name.clone(), solidity_struct); + stage = Stage::Start; + solidity_struct = SolidityStruct::default(); + } + (Stage::StructParams, Pair::First, _) => { + let param = try_lookup_custom_type(word, &custom_types); + solidity_struct.params.push(param); + pair.next(); + } + (Stage::StructParams, Pair::Second, _) => { + pair.next(); + } + + // parse function + (Stage::Start, Pair::First, "function") => { + stage = Stage::FnName; + pair.next(); + } + (Stage::FnName, Pair::Second, _) => { + solidity_fn.name = word.to_string(); + stage = Stage::Args; + pair.next(); + } + (Stage::Args, Pair::First, "external") => { + functions.push(solidity_fn); + stage = Stage::Start; + pair = Pair::First; + solidity_fn = SolidityFunction::default() + } + (Stage::Args, Pair::First, _) => { + let mut arg = word.to_string(); + arg = try_lookup_custom_type(&arg, &custom_types); + + solidity_fn.args.push(arg); + pair.next(); + } + (Stage::Args, Pair::Second, "memory" | "calldata" | "storage") => (), + (Stage::Args, Pair::Second, _) => pair.next(), + _ => { + stage = Stage::Start; + pair = Pair::First; + solidity_fn = SolidityFunction::default() + } + } + } + } + + functions +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_selectors_are_parsed() { + let actual = get_selectors("tests/solidity_test.sol") + .into_iter() + .map(|sol_fn| { + ( + sol_fn.compute_selector_hex(), + sol_fn.docs_selector.clone(), + sol_fn.signature(), + ) + }) + .collect::>(); + let expected = vec![ + ( + String::from("f7af8d91"), + String::from(""), + String::from("fnNoArgs()"), + ), + ( + String::from("d43a9a43"), + String::from("c4921133"), + String::from("fnOneArg(address)"), + ), + ( + String::from("40d6a43d"), + String::from("67ea837e"), + String::from("fnTwoArgs(address,uint256)"), + ), + ( + String::from("cee150c8"), + String::from("d6b423d9"), + String::from("fnSameArgs(uint64,uint64)"), + ), + ( + String::from("c6024207"), + String::from("b9904a86"), + String::from("fnOneArgSameLine(uint64)"), + ), + ( + String::from("fcbc04c3"), + String::from("28f0c44e"), + String::from("fnTwoArgsSameLine(uint64,bytes32)"), + ), + ( + String::from("c590304c"), + String::from("06f0c1ce"), + String::from("fnTwoArgsSameLineExternalSplit(uint64,bytes32)"), + ), + ( + String::from("a19a07e1"), + String::from("18001a4e"), + String::from("fnMemoryArrayArgs(address[],uint256[],bytes[])"), + ), + ( + String::from("ec26cf1c"), + String::from("1ea61a4e"), + String::from("fnCalldataArgs(string,bytes[])"), + ), + ( + String::from("f29f96de"), + String::from("d8af1a4e"), + String::from("fnCustomArgs((uint8,bytes[]),bytes[],uint64)"), + ), + ( + String::from("d751d651"), + String::from("e8af1642"), + String::from("fnEnumArgs(uint8,uint64)"), + ), + ( + String::from("b2c9f1a3"), + String::from("550c1a4e"), + String::from( + "fnCustomArgsMultiple((uint8,bytes[]),(address[],uint256[],bytes[]),bytes[],\ + uint64)", + ), + ), + ( + String::from("d5363eee"), + String::from("77af1a40"), + String::from("fnCustomArrayArgs((uint8,bytes[])[],bytes[])"), + ), + ( + String::from("b82da727"), + String::from("80af0a40"), + String::from( + "fnCustomComposedArg(((uint8,bytes[]),\ + (address[],uint256[],bytes[])[]),uint64)", + ), + ), + ( + String::from("586a2193"), + String::from("97baa040"), + String::from( + "fnCustomComposedArrayArg(((uint8,bytes[]),\ + (address[],uint256[],bytes[])[])[],uint64)", + ), + ), + ]; + + assert_eq!(expected, actual); + } +} diff --git a/precompiles/tests-external/Cargo.toml b/precompiles/tests-external/Cargo.toml new file mode 100644 index 0000000000..87fae7e552 --- /dev/null +++ b/precompiles/tests-external/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "precompile-utils-tests-external" +authors = { workspace = true } +edition = "2021" +version = "0.1.0" + +[lib] +path = "./lib.rs" + +[dependencies] +derive_more = { workspace = true } +hex-literal = { workspace = true } +precompile-utils = { workspace = true, features = ["testing"] } +serde = { workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-balances = { workspace = true, features = ["insecure_zero_ed"] } +pallet-timestamp = { workspace = true } +scale-codec = { package = "parity-scale-codec", workspace = true, features = ["max-encoded-len"] } +scale-info = { workspace = true, features = ["derive"] } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +evm = { workspace = true, features = ["with-codec"] } +fp-evm = { workspace = true } +pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } diff --git a/precompiles/tests-external/lib.rs b/precompiles/tests-external/lib.rs new file mode 100644 index 0000000000..36cb4bfbff --- /dev/null +++ b/precompiles/tests-external/lib.rs @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2023 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#[cfg(test)] +mod tests { + use std::{cell::RefCell, rc::Rc}; + + use evm::Context; + use fp_evm::{ExitReason, ExitRevert, PrecompileFailure, PrecompileHandle}; + use frame_support::{construct_runtime, parameter_types, traits::Everything, weights::Weight}; + use pallet_evm::{EnsureAddressNever, EnsureAddressRoot}; + use precompile_utils::{ + precompile_set::*, + solidity::{codec::Writer, revert::revert}, + testing::*, + EvmResult, + }; + use sp_core::{H160, H256, U256}; + use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + Perbill, + }; + + pub type AccountId = MockAccount; + pub type Balance = u128; + pub type BlockNumber = u32; + + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + type Block = frame_system::mocking::MockBlock; + + construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + } + ); + + parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; + } + + impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + parameter_types! { + pub const ExistentialDeposit: u128 = 0; + } + impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxHolds = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + } + + #[derive(Debug, Clone)] + pub struct MockPrecompile; + + #[precompile_utils::precompile] + impl MockPrecompile { + // a3cab0dd + #[precompile::public("subcall()")] + fn subcall(handle: &mut impl PrecompileHandle) -> EvmResult { + match handle.call( + handle.code_address(), + None, + // calls subcallLayer2() + Writer::new_with_selector(0x0b93381bu32).build(), + None, + false, + &Context { + caller: handle.code_address(), + address: handle.code_address(), + apparent_value: 0.into(), + }, + ) { + (ExitReason::Succeed(_), _) => Ok(()), + (ExitReason::Revert(_), v) => Err(PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: v, + }), + _ => Err(revert("unexpected error")), + } + } + + // 0b93381b + #[precompile::public("success()")] + fn success(_: &mut impl PrecompileHandle) -> EvmResult { + Ok(()) + } + } + + struct MockPrecompileHandle; + impl PrecompileHandle for MockPrecompileHandle { + fn call( + &mut self, + _: sp_core::H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &evm::Context, + ) -> (evm::ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, _: u64) -> Result<(), evm::ExitError> { + Ok(()) + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn log( + &mut self, + _: sp_core::H160, + _: Vec, + _: Vec, + ) -> Result<(), evm::ExitError> { + unimplemented!() + } + + fn code_address(&self) -> sp_core::H160 { + unimplemented!() + } + + fn input(&self) -> &[u8] { + unimplemented!() + } + + fn context(&self) -> &evm::Context { + unimplemented!() + } + + fn is_static(&self) -> bool { + true + } + + fn gas_limit(&self) -> Option { + unimplemented!() + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + ) -> Result<(), fp_evm::ExitError> { + Ok(()) + } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} + } + + pub type Precompiles = PrecompileSetBuilder< + R, + ( + PrecompileAt, MockPrecompile>, + PrecompileAt, MockPrecompile, CallableByContract>, + PrecompileAt, MockPrecompile, CallableByPrecompile>, + PrecompileAt, MockPrecompile, SubcallWithMaxNesting<1>>, + ), + >; + + pub type PCall = MockPrecompileCall; + + const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; + + parameter_types! { + pub BlockGasLimit: U256 = U256::from(u64::MAX); + pub PrecompilesValue: Precompiles = Precompiles::new(); + pub const WeightPerGas: Weight = Weight::from_parts(1, 0); + pub GasLimitPovSizeRatio: u64 = { + let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); + block_gas_limit.saturating_div(MAX_POV_SIZE) + }; + } + + impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = AccountId; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = Precompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = (); + type OnChargeTransaction = (); + type BlockGasLimit = BlockGasLimit; + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); + type OnCreate = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type Timestamp = Timestamp; + type WeightInfo = pallet_evm::weights::SubstrateWeight; + } + + parameter_types! { + pub const MinimumPeriod: u64 = 5; + } + impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); + } + + struct ExtBuilder; + + impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder + } + } + + impl ExtBuilder { + #[cfg(test)] + fn build(self) -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default() + .build_storage::() + .expect("Frame system builds valid default genesis config"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } + } + + #[cfg(test)] + fn precompiles() -> Precompiles { + PrecompilesValue::get() + } + + #[test] + fn default_checks_succeed_when_called_by_eoa() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test(Alice, H160::from_low_u64_be(1), PCall::success {}) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_returns(()) + }) + } + + #[test] + fn default_checks_revert_when_called_by_precompile() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test( + H160::from_low_u64_be(1), + H160::from_low_u64_be(1), + PCall::success {}, + ) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_reverts(|r| r == b"Function not callable by precompiles") + }) + } + + #[test] + fn default_checks_revert_when_called_by_contract() { + ExtBuilder::default().build().execute_with(|| { + pallet_evm::Pallet::::create_account( + Alice.into(), + hex_literal::hex!("1460006000fd").to_vec(), + ); + + precompiles() + .prepare_test(Alice, H160::from_low_u64_be(1), PCall::success {}) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_reverts(|r| r == b"Function not callable by smart contracts") + }) + } + + #[test] + fn default_checks_revert_when_doing_subcall() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test(Alice, H160::from_low_u64_be(1), PCall::subcall {}) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_reverts(|r| r == b"subcalls disabled for this precompile") + }) + } + + #[test] + fn callable_by_contract_works() { + ExtBuilder::default().build().execute_with(|| { + pallet_evm::Pallet::::create_account( + Alice.into(), + hex_literal::hex!("1460006000fd").to_vec(), + ); + + precompiles() + .prepare_test(Alice, H160::from_low_u64_be(2), PCall::success {}) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_returns(()) + }) + } + + #[test] + fn callable_by_precompile_works() { + ExtBuilder::default().build().execute_with(|| { + precompiles() + .prepare_test( + H160::from_low_u64_be(3), + H160::from_low_u64_be(3), + PCall::success {}, + ) + .with_subcall_handle(|Subcall { .. }| panic!("there should be no subcall")) + .execute_returns(()) + }) + } + + #[test] + fn subcalls_works_when_allowed() { + ExtBuilder::default().build().execute_with(|| { + let subcall_occured = Rc::new(RefCell::new(false)); + { + let subcall_occured = Rc::clone(&subcall_occured); + precompiles() + .prepare_test(Alice, H160::from_low_u64_be(4), PCall::subcall {}) + .with_subcall_handle(move |Subcall { .. }| { + *subcall_occured.borrow_mut() = true; + SubcallOutput::succeed() + }) + .execute_returns(()); + } + assert!(*subcall_occured.borrow()); + }) + } + + #[test] + fn get_address_type_works_for_eoa() { + ExtBuilder::default().build().execute_with(|| { + let addr = H160::repeat_byte(0x1d); + assert_eq!( + AddressType::EOA, + get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + ); + }) + } + + #[test] + fn get_address_type_works_for_precompile() { + ExtBuilder::default().build().execute_with(|| { + let addr = H160::repeat_byte(0x1d); + pallet_evm::AccountCodes::::insert(addr, vec![0x60, 0x00, 0x60, 0x00, 0xfd]); + assert_eq!( + AddressType::Precompile, + get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + ); + }) + } + + #[test] + fn get_address_type_works_for_smart_contract() { + ExtBuilder::default().build().execute_with(|| { + let addr = H160::repeat_byte(0x1d); + + // length > 5 + pallet_evm::AccountCodes::::insert( + addr, + vec![0x60, 0x00, 0x60, 0x00, 0xfd, 0xff, 0xff], + ); + assert_eq!( + AddressType::Contract, + get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + ); + + // length < 5 + pallet_evm::AccountCodes::::insert(addr, vec![0x60, 0x00, 0x60]); + assert_eq!( + AddressType::Contract, + get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + ); + }) + } + + #[test] + fn get_address_type_works_for_unknown() { + ExtBuilder::default().build().execute_with(|| { + let addr = H160::repeat_byte(0x1d); + pallet_evm::AccountCodes::::insert(addr, vec![0x11, 0x00, 0x60, 0x00, 0xfd]); + assert_eq!( + AddressType::Unknown, + get_address_type::(&mut MockPrecompileHandle, addr).expect("OOG") + ); + }) + } +} diff --git a/precompiles/tests/solidity_test.sol b/precompiles/tests/solidity_test.sol new file mode 100644 index 0000000000..bd16b8487c --- /dev/null +++ b/precompiles/tests/solidity_test.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// @title Solidity test file with incorrectly defined selectors +interface SolidityTest { + /// A custom enum + enum CustomEnum0 { + A, + B, + C + } + + /// A custom type + struct CustomArg0 { + CustomEnum0 p0; + bytes[] p1; + } + + /// A custom type + struct CustomArg1 { + address[] p0; + uint256[] p1; + bytes[] p2; + } + + /// A composed custom type + struct CustomArg2 { + CustomArg0 p0; + CustomArg1[] p1; + } + + /// @dev Function without params and no selector + function fnNoArgs() external; + + /// @dev Function info + /// + /// @param arg0 Arg0 Description + /// @custom:selector c4921133 + function fnOneArg(address arg0) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 67ea837e + function fnTwoArgs(address arg0, uint256 arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector d6b423d9 + function fnSameArgs(uint64 arg0, uint64 arg1) external; + + /// @param arg0 Arg0 Description + /// @custom:selector b9904a86 + function fnOneArgSameLine(uint64 arg0) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 28f0c44e + function fnTwoArgsSameLine(uint64 arg0, bytes32 arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 06f0c1ce + function fnTwoArgsSameLineExternalSplit(uint64 arg0, bytes32 arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @param arg2 Arg2 Description + /// @custom:selector 18001a4e + function fnMemoryArrayArgs( + address[] memory arg0, + uint256[] memory arg1, + bytes[] memory arg2 + ) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 1ea61a4e + function fnCalldataArgs(string calldata arg0, bytes[] memory arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @param arg2 Arg2 Description + /// @custom:selector d8af1a4e + function fnCustomArgs( + CustomArg0 memory arg0, + bytes[] memory arg1, + uint64 arg2 + ) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector e8af1642 + function fnEnumArgs(CustomEnum0 arg0, uint64 arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @param arg2 Arg2 Description + /// @param arg3 Arg3 Description + /// @custom:selector 550c1a4e + function fnCustomArgsMultiple( + CustomArg0 memory arg0, + CustomArg1 memory arg1, + bytes[] memory arg2, + uint64 arg3 + ) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 77af1a40 + function fnCustomArrayArgs(CustomArg0[] memory arg0, bytes[] memory arg1) + external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 80af0a40 + function fnCustomComposedArg(CustomArg2 memory arg0, uint64 arg1) external; + + /// @param arg0 Arg0 Description + /// @param arg1 Arg1 Description + /// @custom:selector 97baa040 + function fnCustomComposedArrayArg(CustomArg2[] memory arg0, uint64 arg1) + external; +}