diff --git a/Cargo.lock b/Cargo.lock index d9dc87b26..3e8323de2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,7 +200,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -607,7 +607,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "synstructure 0.13.1", ] @@ -630,7 +630,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -688,7 +688,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -738,7 +738,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -825,7 +825,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1246,7 +1246,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1645,7 +1645,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1672,7 +1672,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1689,7 +1689,7 @@ checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1713,7 +1713,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1724,7 +1724,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1833,7 +1833,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1846,7 +1846,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1935,7 +1935,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -1959,7 +1959,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.87", + "syn 2.0.90", "termcolor", "toml 0.8.19", "walkdir", @@ -2121,7 +2121,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2141,7 +2141,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2334,7 +2334,7 @@ dependencies = [ "prettyplease 0.2.22", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2640,6 +2640,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2969,7 +2975,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2982,7 +2988,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -2994,7 +3000,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3005,7 +3011,7 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3015,7 +3021,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3186,7 +3192,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -3475,6 +3481,11 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hashlink" @@ -3698,9 +3709,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -3734,15 +3745,15 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.0", "pin-project-lite", "tokio", "tower-service", @@ -4095,9 +4106,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126b48a5acc3c52fbd5381a77898cb60e145123179588a29e7ac48f9c06e401b" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ "jsonrpsee-core", "jsonrpsee-proc-macros", @@ -4109,9 +4120,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e503369a76e195b65af35058add0e6900b794a4e9a9316900ddd3a87a80477" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", @@ -4132,28 +4143,28 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc660a9389e2748e794a40673a4155d501f32db667757cdb80edeff0306b489b" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "jsonrpsee-server" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6e6c9b6d975edcb443565d648b605f3e85a04ec63aa6941811a8894cc9cded" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -4172,9 +4183,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fb16314327cbc94fdf7965ef7e4422509cd5597f76d137bd104eb34aeede67" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" dependencies = [ "http 1.1.0", "serde", @@ -4353,7 +4364,7 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.2", "multistream-select", "once_cell", "parking_lot 0.12.3", @@ -4398,7 +4409,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "log", - "lru 0.12.4", + "lru 0.12.5", "quick-protobuf", "quick-protobuf-codec", "smallvec", @@ -4415,7 +4426,7 @@ dependencies = [ "bs58 0.5.1", "ed25519-dalek", "hkdf", - "multihash 0.19.1", + "multihash 0.19.2", "quick-protobuf", "rand", "sha2 0.10.8", @@ -4504,7 +4515,7 @@ dependencies = [ "libp2p-identity", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.2", "once_cell", "quick-protobuf", "rand", @@ -4609,7 +4620,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -4947,11 +4958,11 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -5000,7 +5011,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5014,7 +5025,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5025,7 +5036,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5036,7 +5047,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5250,7 +5261,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5283,7 +5294,7 @@ dependencies = [ "data-encoding", "libp2p-identity", "multibase", - "multihash 0.19.1", + "multihash 0.19.2", "percent-encoding", "serde", "static_assertions", @@ -5338,12 +5349,12 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +checksum = "cc41f430805af9d1cf4adae4ed2149c759b877b01d909a1f40256188d09345d2" dependencies = [ "core2", - "unsigned-varint 0.7.2", + "unsigned-varint 0.8.0", ] [[package]] @@ -5366,12 +5377,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" - [[package]] name = "multistream-select" version = "0.13.0" @@ -5410,7 +5415,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5869,7 +5874,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5943,9 +5948,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -5964,7 +5969,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -5975,18 +5980,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.2+3.3.2" +version = "300.4.0+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -6814,7 +6819,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -6855,7 +6860,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -6895,7 +6900,7 @@ dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common", + "polkavm-common 0.9.0", "polkavm-linux-raw", ] @@ -6917,13 +6922,28 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-common" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0dbafef4ab6ceecb4982ac3b550df430ef4f9fdbf07c108b7d4f91a0682fce" + [[package]] name = "polkavm-derive" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.9.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206caf322dfc02144510ad8360ff2051e5072f0874dcab3b410f78cdd52d0ebb" +dependencies = [ + "polkavm-derive-impl-macro 0.17.0", ] [[package]] @@ -6932,10 +6952,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c" dependencies = [ - "polkavm-common", + "polkavm-common 0.9.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42565aed4adbc4034612d0b17dea8db3681fb1bd1aed040d6edc5455a9f478a1" +dependencies = [ + "polkavm-common 0.17.0", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -6944,8 +6976,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ - "polkavm-derive-impl", - "syn 2.0.87", + "polkavm-derive-impl 0.9.0", + "syn 2.0.90", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d9838e95241b0bce4fe269cdd4af96464160505840ed5a8ac8536119ba19e2" +dependencies = [ + "polkavm-derive-impl 0.17.0", + "syn 2.0.90", ] [[package]] @@ -6958,7 +7000,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "object 0.32.2", - "polkavm-common", + "polkavm-common 0.9.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -7085,7 +7127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7165,7 +7207,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7176,14 +7218,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -7204,7 +7246,7 @@ dependencies = [ "quote", "regex", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7241,7 +7283,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7275,7 +7317,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "multimap 0.8.3", + "multimap", "petgraph", "prettyplease 0.1.25", "prost 0.11.9", @@ -7296,14 +7338,14 @@ dependencies = [ "heck 0.5.0", "itertools 0.12.1", "log", - "multimap 0.10.0", + "multimap", "once_cell", "petgraph", "prettyplease 0.2.22", "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.87", + "syn 2.0.90", "tempfile", ] @@ -7330,7 +7372,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -7657,7 +7699,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8127,7 +8169,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -8749,7 +8791,7 @@ dependencies = [ "litep2p", "log", "multiaddr 0.18.2", - "multihash 0.19.1", + "multihash 0.19.2", "rand", "thiserror", "zeroize", @@ -8861,7 +8903,7 @@ dependencies = [ "governor", "http 1.1.0", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "ip_network", "jsonrpsee", "log", @@ -9059,7 +9101,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9357,9 +9399,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -9394,13 +9436,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9461,7 +9503,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9721,7 +9763,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -9914,7 +9956,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -9985,7 +10027,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409)", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10004,23 +10046,23 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "environmental", "parity-scale-codec", @@ -10073,7 +10115,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "rustversion", "secp256k1", "sp-core", @@ -10198,12 +10240,12 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.17.1", "primitive-types 0.13.1", "sp-externalities 0.25.0", "sp-runtime-interface-proc-macro 17.0.0", @@ -10222,7 +10264,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.9.1", "primitive-types 0.12.2", "sp-externalities 0.29.0", "sp-runtime-interface-proc-macro 18.0.0", @@ -10236,14 +10278,14 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "Inflector", "expander", "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10256,7 +10298,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10338,12 +10380,12 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "impl-serde 0.5.0", "parity-scale-codec", @@ -10379,7 +10421,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "parity-scale-codec", "tracing", @@ -10469,13 +10511,13 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#c1238b64431b4c74d88c082099caed36177b5b11" +source = "git+https://github.com/paritytech/polkadot-sdk#44766de645c741fd03d159fca7f17cd6e99c33e3" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -10613,7 +10655,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10634,7 +10676,7 @@ dependencies = [ "sha2 0.10.8", "sqlx-core", "sqlx-sqlite", - "syn 2.0.87", + "syn 2.0.90", "tempfile", "tokio", "url", @@ -10801,7 +10843,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10858,7 +10900,7 @@ version = "0.17.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409#87971b3e92721bdf10bf40b410eaae779d494ca0" dependencies = [ "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "log", "prometheus", @@ -10905,7 +10947,7 @@ dependencies = [ "quote", "rayon", "subtensor-linting", - "syn 2.0.87", + "syn 2.0.90", "walkdir", ] @@ -10941,7 +10983,7 @@ dependencies = [ "proc-macro2", "procedural-fork", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10951,7 +10993,7 @@ dependencies = [ "ahash 0.8.11", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -10989,9 +11031,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -11018,7 +11060,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11109,7 +11151,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11256,7 +11298,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11416,7 +11458,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -11862,7 +11904,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -11896,7 +11938,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -12600,7 +12642,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -12660,7 +12702,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] @@ -12680,7 +12722,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.90", ] [[package]] diff --git a/pallets/drand/src/lib.rs b/pallets/drand/src/lib.rs index 435f41998..a7cc036b8 100644 --- a/pallets/drand/src/lib.rs +++ b/pallets/drand/src/lib.rs @@ -37,7 +37,6 @@ pub use pallet::*; extern crate alloc; -use crate::alloc::string::ToString; use alloc::{format, string::String, vec, vec::Vec}; use codec::Encode; @@ -53,7 +52,6 @@ use scale_info::prelude::cmp; use sha2::{Digest, Sha256}; use sp_core::blake2_256; use sp_runtime::{ - offchain::{http, Duration}, traits::{Hash, One}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, KeyTypeId, Saturating, @@ -79,7 +77,14 @@ pub mod weights; pub use weights::*; /// the main drand api endpoint -pub const API_ENDPOINT: &str = "https://drand.cloudflare.com"; +const ENDPOINTS: [&str; 5] = [ + "https://api.drand.sh", + "https://api2.drand.sh", + "https://api3.drand.sh", + "https://drand.cloudflare.com", + "https://api.drand.secureweb3.com:6875", +]; + /// the drand quicknet chain hash /// quicknet uses 'Tiny' BLS381, with small 48-byte sigs in G1 and 96-byte pubkeys in G2 pub const QUICKNET_CHAIN_HASH: &str = @@ -390,15 +395,8 @@ impl Pallet { } let mut last_stored_round = LastStoredRound::::get(); - let latest_pulse_body = Self::fetch_drand_latest().map_err(|_| "Failed to query drand")?; - let latest_unbounded_pulse: DrandResponseBody = serde_json::from_str(&latest_pulse_body) - .map_err(|_| { - log::warn!( - "Drand: Response that failed to deserialize: {}", - latest_pulse_body - ); - "Drand: Failed to serialize response body to pulse" - })?; + let latest_unbounded_pulse = + Self::fetch_drand_latest().map_err(|_| "Failed to query drand")?; let latest_pulse = latest_unbounded_pulse .try_into_pulse() .map_err(|_| "Drand: Received pulse contains invalid data")?; @@ -420,17 +418,8 @@ impl Pallet { for round in (last_stored_round.saturating_add(1)) ..=(last_stored_round.saturating_add(rounds_to_fetch)) { - let pulse_body = Self::fetch_drand_by_round(round) + let unbounded_pulse = Self::fetch_drand_by_round(round) .map_err(|_| "Drand: Failed to query drand for round")?; - let unbounded_pulse: DrandResponseBody = serde_json::from_str(&pulse_body) - .map_err(|_| { - log::warn!( - "Drand: Response that failed to deserialize for round {}: {}", - round, - pulse_body - ); - "Drand: Failed to serialize response body to pulse" - })?; let pulse = unbounded_pulse .try_into_pulse() .map_err(|_| "Drand: Received pulse contains invalid data")?; @@ -470,42 +459,107 @@ impl Pallet { Ok(()) } - /// Query the endpoint `{api}/{chainHash}/info` to receive information about the drand chain - /// Valid response bodies are deserialized into `BeaconInfoResponse` - fn fetch_drand_by_round(round: RoundNumber) -> Result { - let uri: &str = &format!("{}/{}/public/{}", API_ENDPOINT, CHAIN_HASH, round); - Self::fetch(uri) + fn fetch_drand_by_round(round: RoundNumber) -> Result { + let relative_path = format!("/{}/public/{}", CHAIN_HASH, round); + Self::fetch_and_decode_from_any_endpoint(&relative_path) } - fn fetch_drand_latest() -> Result { - let uri: &str = &format!("{}/{}/public/latest", API_ENDPOINT, CHAIN_HASH); - Self::fetch(uri) + + fn fetch_drand_latest() -> Result { + let relative_path = format!("/{}/public/latest", CHAIN_HASH); + Self::fetch_and_decode_from_any_endpoint(&relative_path) } - /// Fetch a remote URL and return the body of the response as a string. - fn fetch(uri: &str) -> Result { - let deadline = - sp_io::offchain::timestamp().add(Duration::from_millis(T::HttpFetchTimeout::get())); - let request = http::Request::get(uri); - let pending = request.deadline(deadline).send().map_err(|_| { - log::warn!("Drand: HTTP IO Error"); - http::Error::IoError - })?; - let response = pending.try_wait(deadline).map_err(|_| { - log::warn!("Drand: HTTP Deadline Reached"); - http::Error::DeadlineReached - })??; - - if response.code != 200 { - log::warn!("Drand: Unexpected status code: {}", response.code); - return Err(http::Error::Unknown); + /// Try to fetch from multiple endpoints simultaneously and return the first successfully decoded JSON response. + fn fetch_and_decode_from_any_endpoint( + relative_path: &str, + ) -> Result { + let uris: Vec = ENDPOINTS + .iter() + .map(|e| format!("{}{}", e, relative_path)) + .collect(); + let deadline = sp_io::offchain::timestamp().add( + sp_runtime::offchain::Duration::from_millis(T::HttpFetchTimeout::get()), + ); + + let mut pending_requests: Vec<(String, sp_runtime::offchain::http::PendingRequest)> = + vec![]; + + // Try sending requests to all endpoints. + for uri in &uris { + let request = sp_runtime::offchain::http::Request::get(uri); + match request.deadline(deadline).send() { + Ok(pending_req) => { + pending_requests.push((uri.clone(), pending_req)); + } + Err(_) => { + log::warn!("Drand: HTTP IO Error on endpoint {}", uri); + } + } + } + + if pending_requests.is_empty() { + log::warn!("Drand: No endpoints could be queried"); + return Err("Drand: No endpoints could be queried"); + } + + loop { + let now = sp_io::offchain::timestamp(); + if now > deadline { + // We've passed our deadline without getting a valid response. + log::warn!("Drand: HTTP Deadline Reached"); + break; + } + + let mut still_pending = false; + let mut next_iteration_requests = Vec::new(); + + for (uri, request) in pending_requests.drain(..) { + match request.try_wait(Some(deadline)) { + Ok(Ok(response)) => { + if response.code != 200 { + log::warn!( + "Drand: Unexpected status code: {} from {}", + response.code, + uri + ); + continue; + } + + let body = response.body().collect::>(); + match serde_json::from_slice::(&body) { + Ok(decoded) => { + return Ok(decoded); + } + Err(e) => { + log::warn!( + "Drand: JSON decode error from {}: {}. Response body: {}", + uri, + e, + String::from_utf8_lossy(&body) + ); + } + } + } + Ok(Err(e)) => { + log::warn!("Drand: HTTP error from {}: {:?}", uri, e); + } + Err(pending_req) => { + still_pending = true; + next_iteration_requests.push((uri, pending_req)); + } + } + } + + pending_requests = next_iteration_requests; + + if !still_pending { + break; + } } - let body = response.body().collect::>(); - let body_str = alloc::str::from_utf8(&body).map_err(|_| { - log::warn!("Drand: No UTF8 body"); - http::Error::Unknown - })?; - Ok(body_str.to_string()) + // If we reached here, no valid response was obtained from any endpoint. + log::warn!("Drand: No valid response from any endpoint"); + Err("Drand: No valid response from any endpoint") } /// get the randomness at a specific block height diff --git a/pallets/drand/src/tests.rs b/pallets/drand/src/tests.rs index a0f1c58fe..3fcf8a9b0 100644 --- a/pallets/drand/src/tests.rs +++ b/pallets/drand/src/tests.rs @@ -16,7 +16,7 @@ use crate::{ mock::*, BeaconConfig, BeaconConfigurationPayload, BeaconInfoResponse, Call, DrandResponseBody, - Error, Pulse, Pulses, PulsesPayload, RoundNumber, + Error, Pulse, Pulses, PulsesPayload, ENDPOINTS, QUICKNET_CHAIN_HASH, }; use codec::Encode; use frame_support::{ @@ -38,6 +38,7 @@ pub const ROUND_NUMBER: u64 = 1000; // Quicknet parameters pub const DRAND_PULSE: &str = "{\"round\":1000,\"randomness\":\"fe290beca10872ef2fb164d2aa4442de4566183ec51c56ff3cd603d930e54fdd\",\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}"; pub const DRAND_INFO_RESPONSE: &str = "{\"public_key\":\"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a\",\"period\":3,\"genesis_time\":1692803367,\"hash\":\"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971\",\"groupHash\":\"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e\",\"schemeID\":\"bls-unchained-g1-rfc9380\",\"metadata\":{\"beaconID\":\"quicknet\"}}"; +const INVALID_JSON: &str = r#"{"round":1000,"randomness":"not base64??","signature":}"#; #[test] fn it_can_submit_valid_pulse_when_beacon_config_exists() { @@ -342,53 +343,45 @@ fn test_not_validate_unsigned_write_pulse_with_no_payload_signature() { } #[test] -#[ignore] -fn test_validate_unsigned_write_pulse_by_non_authority() { - // TODO: https://github.com/ideal-lab5/pallet-drand/issues/3 - todo!( - "the transaction should be validated even if the signer of the payload is not an authority" - ); -} +fn can_execute_and_handle_valid_http_responses() { + use serde_json; -#[test] -#[ignore] -fn test_not_validate_unsigned_set_beacon_config_by_non_authority() { - // TODO: https://github.com/ideal-lab5/pallet-drand/issues/3 - todo!( - "the transaction should not be validated if the signer of the payload is not an authority" - ); -} + let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap(); -#[test] -fn can_execute_and_handle_valid_http_responses() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); { let mut state = state.write(); - state.expect_request(PendingRequest { - method: "GET".into(), - uri: "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/1".into(), - response: Some(DRAND_PULSE.as_bytes().to_vec()), - sent: true, - ..Default::default() - }); - state.expect_request(PendingRequest { - method: "GET".into(), - uri: "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/latest".into(), - response: Some(DRAND_PULSE.as_bytes().to_vec()), - sent: true, - ..Default::default() - }); + + for endpoint in ENDPOINTS.iter() { + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoint, QUICKNET_CHAIN_HASH), + response: Some(DRAND_PULSE.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + } + + for endpoint in ENDPOINTS.iter() { + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/latest", endpoint, QUICKNET_CHAIN_HASH), + response: Some(DRAND_PULSE.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + } } t.execute_with(|| { - let actual_specific = Drand::fetch_drand_by_round(RoundNumber::from(1u64)).unwrap(); - assert_eq!(actual_specific, DRAND_PULSE); + let actual_specific = Drand::fetch_drand_by_round(1000u64).unwrap(); + assert_eq!(actual_specific, expected_pulse); let actual_pulse = Drand::fetch_drand_latest().unwrap(); - assert_eq!(actual_pulse, DRAND_PULSE); + assert_eq!(actual_pulse, expected_pulse); }); } @@ -417,3 +410,142 @@ fn validate_unsigned_rejects_future_block_number() { assert_noop!(validity, InvalidTransaction::Future); }); } + +#[test] +fn test_all_endpoints_fail() { + let (offchain, state) = TestOffchainExt::new(); + let mut t = sp_io::TestExternalities::default(); + t.register_extension(OffchainWorkerExt::new(offchain)); + + { + let mut state = state.write(); + let endpoints = ENDPOINTS; + + for endpoint in endpoints.iter() { + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoint, QUICKNET_CHAIN_HASH), + response: Some(INVALID_JSON.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + } + } + + t.execute_with(|| { + let result = Drand::fetch_drand_by_round(1000u64); + assert!( + result.is_err(), + "All endpoints should fail due to invalid JSON responses" + ); + }); +} + +#[test] +fn test_eventual_success() { + let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap(); + + let (offchain, state) = TestOffchainExt::new(); + let mut t = sp_io::TestExternalities::default(); + t.register_extension(OffchainWorkerExt::new(offchain)); + + { + let mut state = state.write(); + let endpoints = ENDPOINTS; + + // We'll make all endpoints except the last return invalid JSON. + // Since no meta is provided, these are "200 OK" but invalid JSON, causing decode failures. + // The last endpoint returns the valid DRAND_PULSE JSON, leading to success. + + // Endpoint 0: Invalid JSON (decode fail) + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[0], QUICKNET_CHAIN_HASH), + response: Some(INVALID_JSON.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + + // Endpoint 1: Invalid JSON + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[1], QUICKNET_CHAIN_HASH), + response: Some(Vec::new()), + sent: true, + ..Default::default() + }); + + // Endpoint 2: Invalid JSON + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[2], QUICKNET_CHAIN_HASH), + response: Some(INVALID_JSON.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + + // Endpoint 3: Invalid JSON + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[3], QUICKNET_CHAIN_HASH), + response: Some(INVALID_JSON.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + + // Endpoint 4: Valid JSON (success) + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[4], QUICKNET_CHAIN_HASH), + response: Some(DRAND_PULSE.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + } + + t.execute_with(|| { + let actual = Drand::fetch_drand_by_round(1000u64).unwrap(); + assert_eq!( + actual, expected_pulse, + "Should succeed on the last endpoint after failing at the previous ones" + ); + }); +} + +#[test] +fn test_invalid_json_then_success() { + let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap(); + + let (offchain, state) = TestOffchainExt::new(); + let mut t = sp_io::TestExternalities::default(); + t.register_extension(OffchainWorkerExt::new(offchain)); + + { + let mut state = state.write(); + + let endpoints = ENDPOINTS; + + // Endpoint 1: Invalid JSON + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[0], QUICKNET_CHAIN_HASH), + response: Some(INVALID_JSON.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + + // Endpoint 2: Valid response + state.expect_request(PendingRequest { + method: "GET".into(), + uri: format!("{}/{}/public/1000", endpoints[1], QUICKNET_CHAIN_HASH), + response: Some(DRAND_PULSE.as_bytes().to_vec()), + sent: true, + ..Default::default() + }); + } + + t.execute_with(|| { + let actual = Drand::fetch_drand_by_round(1000u64).unwrap(); + assert_eq!(actual, expected_pulse); + }); +} diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 3db36441b..9d53151b4 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -917,10 +917,8 @@ impl Pallet { let coldkey = ensure_signed(origin)?; // --- 1. Rate limit for network registrations. - let current_block = Self::get_current_block_as_u64(); - let last_lock_block = Self::get_network_last_lock_block(); ensure!( - current_block.saturating_sub(last_lock_block) >= NetworkRateLimit::::get(), + Self::passes_rate_limit(&TransactionType::RegisterNetwork, &coldkey), Error::::NetworkTxRateLimitExceeded ); @@ -1339,6 +1337,9 @@ impl Pallet { pub fn get_network_last_lock_block() -> u64 { NetworkLastRegistered::::get() } + pub fn set_network_last_lock_block(block: u64) { + NetworkLastRegistered::::set(block); + } pub fn set_lock_reduction_interval(interval: u64) { NetworkLockReductionInterval::::set(interval); Self::deposit_event(Event::NetworkLockCostReductionIntervalSet(interval)); diff --git a/pallets/subtensor/src/staking/set_children.rs b/pallets/subtensor/src/staking/set_children.rs index ce9362b22..682475abe 100644 --- a/pallets/subtensor/src/staking/set_children.rs +++ b/pallets/subtensor/src/staking/set_children.rs @@ -61,7 +61,7 @@ impl Pallet { // Set last transaction block let current_block = Self::get_current_block_as_u64(); - Self::set_last_transaction_block( + Self::set_last_transaction_block_on_subnet( &hotkey, netuid, &TransactionType::SetChildren, @@ -325,7 +325,7 @@ impl Pallet { // Set last transaction block let current_block = Self::get_current_block_as_u64(); - Self::set_last_transaction_block( + Self::set_last_transaction_block_on_subnet( &hotkey, netuid, &TransactionType::SetChildkeyTake, @@ -336,7 +336,7 @@ impl Pallet { ChildkeyTake::::insert(hotkey.clone(), netuid, take); // Update the last transaction block - Self::set_last_transaction_block( + Self::set_last_transaction_block_on_subnet( &hotkey, netuid, &TransactionType::SetChildkeyTake, diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index dd2c12a80..10087d7b2 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -937,7 +937,7 @@ fn test_childkey_take_rate_limiting() { // Helper function to log rate limit information let log_rate_limit_info = || { let current_block = SubtensorModule::get_current_block_as_u64(); - let last_block = SubtensorModule::get_last_transaction_block( + let last_block = SubtensorModule::get_last_transaction_block_on_subnet( &hotkey, netuid, &TransactionType::SetChildkeyTake, @@ -947,7 +947,7 @@ fn test_childkey_take_rate_limiting() { &hotkey, netuid, ); - let limit = SubtensorModule::get_rate_limit(&TransactionType::SetChildkeyTake, 0); + let limit = SubtensorModule::get_rate_limit_on_subnet(&TransactionType::SetChildkeyTake, netuid); log::info!( "Rate limit info: current_block: {}, last_block: {}, limit: {}, passes: {}, diff: {}", current_block, @@ -3609,7 +3609,8 @@ fn test_set_children_rate_limit_fail_then_succeed() { // Try again after rate limit period has passed // Check rate limit - let limit = SubtensorModule::get_rate_limit(&TransactionType::SetChildren, netuid); + let limit = + SubtensorModule::get_rate_limit_on_subnet(&TransactionType::SetChildren, netuid); // Step that many blocks step_block(limit as u16); diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 59011bf59..3f6697f38 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -709,7 +709,7 @@ pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: u16, child_vec: #[allow(dead_code)] pub fn step_rate_limit(transaction_type: &TransactionType, netuid: u16) { // Check rate limit - let limit = SubtensorModule::get_rate_limit(transaction_type, netuid); + let limit = SubtensorModule::get_rate_limit_on_subnet(transaction_type, netuid); // Step that many blocks step_block(limit as u16); diff --git a/pallets/subtensor/src/tests/root.rs b/pallets/subtensor/src/tests/root.rs index 6e548aa22..b6f7fb300 100644 --- a/pallets/subtensor/src/tests/root.rs +++ b/pallets/subtensor/src/tests/root.rs @@ -2,8 +2,10 @@ use super::mock::*; use crate::Error; -use crate::{migrations, SubnetIdentity}; -use crate::{SubnetIdentities, SubnetIdentityOf}; +use crate::{ + migrations, utils::rate_limiting::TransactionType, NetworkRateLimit, SubnetIdentities, + SubnetIdentity, SubnetIdentityOf, +}; use frame_support::{assert_err, assert_ok}; use frame_system::Config; use frame_system::{EventRecord, Phase}; @@ -1050,3 +1052,45 @@ fn test_user_add_network_with_identity_fields_ok() { ); }); } + +#[test] +fn test_register_network_rate_limit() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + + // Set rate limit + let rate_limit = 1; + NetworkRateLimit::::put(rate_limit); + + // Give enough balance to register a network. + let balance = SubtensorModule::get_network_lock_cost() + 10_000; + SubtensorModule::add_balance_to_coldkey_account(&coldkey, balance); + + // Register network. + assert_ok!(SubtensorModule::register_network(RuntimeOrigin::signed( + coldkey + ))); + + // Give more TA + let mut lock_cost = SubtensorModule::get_network_lock_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, lock_cost + 10_000); + + // Try to register another network. + assert_err!( + SubtensorModule::register_network(RuntimeOrigin::signed(coldkey)), + Error::::NetworkTxRateLimitExceeded + ); + + // Step the rate limit. + step_rate_limit(&TransactionType::RegisterNetwork, 0); + + // Give more TAO + lock_cost = SubtensorModule::get_network_lock_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, lock_cost + 10_000); + + // Register network again. + assert_ok!(SubtensorModule::register_network(RuntimeOrigin::signed( + coldkey + ))); + }); +} diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index 3ab650645..8b30f9665 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -6,6 +6,7 @@ pub enum TransactionType { SetChildren, SetChildkeyTake, Unknown, + RegisterNetwork, } /// Implement conversion from TransactionType to u16 @@ -15,6 +16,7 @@ impl From for u16 { TransactionType::SetChildren => 0, TransactionType::SetChildkeyTake => 1, TransactionType::Unknown => 2, + TransactionType::RegisterNetwork => 3, } } } @@ -25,6 +27,7 @@ impl From for TransactionType { match value { 0 => TransactionType::SetChildren, 1 => TransactionType::SetChildkeyTake, + 3 => TransactionType::RegisterNetwork, _ => TransactionType::Unknown, } } @@ -34,14 +37,35 @@ impl Pallet { // ==== Rate Limiting ===== // ======================== /// Get the rate limit for a specific transaction type - pub fn get_rate_limit(tx_type: &TransactionType, _netuid: u16) -> u64 { + pub fn get_rate_limit(tx_type: &TransactionType) -> u64 { match tx_type { - TransactionType::SetChildren => 7200, // Cannot set children twice within a day + TransactionType::SetChildren => 150, // 30 minutes TransactionType::SetChildkeyTake => TxChildkeyTakeRateLimit::::get(), TransactionType::Unknown => 0, // Default to no limit for unknown types (no limit) + TransactionType::RegisterNetwork => NetworkRateLimit::::get(), } } + pub fn get_rate_limit_on_subnet(tx_type: &TransactionType, _netuid: u16) -> u64 { + #[allow(clippy::match_single_binding)] + match tx_type { + _ => Self::get_rate_limit(tx_type), + } + } + + pub fn check_passes_rate_limit(limit: u64, block: u64, last_block: u64) -> bool { + // Allow the first transaction (when last_block is 0) or if the rate limit has passed + last_block == 0 || block.saturating_sub(last_block) >= limit + } + + pub fn passes_rate_limit(tx_type: &TransactionType, key: &T::AccountId) -> bool { + let block: u64 = Self::get_current_block_as_u64(); + let limit: u64 = Self::get_rate_limit(tx_type); + let last_block: u64 = Self::get_last_transaction_block(key, tx_type); + + Self::check_passes_rate_limit(limit, block, last_block) + } + /// Check if a transaction should be rate limited on a specific subnet pub fn passes_rate_limit_on_subnet( tx_type: &TransactionType, @@ -49,32 +73,57 @@ impl Pallet { netuid: u16, ) -> bool { let block: u64 = Self::get_current_block_as_u64(); - let limit: u64 = Self::get_rate_limit(tx_type, netuid); - let last_block: u64 = Self::get_last_transaction_block(hotkey, netuid, tx_type); + let limit: u64 = Self::get_rate_limit_on_subnet(tx_type, netuid); + let last_block: u64 = Self::get_last_transaction_block_on_subnet(hotkey, netuid, tx_type); - // Allow the first transaction (when last_block is 0) or if the rate limit has passed - last_block == 0 || block.saturating_sub(last_block) >= limit + Self::check_passes_rate_limit(limit, block, last_block) + } + + /// Get the block number of the last transaction for a specific key, and transaction type + pub fn get_last_transaction_block(key: &T::AccountId, tx_type: &TransactionType) -> u64 { + match tx_type { + TransactionType::RegisterNetwork => Self::get_network_last_lock_block(), + _ => Self::get_last_transaction_block_on_subnet(key, 0, tx_type), + } } /// Get the block number of the last transaction for a specific hotkey, network, and transaction type - pub fn get_last_transaction_block( + pub fn get_last_transaction_block_on_subnet( hotkey: &T::AccountId, netuid: u16, tx_type: &TransactionType, ) -> u64 { - let tx_as_u16: u16 = (*tx_type).into(); - TransactionKeyLastBlock::::get((hotkey, netuid, tx_as_u16)) + match tx_type { + TransactionType::RegisterNetwork => Self::get_network_last_lock_block(), + _ => { + let tx_as_u16: u16 = (*tx_type).into(); + TransactionKeyLastBlock::::get((hotkey, netuid, tx_as_u16)) + } + } + } + + /// Set the block number of the last transaction for a specific key, and transaction type + pub fn set_last_transaction_block(key: &T::AccountId, tx_type: &TransactionType, block: u64) { + match tx_type { + TransactionType::RegisterNetwork => Self::set_network_last_lock_block(block), + _ => Self::set_last_transaction_block_on_subnet(key, 0, tx_type, block), + } } /// Set the block number of the last transaction for a specific hotkey, network, and transaction type - pub fn set_last_transaction_block( - hotkey: &T::AccountId, + pub fn set_last_transaction_block_on_subnet( + key: &T::AccountId, netuid: u16, tx_type: &TransactionType, block: u64, ) { - let tx_as_u16: u16 = (*tx_type).into(); - TransactionKeyLastBlock::::insert((hotkey, netuid, tx_as_u16), block); + match tx_type { + TransactionType::RegisterNetwork => Self::set_network_last_lock_block(block), + _ => { + let tx_as_u16: u16 = (*tx_type).into(); + TransactionKeyLastBlock::::insert((key, netuid, tx_as_u16), block); + } + } } pub fn set_last_tx_block(key: &T::AccountId, block: u64) { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2316cb5f4..24d36b179 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 215, + spec_version: 216, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/src/precompiles/metagraph.rs b/runtime/src/precompiles/metagraph.rs new file mode 100644 index 000000000..28bf664b8 --- /dev/null +++ b/runtime/src/precompiles/metagraph.rs @@ -0,0 +1,360 @@ +extern crate alloc; +use crate::precompiles::{get_method_id, get_slice}; +use crate::Runtime; +use fp_evm::{ + ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, +}; +use sp_core::{ByteArray, U256}; +use sp_std::vec; +pub const METAGRAPH_PRECOMPILE_INDEX: u64 = 2050; +pub struct MetagraphPrecompile; + +const NO_HOTKEY: &str = "no hotkey"; + +impl MetagraphPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + let method_id = get_slice(txdata, 0, 4)?; + let method_input = txdata + .get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + + match method_id { + id if id == get_method_id("getUidCount(uint16)") => Self::get_uid_count(&method_input), + id if id == get_method_id("getStake(uint16,uint16)") => Self::get_stake(&method_input), + id if id == get_method_id("getRank(uint16,uint16)") => Self::get_rank(&method_input), + id if id == get_method_id("getTrust(uint16,uint16)") => Self::get_trust(&method_input), + id if id == get_method_id("getConsensus(uint16,uint16)") => { + Self::get_consensus(&method_input) + } + id if id == get_method_id("getIncentive(uint16,uint16)") => { + Self::get_incentive(&method_input) + } + id if id == get_method_id("getDividends(uint16,uint16)") => { + Self::get_dividends(&method_input) + } + id if id == get_method_id("getEmission(uint16,uint16)") => { + Self::get_emission(&method_input) + } + id if id == get_method_id("getVtrust(uint16,uint16)") => { + Self::get_vtrust(&method_input) + } + id if id == get_method_id("getValidatorStatus(uint16,uint16)") => { + Self::get_validator_status(&method_input) + } + id if id == get_method_id("getLastUpdate(uint16,uint16)") => { + Self::get_last_update(&method_input) + } + id if id == get_method_id("getIsActive(uint16,uint16)") => { + Self::get_is_active(&method_input) + } + id if id == get_method_id("getAxon(uint16,uint16)") => Self::get_axon(&method_input), + id if id == get_method_id("getHotkey(uint16,uint16)") => { + Self::get_hotkey(&method_input) + } + id if id == get_method_id("getColdkey(uint16,uint16)") => { + Self::get_coldkey(&method_input) + } + + _ => Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }), + } + } + + fn get_uid_count(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid_count = pallet_subtensor::SubnetworkN::::get(netuid); + + let uid_count_u256 = U256::from(uid_count); + let mut result = [0_u8; 32]; + U256::to_big_endian(&uid_count_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_stake(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid, uid) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + })?; + + let stake = pallet_subtensor::TotalHotkeyStake::::get(&hotkey); + let result_u256 = U256::from(stake); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_rank(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + let rank = pallet_subtensor::Pallet::::get_rank_for_uid(netuid, uid); + + let result_u256 = U256::from(rank); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_trust(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let trust = pallet_subtensor::Pallet::::get_trust_for_uid(netuid, uid); + + let result_u256 = U256::from(trust); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_consensus(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let consensus = pallet_subtensor::Pallet::::get_consensus_for_uid(netuid, uid); + + let result_u256 = U256::from(consensus); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_incentive(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let incentive = pallet_subtensor::Pallet::::get_incentive_for_uid(netuid, uid); + + let result_u256 = U256::from(incentive); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_dividends(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let dividends = pallet_subtensor::Pallet::::get_dividends_for_uid(netuid, uid); + + let result_u256 = U256::from(dividends); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_emission(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let emission = pallet_subtensor::Pallet::::get_emission_for_uid(netuid, uid); + + let result_u256 = U256::from(emission); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_vtrust(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let vtrust = pallet_subtensor::Pallet::::get_validator_trust_for_uid(netuid, uid); + + let result_u256 = U256::from(vtrust); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_validator_status(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let validator_permit = + pallet_subtensor::Pallet::::get_validator_permit_for_uid(netuid, uid); + + let result_u256 = if validator_permit { + U256::from(1) + } else { + U256::from(0) + }; + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_last_update(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let last_update = pallet_subtensor::Pallet::::get_last_update_for_uid(netuid, uid); + + let result_u256 = U256::from(last_update); + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_is_active(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let active = pallet_subtensor::Pallet::::get_active_for_uid(netuid, uid); + + let result_u256 = if active { U256::from(1) } else { U256::from(0) }; + let mut result = [0_u8; 32]; + U256::to_big_endian(&result_u256, &mut result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_axon(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid, uid) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other(sp_version::Cow::Borrowed(NO_HOTKEY)), + })?; + + let axon = pallet_subtensor::Pallet::::get_axon_info(netuid, &hotkey); + + let mut block_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.block), &mut block_result); + + let mut version_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.version), &mut version_result); + + let mut ip_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.ip), &mut ip_result); + + let mut port_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.port), &mut port_result); + + let mut ip_type_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.ip_type), &mut ip_type_result); + + let mut protocol_result = [0_u8; 32]; + U256::to_big_endian(&U256::from(axon.protocol), &mut protocol_result); + + let mut result = [0_u8; 192]; + result[..32].copy_from_slice(&block_result); + result[32..64].copy_from_slice(&version_result); + result[64..96].copy_from_slice(&ip_result); + result[96..128].copy_from_slice(&port_result); + result[128..160].copy_from_slice(&ip_type_result); + result[160..].copy_from_slice(&protocol_result); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: result.into(), + }) + } + + fn get_hotkey(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid, uid) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + })?; + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: hotkey.as_slice().into(), + }) + } + + fn get_coldkey(data: &[u8]) -> PrecompileResult { + let netuid = Self::parse_netuid(data)?; + let uid = Self::parse_uid(get_slice(data, 32, 64)?)?; + + let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid, uid) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + })?; + + let coldkey = pallet_subtensor::Owner::::get(&hotkey); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: coldkey.as_slice().into(), + }) + } + + fn parse_netuid(data: &[u8]) -> Result { + if data.len() < 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut netuid = [0u8; 2]; + netuid.copy_from_slice(get_slice(data, 30, 32)?); + let result = u16::from_be_bytes(netuid); + Ok(result) + } + + fn parse_uid(data: &[u8]) -> Result { + if data.len() < 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut uid = [0u8; 2]; + uid.copy_from_slice(get_slice(data, 30, 32)?); + let result = u16::from_be_bytes(uid); + Ok(result) + } +} diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 22f2a4881..e13516e95 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -13,10 +13,12 @@ use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripe // Include custom precompiles mod balance_transfer; mod ed25519; +mod metagraph; mod staking; use balance_transfer::*; use ed25519::*; +use metagraph::*; use staking::*; pub struct FrontierPrecompiles(PhantomData); @@ -37,7 +39,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 10] { + pub fn used_addresses() -> [H160; 11] { [ hash(1), hash(2), @@ -49,6 +51,7 @@ where hash(EDVERIFY_PRECOMPILE_INDEX), hash(BALANCE_TRANSFER_INDEX), hash(STAKING_PRECOMPILE_INDEX), + hash(METAGRAPH_PRECOMPILE_INDEX), ] } } @@ -73,6 +76,10 @@ where Some(BalanceTransferPrecompile::execute(handle)) } a if a == hash(STAKING_PRECOMPILE_INDEX) => Some(StakingPrecompile::execute(handle)), + a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => { + Some(MetagraphPrecompile::execute(handle)) + } + _ => None, } } diff --git a/runtime/src/precompiles/solidity/metagraph.abi b/runtime/src/precompiles/solidity/metagraph.abi new file mode 100644 index 000000000..eb4b9be6a --- /dev/null +++ b/runtime/src/precompiles/solidity/metagraph.abi @@ -0,0 +1,389 @@ +[ + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getAxon", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "block", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "version", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "ip", + "type": "uint128" + }, + { + "internalType": "uint16", + "name": "port", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "ip_type", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "protocol", + "type": "uint8" + } + ], + "internalType": "struct AxonInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getColdkey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getConsensus", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getDividends", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getEmission", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getHotkey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getIncentive", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getIsActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getLastUpdate", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getRank", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getStake", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getTrust", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getUidCount", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getValidatorStatus", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getVtrust", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/metagraph.sol b/runtime/src/precompiles/solidity/metagraph.sol new file mode 100644 index 000000000..3a19281a5 --- /dev/null +++ b/runtime/src/precompiles/solidity/metagraph.sol @@ -0,0 +1,134 @@ +pragma solidity ^0.8.0; + +address constant IMetagraph_ADDRESS = 0x0000000000000000000000000000000000000802; + +struct AxonInfo { + uint64 block; + uint32 version; + uint128 ip; + uint16 port; + uint8 ip_type; + uint8 protocol; +} + +interface IMetagraph { + + /** + * @dev Returns the count of unique identifiers (UIDs) associated with a given network identifier (netuid). + * @param netuid The network identifier for which to retrieve the UID count. + * @return The count of UIDs associated with the specified netuid. + */ + function getUidCount(uint16 netuid) external view returns (uint16); + + /** + * @dev Retrieves the stake amount associated with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the stake. + * @param uid The unique identifier for which to retrieve the stake. + * @return The stake amount associated with the specified netuid and uid. + */ + function getStake(uint16 netuid, uint16 uid) external view returns (uint64); + + /** + * @dev Retrieves the rank of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the rank. + * @param uid The unique identifier for which to retrieve the rank. + * @return The rank of the node with the specified netuid and uid. + */ + function getRank(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Retrieves the trust value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the trust value. + * @param uid The unique identifier for which to retrieve the trust value. + * @return The trust value of the node with the specified netuid and uid. + */ + function getTrust(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Retrieves the consensus value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the consensus value. + * @param uid The unique identifier for which to retrieve the consensus value. + * @return The consensus value of the node with the specified netuid and uid. + */ + function getConsensus(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Retrieves the incentive value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the incentive value. + * @param uid The unique identifier for which to retrieve the incentive value. + * @return The incentive value of the node with the specified netuid and uid. + */ + function getIncentive(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Retrieves the dividend value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the dividend value. + * @param uid The unique identifier for which to retrieve the dividend value. + * @return The dividend value of the node with the specified netuid and uid. + */ + function getDividends(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Retrieves the emission value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the emission value. + * @param uid The unique identifier for which to retrieve the emission value. + * @return The emission value of the node with the specified netuid and uid. + */ + function getEmission(uint16 netuid, uint16 uid) external view returns (uint64); + + /** + * @dev Retrieves the v-trust value of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the v-trust value. + * @param uid The unique identifier for which to retrieve the v-trust value. + * @return The v-trust value of the node with the specified netuid and uid. + */ + function getVtrust(uint16 netuid, uint16 uid) external view returns (uint16); + + /** + * @dev Checks the validator status of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to check the validator status. + * @param uid The unique identifier for which to check the validator status. + * @return Returns true if the node is a validator, false otherwise. + */ + function getValidatorStatus(uint16 netuid, uint16 uid) external view returns (bool); + + /** + * @dev Retrieves the last update timestamp of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the last update timestamp. + * @param uid The unique identifier for which to retrieve the last update timestamp. + * @return The last update timestamp of the node with the specified netuid and uid. + */ + function getLastUpdate(uint16 netuid, uint16 uid) external view returns (uint64); + + /** + * @dev Checks if a node with a given network identifier (netuid) and unique identifier (uid) is active. + * @param netuid The network identifier for which to check the node's activity. + * @param uid The unique identifier for which to check the node's activity. + * @return Returns true if the node is active, false otherwise. + */ + function getIsActive(uint16 netuid, uint16 uid) external view returns (bool); + + /** + * @dev Retrieves the axon information of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the axon information. + * @param uid The unique identifier for which to retrieve the axon information. + * @return The axon information of the node with the specified netuid and uid. + */ + function getAxon(uint16 netuid, uint16 uid) external view returns (AxonInfo memory); + + /** + * @dev Retrieves the hotkey of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the hotkey. + * @param uid The unique identifier for which to retrieve the hotkey. + * @return The hotkey of the node with the specified netuid and uid. + */ + function getHotkey(uint16 netuid, uint16 uid) external view returns (bytes32); + + /** + * @dev Retrieves the coldkey of a node with a given network identifier (netuid) and unique identifier (uid). + * @param netuid The network identifier for which to retrieve the coldkey. + * @param uid The unique identifier for which to retrieve the coldkey. + * @return The coldkey of the node with the specified netuid and uid. + */ + function getColdkey(uint16 netuid, uint16 uid) external view returns (bytes32); +}