diff --git a/Cargo.lock b/Cargo.lock index 4252f93a..2eedaf53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,11 +13,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "gimli 0.28.1", + "gimli 0.29.0", ] [[package]] @@ -26,6 +26,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "adler32" version = "1.2.0" @@ -38,7 +44,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -81,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -105,9 +111,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ascii-canvas" @@ -146,7 +152,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -228,7 +234,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -239,7 +245,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -261,14 +267,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -317,16 +323,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ - "addr2line 0.21.0", + "addr2line 0.22.0", "cc", "cfg-if", "libc", - "miniz_oxide", - "object 0.32.2", + "miniz_oxide 0.7.4", + "object 0.36.3", "rustc-demangle", ] @@ -362,9 +368,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -448,9 +454,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -491,9 +497,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "borsh" -version = "1.4.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" dependencies = [ "borsh-derive", "cfg_aliases", @@ -501,15 +507,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.4.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "syn_derive", ] @@ -550,7 +556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db629469a6955021b15eb99cf5983ae8dcf9d0432ba2a8295715efee232da912" dependencies = [ "chrono", - "derive_more 0.99.17", + "derive_more 0.99.18", "semver", "serde", ] @@ -636,9 +642,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] @@ -657,9 +663,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -704,7 +710,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -731,9 +737,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -743,9 +752,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" @@ -759,7 +768,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -876,9 +885,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -910,9 +919,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpp_demangle" @@ -925,9 +934,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -1042,18 +1051,18 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -1079,9 +1088,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1183,9 +1192,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "debugid" @@ -1242,15 +1251,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.76", ] [[package]] @@ -1341,13 +1350,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -1412,9 +1421,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -1438,9 +1447,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -1455,7 +1464,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -1475,9 +1484,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1485,9 +1494,9 @@ dependencies = [ [[package]] name = "escargot" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f474c6844cbd04e783d0f25757583db4f491770ca618bedf2fb01815fc79939" +checksum = "c000f23e9d459aef148b7267e02b03b94a0aaacf4ec64c65612f67e02f525fb6" dependencies = [ "log", "once_cell", @@ -1549,7 +1558,7 @@ version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bytes", "chrono", "const-hex", @@ -1557,13 +1566,13 @@ dependencies = [ "ethabi", "generic-array", "k256", - "num_enum 0.7.2", + "num_enum 0.7.3", "open-fastrlp", "rand", "rlp", "serde", "serde_json", - "strum 0.26.2", + "strum 0.26.3", "tempfile", "thiserror", "tiny-keccak", @@ -1588,10 +1597,10 @@ dependencies = [ "candid", "ethers-core", "evm_rpc_types", - "getrandom 0.2.14", + "getrandom 0.2.15", "hex", "ic-base-types 0.8.0", - "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814)", + "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41)", "ic-canisters-http-types 0.8.0", "ic-cdk", "ic-cdk-macros", @@ -1611,6 +1620,7 @@ dependencies = [ "serde", "serde_json", "url", + "zeroize", ] [[package]] @@ -1632,9 +1642,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fe-derive" @@ -1689,12 +1699,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -1795,7 +1805,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -1843,7 +1853,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "debugid", "fxhash", "serde", @@ -1874,9 +1884,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1896,9 +1906,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -1940,7 +1950,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.6", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", @@ -1983,9 +1993,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hdrhistogram" @@ -2012,6 +2022,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2027,6 +2043,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2090,9 +2112,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2117,9 +2139,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2225,7 +2247,7 @@ source = "git+https://github.com/dfinity/ic?rev=release-2023-09-27_23-01#ca5e505 dependencies = [ "async-stream", "byte-unit", - "derive_more 0.99.17", + "derive_more 0.99.18", "futures", "futures-util", "hyper", @@ -2260,7 +2282,7 @@ dependencies = [ [[package]] name = "ic-base-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "base32", "byte-unit", @@ -2272,7 +2294,7 @@ dependencies = [ "ic-protobuf 0.9.0", "ic-stable-structures", "phantom_newtype 0.9.0", - "prost 0.12.4", + "prost 0.12.6", "serde", "strum 0.25.0", "strum_macros 0.25.3", @@ -2303,7 +2325,7 @@ dependencies = [ [[package]] name = "ic-btc-types-internal" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "candid", "ic-btc-interface", @@ -2316,7 +2338,7 @@ dependencies = [ [[package]] name = "ic-canister-log" version = "0.2.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "serde", ] @@ -2420,7 +2442,7 @@ dependencies = [ [[package]] name = "ic-canisters-http-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "candid", "serde", @@ -2552,7 +2574,7 @@ dependencies = [ [[package]] name = "ic-cketh-minter" version = "0.1.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "askama", "async-trait", @@ -2561,7 +2583,7 @@ dependencies = [ "futures", "hex", "hex-literal 0.4.1", - "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814)", + "ic-canister-log 0.2.0 (git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41)", "ic-canisters-http-types 0.9.0", "ic-cdk", "ic-cdk-macros", @@ -2660,7 +2682,7 @@ dependencies = [ "ic-registry-keys", "ic-registry-proto-data-provider", "ic-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "serde", "slog", "strum 0.23.0", @@ -2687,7 +2709,7 @@ dependencies = [ [[package]] name = "ic-crypto-ecdsa-secp256k1" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "k256", "lazy_static", @@ -2727,7 +2749,7 @@ name = "ic-crypto-getrandom-for-wasm" version = "0.1.0" source = "git+https://github.com/dfinity/ic?rev=release-2023-09-27_23-01#ca5e5052886de781021506814d2c6502e375da48" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -2919,7 +2941,7 @@ dependencies = [ "ic-protobuf 0.8.0", "ic-types", "ic-utils 0.8.0", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prost 0.11.9", "rand", "rand_chacha", @@ -3006,7 +3028,7 @@ dependencies = [ [[package]] name = "ic-crypto-internal-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "sha2 0.10.8", ] @@ -3038,7 +3060,7 @@ dependencies = [ "ic-crypto-sha2 0.8.0", "ic-types", "lazy_static", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand", "rand_chacha", "serde", @@ -3172,7 +3194,7 @@ dependencies = [ [[package]] name = "ic-crypto-sha2" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "ic-crypto-internal-sha2 0.9.0", ] @@ -3180,7 +3202,7 @@ dependencies = [ [[package]] name = "ic-crypto-sha3" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "sha3 0.9.1", ] @@ -3394,7 +3416,7 @@ dependencies = [ [[package]] name = "ic-error-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "ic-utils 0.9.0", "serde", @@ -3481,7 +3503,7 @@ dependencies = [ [[package]] name = "ic-ic00-types" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "candid", "ic-base-types 0.9.0", @@ -3771,13 +3793,13 @@ dependencies = [ [[package]] name = "ic-protobuf" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "bincode", "candid", "erased-serde", "maplit", - "prost 0.12.4", + "prost 0.12.6", "serde", "serde_json", "slog", @@ -4062,7 +4084,7 @@ dependencies = [ "ic-types", "ic-utils 0.8.0", "nix 0.23.2", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "prost 0.11.9", "rand", @@ -4093,7 +4115,7 @@ dependencies = [ [[package]] name = "ic-sys" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "hex", "ic-crypto-sha2 0.9.0", @@ -4235,14 +4257,14 @@ dependencies = [ [[package]] name = "ic-utils" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "cvt", "hex", "ic-sys 0.9.0", "libc", "nix 0.24.3", - "prost 0.12.4", + "prost 0.12.6", "rand", "scoped_threadpool", "serde", @@ -4252,7 +4274,7 @@ dependencies = [ [[package]] name = "ic-utils-ensure" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" [[package]] name = "ic-utils-lru-cache" @@ -4330,7 +4352,7 @@ dependencies = [ [[package]] name = "icrc-ledger-client" version = "0.1.2" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "async-trait", "candid", @@ -4341,7 +4363,7 @@ dependencies = [ [[package]] name = "icrc-ledger-client-cdk" version = "0.1.2" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "async-trait", "candid", @@ -4367,7 +4389,7 @@ dependencies = [ [[package]] name = "icrc-ledger-types" version = "0.1.4" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "base32", "candid", @@ -4447,19 +4469,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -4477,11 +4499,11 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] @@ -4521,9 +4543,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -4575,7 +4597,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", @@ -4594,11 +4616,11 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin 0.9.8", ] [[package]] @@ -4609,9 +4631,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libflate" @@ -4645,7 +4667,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -4657,15 +4679,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -4673,9 +4695,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "logos" @@ -4697,7 +4719,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -4749,9 +4771,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memfd" @@ -4759,7 +4781,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.34", + "rustix 0.38.35", ] [[package]] @@ -4805,9 +4827,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -4841,22 +4863,32 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5016,7 +5048,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -5093,11 +5125,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "num_enum_derive 0.7.2", + "num_enum_derive 0.7.3", ] [[package]] @@ -5109,19 +5141,19 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -5138,9 +5170,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -5177,7 +5209,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "auto_impl", "bytes", "ethereum-types", @@ -5244,11 +5276,11 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "bitvec", "byte-slice-cast", "impl-trait-for-tuples", @@ -5258,11 +5290,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -5281,12 +5313,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -5305,22 +5337,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" @@ -5337,7 +5369,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -5358,9 +5390,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -5369,9 +5401,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -5379,22 +5411,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -5403,12 +5435,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.4.0", ] [[package]] @@ -5424,7 +5456,7 @@ dependencies = [ [[package]] name = "phantom_newtype" version = "0.9.0" -source = "git+https://github.com/dfinity/ic?rev=5991b07d0e396b7885d5b4d4c917f7290dd70814#5991b07d0e396b7885d5b4d4c917f7290dd70814" +source = "git+https://github.com/dfinity/ic?rev=1f551bea63354370b6e7a5037e96d464bdab3b41#1f551bea63354370b6e7a5037e96d464bdab3b41" dependencies = [ "candid", "serde", @@ -5463,7 +5495,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -5513,9 +5545,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -5539,15 +5574,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -5629,20 +5664,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit 0.22.20", ] [[package]] @@ -5677,9 +5703,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -5723,13 +5749,13 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec 0.6.3", - "bitflags 2.5.0", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -5747,12 +5773,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.4", + "prost-derive 0.12.6", ] [[package]] @@ -5792,15 +5818,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -5855,9 +5881,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -5904,7 +5930,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -5972,20 +5998,20 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -6005,25 +6031,25 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -6034,9 +6060,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rend" @@ -6080,7 +6106,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -6089,9 +6115,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec", "bytecheck", @@ -6107,9 +6133,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", @@ -6167,11 +6193,11 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", "borsh", "bytes", "num-traits", @@ -6183,9 +6209,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -6201,9 +6227,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -6233,22 +6259,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", @@ -6268,9 +6294,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -6286,9 +6312,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -6301,23 +6327,23 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", - "derive_more 0.99.17", + "derive_more 0.99.18", "parity-scale-codec", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 1.0.109", @@ -6367,9 +6393,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -6410,7 +6436,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -6513,6 +6539,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -6646,9 +6678,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -6690,15 +6722,15 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -6715,7 +6747,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "phf_shared", "precomputed-hash", ] @@ -6749,11 +6781,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.2", + "strum_macros 0.26.4", ] [[package]] @@ -6792,27 +6824,27 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "stubborn-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc21d6dc10a89b8004044f2eba7e2239738fbad57b19d5295e2c446f092292c" +checksum = "373a722240991e091384a571e1fd8abde15eca4494a1a2bff95dbf603d15a866" dependencies = [ "log", "rand", @@ -6821,9 +6853,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "subtle-ng" @@ -6844,9 +6876,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -6862,7 +6894,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -6897,9 +6929,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tarpc" @@ -6938,14 +6970,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "rustix 0.38.34", - "windows-sys 0.52.0", + "once_cell", + "rustix 0.38.35", + "windows-sys 0.59.0", ] [[package]] @@ -6982,22 +7015,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -7067,9 +7100,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -7082,21 +7115,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7111,13 +7143,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -7171,9 +7203,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -7181,14 +7213,13 @@ dependencies = [ "pin-project-lite", "slab", "tokio", - "tracing", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" @@ -7196,31 +7227,20 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.4.0", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.4.0", "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -7287,15 +7307,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -7317,7 +7337,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -7444,15 +7464,15 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "untrusted" @@ -7486,11 +7506,11 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "serde", ] @@ -7502,9 +7522,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" @@ -7548,31 +7568,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" -source = "git+https://github.com/dfinity/wasm-bindgen?rev=9cde9c9a88c1fad13a8663277650e01b5671843e#9cde9c9a88c1fad13a8663277650e01b5671843e" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" -source = "git+https://github.com/dfinity/wasm-bindgen?rev=9cde9c9a88c1fad13a8663277650e01b5671843e#9cde9c9a88c1fad13a8663277650e01b5671843e" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" -source = "git+https://github.com/dfinity/wasm-bindgen?rev=9cde9c9a88c1fad13a8663277650e01b5671843e#9cde9c9a88c1fad13a8663277650e01b5671843e" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7580,20 +7604,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "git+https://github.com/dfinity/wasm-bindgen?rev=9cde9c9a88c1fad13a8663277650e01b5671843e#9cde9c9a88c1fad13a8663277650e01b5671843e" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" -source = "git+https://github.com/dfinity/wasm-bindgen?rev=9cde9c9a88c1fad13a8663277650e01b5671843e#9cde9c9a88c1fad13a8663277650e01b5671843e" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-encoder" @@ -7606,9 +7632,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.205.0" +version = "0.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e95b3563d164f33c1cfb0a7efbd5940c37710019be10cd09f800fdec8b0e5c" +checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" dependencies = [ "leb128", ] @@ -7629,7 +7655,7 @@ version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf9564f29de2890ee34406af52d2a92dec6ef044c8ddfc5add5db8dcfd36e6c" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.4.0", "semver", ] @@ -7813,31 +7839,31 @@ dependencies = [ [[package]] name = "wast" -version = "205.0.0" +version = "216.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a6a195b3b5245e26d450bbcc91366c6b652382a22f63cbe3c73240e13b2bb" +checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.205.0", + "wasm-encoder 0.216.0", ] [[package]] name = "wat" -version = "1.205.0" +version = "1.216.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19832624d606e7c6bf3cd4caa73578ecec5eac30c768269256d19c79900beb18" +checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -7852,7 +7878,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.34", + "rustix 0.38.35", ] [[package]] @@ -7873,11 +7899,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -7892,7 +7918,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -7910,7 +7936,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -7930,18 +7965,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -7952,9 +7987,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -7964,9 +7999,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -7976,15 +8011,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -7994,9 +8029,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -8006,9 +8041,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -8018,9 +8053,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -8030,9 +8065,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -8043,6 +8078,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "wsl" version = "0.1.0" @@ -8120,29 +8164,30 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -8155,5 +8200,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] diff --git a/Cargo.toml b/Cargo.toml index 9b93a3c4..9d88c4f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,11 +24,11 @@ ic-canisters-http-types = { workspace = true } ic-nervous-system-common = { workspace = true } ic-metrics-encoder = { workspace = true } ic-stable-structures = { workspace = true } -ic-certified-map = { workspace = true } +ic-canister-log = { git = "https://github.com/dfinity/ic", rev = "1f551bea63354370b6e7a5037e96d464bdab3b41", package = "ic-canister-log" } ic-cdk = { workspace = true } ic-cdk-macros = { workspace = true } -ic-canister-log = { git = "https://github.com/dfinity/ic", rev = "5991b07d0e396b7885d5b4d4c917f7290dd70814", package = "ic-canister-log" } -cketh-common = { git = "https://github.com/dfinity/ic", rev = "5991b07d0e396b7885d5b4d4c917f7290dd70814", package = "ic-cketh-minter" } +ic-certified-map = { workspace = true } +cketh-common = { git = "https://github.com/dfinity/ic", rev = "1f551bea63354370b6e7a5037e96d464bdab3b41", package = "ic-cketh-minter" } maplit = "1.0" num = "0.4" num-traits = "0.2" @@ -39,6 +39,7 @@ url = "2.5" async-trait = "0.1" hex = "0.4" ethers-core = "2.0" +zeroize = "1.8" [dev-dependencies] ic-ic00-types = { git = "https://github.com/dfinity/ic", rev = "release-2023-09-27_23-01" } @@ -53,21 +54,17 @@ candid = { version = "0.9" } getrandom = { version = "0.2", features = ["custom"] } hex = "0.4.3" ic-canisters-http-types = { git = "https://github.com/dfinity/ic", rev = "release-2023-09-27_23-01" } +ic-cdk = "0.10" +ic-cdk-bindgen = "0.1" +ic-cdk-macros = "0.7" +ic-certified-map = "0.4" ic-nervous-system-common = { git = "https://github.com/dfinity/ic", rev = "release-2023-09-27_23-01" } ic-metrics-encoder = "1.1" ic-stable-structures = "0.5" -ic-certified-map = "0.4" -ic-cdk = "0.10" -ic-cdk-macros = "0.7" -ic-cdk-bindgen = "0.1" num-bigint = "0.4.6" proptest = "1.5.0" serde = "1.0" serde_json = "1.0" - -[patch.crates-io] -wasm-bindgen = { git = "https://github.com/dfinity/wasm-bindgen", rev = "9cde9c9a88c1fad13a8663277650e01b5671843e" } - [workspace] members = ["e2e/rust", "evm_rpc_types"] diff --git a/README.md b/README.md index 5b6453c9..f9fd48b4 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,11 @@ npm install # Deploy to the local replica dfx start --background npm run generate -dfx deploy evm_rpc --argument "(record {nodesInSubnet = 28})" +dfx deploy evm_rpc + +# Alternatively, deploy and run test suite +dfx start --background +scripts/e2e ``` Regenerate language bindings with the `generate` [npm script](https://docs.npmjs.com/cli/v10/using-npm/scripts): diff --git a/candid/evm_rpc.did b/candid/evm_rpc.did index 8ea31cc1..7c2c26d0 100644 --- a/candid/evm_rpc.did +++ b/candid/evm_rpc.did @@ -1,4 +1,3 @@ -type Auth = variant { FreeRpc; PriorityRpc; RegisterProvider; Manage }; type Block = record { miner : text; totalDifficulty : opt nat; @@ -43,6 +42,7 @@ type EthSepoliaService = variant { Ankr; BlockPi; PublicNode; + Sepolia; }; type L2MainnetService = variant { Alchemy; @@ -79,7 +79,8 @@ type HttpOutcallError = variant { }; }; type InitArgs = record { - nodesInSubnet : nat32; + demo : opt bool; + manageApiKeys : opt vec principal; }; type JsonRpcError = record { code : int64; message : text }; type LogEntry = record { @@ -93,12 +94,6 @@ type LogEntry = record { logIndex : opt nat; removed : bool; }; -type ManageProviderArgs = record { - providerId : nat64; - chainId: opt nat64; - "service" : opt RpcService; - primary : opt bool; -}; type Metrics = record { requests : vec record { record { text; text }; nat64 }; responses : vec record { record { text; text; text }; nat64 }; @@ -140,22 +135,25 @@ type ProviderError = variant { NoPermission; }; type ProviderId = nat64; -type ProviderView = record { - cyclesPerCall : nat64; - owner : principal; - hostname : text; - primary : bool; - chainId : nat64; - cyclesPerMessageByte : nat64; - providerId : nat64; -}; -type RegisterProviderArgs = record { - cyclesPerCall : nat64; - credentialPath : text; - hostname : text; - credentialHeaders : opt vec HttpHeader; - chainId : nat64; - cyclesPerMessageByte : nat64; +type ChainId = nat64; +type Provider = record { + providerId : ProviderId; + chainId : ChainId; + access : RpcAccess; + alias : opt RpcService; +}; +type RpcAccess = variant { + Authenticated : record { + auth : RpcAuth; + publicUrl : opt text; + }; + Unauthenticated : record { + publicUrl : text; + }; +}; +type RpcAuth = variant { + BearerToken : record { url : text }; + UrlParameter : record { urlPattern : text }; }; type RejectionCode = variant { NoError; @@ -200,7 +198,7 @@ type RpcService = variant { }; type RpcServices = variant { Custom : record { - chainId : nat64; + chainId : ChainId; services : vec RpcApi; }; EthSepolia : opt vec EthSepoliaService; @@ -233,13 +231,6 @@ type TransactionReceipt = record { contractAddress : opt text; gasUsed : nat; }; -type UpdateProviderArgs = record { - providerId : nat64; - cyclesPerCall : opt nat64; - credentialPath : opt text; - credentialHeaders : opt vec HttpHeader; - cyclesPerMessageByte : opt nat64; -}; type ValidationError = variant { Custom : text; HostNotAllowed : text; @@ -249,29 +240,17 @@ type ValidationError = variant { CredentialHeaderNotAllowed; }; service : (InitArgs) -> { - authorize : (principal, Auth) -> (success : bool); - deauthorize : (principal, Auth) -> (success : bool); eth_feeHistory : (RpcServices, opt RpcConfig, FeeHistoryArgs) -> (MultiFeeHistoryResult); eth_getBlockByNumber : (RpcServices, opt RpcConfig, BlockTag) -> (MultiGetBlockByNumberResult); eth_getLogs : (RpcServices, opt RpcConfig, GetLogsArgs) -> (MultiGetLogsResult); - eth_getTransactionCount : (RpcServices, opt RpcConfig, GetTransactionCountArgs) -> ( - MultiGetTransactionCountResult - ); + eth_getTransactionCount : (RpcServices, opt RpcConfig, GetTransactionCountArgs) -> (MultiGetTransactionCountResult); eth_getTransactionReceipt : (RpcServices, opt RpcConfig, hash : text) -> (MultiGetTransactionReceiptResult); eth_sendRawTransaction : (RpcServices, opt RpcConfig, rawSignedTransactionHex : text) -> (MultiSendRawTransactionResult); - getAccumulatedCycleCount : (ProviderId) -> (cycles : nat) query; - getAuthorized : (Auth) -> (vec principal) query; - getMetrics : () -> (Metrics) query; - getNodesInSubnet : () -> (numberOfNodes : nat32) query; - getOpenRpcAccess : () -> (active : bool) query; - getProviders : () -> (vec ProviderView) query; - getServiceProviderMap : () -> (vec record { RpcService; nat64 }) query; - manageProvider : (ManageProviderArgs) -> (); - registerProvider : (RegisterProviderArgs) -> (nat64); request : (RpcService, json : text, maxResponseBytes : nat64) -> (RequestResult); requestCost : (RpcService, json : text, maxResponseBytes : nat64) -> (RequestCostResult) query; - setOpenRpcAccess : (active : bool) -> (); - unregisterProvider : (ProviderId) -> (bool); - updateProvider : (UpdateProviderArgs) -> (); - withdrawAccumulatedCycles : (ProviderId, recipient : principal) -> (); + getMetrics : () -> (Metrics) query; + getNodesInSubnet : () -> (numberOfNodes : nat32) query; + getProviders : () -> (vec Provider) query; + getServiceProviderMap : () -> (vec record { RpcService; ProviderId }) query; + updateApiKeys : (vec record { ProviderId; opt text }) -> (); }; diff --git a/canister_ids.json b/canister_ids.json index 7b64e439..2d1ed000 100644 --- a/canister_ids.json +++ b/canister_ids.json @@ -8,10 +8,7 @@ "evm_rpc": { "ic": "7hfb6-caaaa-aaaar-qadga-cai" }, - "evm_rpc_staging_13_node": { - "ic": "a6d44-nyaaa-aaaap-abp7q-cai" - }, - "evm_rpc_staging_fiduciary": { + "evm_rpc_staging": { "ic": "xhcuo-6yaaa-aaaar-qacqq-cai" } } \ No newline at end of file diff --git a/dfx.json b/dfx.json index 609c7f69..d8067642 100644 --- a/dfx.json +++ b/dfx.json @@ -12,27 +12,27 @@ "pullable": { "dependencies": [], "wasm_url": "https://github.com/internet-computer-protocol/evm-rpc-canister/releases/latest/download/evm_rpc.wasm.gz", - "init_guide": "Number of nodes in the subnet, e.g. '(record {nodesInSubnet = 28})'" + "init_guide": "Documentation: https://internetcomputer.org/docs/current/developer-docs/multi-chain/ethereum/evm-rpc/evm-rpc-canister" }, "gzip": true, - "init_arg": "(record {nodesInSubnet = 28})" + "init_arg": "(record {})" }, - "evm_rpc_staging_13_node": { + "evm_rpc_demo": { "candid": "candid/evm_rpc.did", "type": "rust", "package": "evm_rpc", "gzip": true, - "init_arg": "(record {nodesInSubnet = 13})" + "init_arg": "(record {demo = opt true})" }, - "evm_rpc_staging_fiduciary": { + "evm_rpc_staging": { "candid": "candid/evm_rpc.did", "type": "rust", "package": "evm_rpc", "gzip": true, - "init_arg": "(record {nodesInSubnet = 28})" + "init_arg": "(record {})" }, "e2e_rust": { - "dependencies": ["evm_rpc_staging_fiduciary"], + "dependencies": ["evm_rpc_staging"], "candid": "e2e/rust/e2e_rust.did", "type": "rust", "package": "e2e" @@ -40,8 +40,8 @@ "e2e_motoko": { "dependencies": [ "evm_rpc", - "evm_rpc_staging_13_node", - "evm_rpc_staging_fiduciary" + "evm_rpc_demo", + "evm_rpc_staging" ], "type": "motoko", "main": "e2e/motoko/main.mo" diff --git a/e2e/motoko/main.mo b/e2e/motoko/main.mo index f4c10059..37f3bab3 100644 --- a/e2e/motoko/main.mo +++ b/e2e/motoko/main.mo @@ -1,6 +1,5 @@ import EvmRpc "canister:evm_rpc"; -import EvmRpcStaging13Node "canister:evm_rpc_staging_13_node"; -import EvmRpcStagingFidicuary "canister:evm_rpc_staging_fiduciary"; +import EvmRpcStaging "canister:evm_rpc_staging"; import Buffer "mo:base/Buffer"; import Cycles "mo:base/ExperimentalCycles"; @@ -16,14 +15,12 @@ shared ({ caller = installer }) actor class Main() { // (`subnet name`, `nodes in subnet`, `expected cycles for JSON-RPC call`) type SubnetTarget = (Text, Nat32, Nat); let collateralCycles = 10_000_000; - let defaultSubnet : SubnetTarget = ("13-node", 13, 99_330_400); let fiduciarySubnet : SubnetTarget = ("fiduciary", 28, 239_142_400); let testTargets = [ // (`canister module`, `canister type`, `subnet`) (EvmRpc, #production, fiduciarySubnet), - (EvmRpcStaging13Node, #staging, defaultSubnet), - (EvmRpcStagingFidicuary, #staging, fiduciarySubnet), + (EvmRpcStaging, #staging, fiduciarySubnet), ]; // (`RPC service`, `method`) @@ -155,7 +152,7 @@ shared ({ caller = installer }) actor class Main() { }; // All RPC services suitable for E2E testing - let mainnetServices = [#Alchemy, #Ankr, #BlockPi, #PublicNode, #Llama]; + let mainnetServices = [#Ankr, #BlockPi, #PublicNode, #Llama]; let l2Services = [#Ankr, #BlockPi, #PublicNode, #Llama]; let allServices : [(Text, EvmRpc.RpcServices)] = [ ( diff --git a/e2e/rust/Cargo.toml b/e2e/rust/Cargo.toml index 169f2bda..b42a5284 100644 --- a/e2e/rust/Cargo.toml +++ b/e2e/rust/Cargo.toml @@ -5,9 +5,6 @@ description = "End-to-end testing with a Rust canister." authors = ["DFINITY Foundation"] edition = "2021" -[lib] -crate-type = ["cdylib", "rlib"] - [dependencies] candid = { workspace = true } ic-certified-map = { workspace = true } diff --git a/e2e/rust/build.rs b/e2e/rust/build.rs index 42326a75..4b23e54d 100644 --- a/e2e/rust/build.rs +++ b/e2e/rust/build.rs @@ -5,7 +5,7 @@ fn main() { builder.add({ // Uppercase canister name is a workaround for using `ic-cdk-bindgen` with `dfx` >= 0.18.0 - let mut config = Config::new("EVM_RPC_STAGING_FIDUCIARY"); + let mut config = Config::new("EVM_RPC_STAGING"); config.binding.type_attributes = "#[derive(CandidType, Clone, Debug, Deserialize)]".to_string(); config diff --git a/e2e/rust/src/lib.rs b/e2e/rust/src/lib.rs deleted file mode 100644 index 89d3fcf4..00000000 --- a/e2e/rust/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod declarations; diff --git a/e2e/rust/src/main.rs b/e2e/rust/src/main.rs index 12177d7c..7619d0f2 100644 --- a/e2e/rust/src/main.rs +++ b/e2e/rust/src/main.rs @@ -1,9 +1,11 @@ use candid::candid_method; use ic_cdk_macros::update; -use e2e::declarations::EVM_RPC_STAGING_FIDUCIARY::{ +mod declarations; + +use declarations::EVM_RPC_STAGING::{ BlockTag, EthMainnetService, GetBlockByNumberResult, MultiGetBlockByNumberResult, - ProviderError, RpcError, RpcService, RpcServices, EVM_RPC_STAGING_FIDUCIARY as evm_rpc, + ProviderError, RpcError, RpcService, RpcServices, EVM_RPC_STAGING as evm_rpc, }; fn main() {} diff --git a/scripts/e2e b/scripts/e2e index 24718904..7494c0da 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -3,9 +3,9 @@ ( dfx canister create --all && npm run generate && - dfx deploy evm_rpc --argument "(record {nodesInSubnet = 28})" --mode reinstall -y && - dfx deploy evm_rpc_staging_13_node --argument "(record {nodesInSubnet = 13})" --mode reinstall -y && - dfx deploy evm_rpc_staging_fiduciary --argument "(record {nodesInSubnet = 28})" --mode reinstall -y && + dfx deploy evm_rpc --mode reinstall -y && + dfx deploy evm_rpc_demo --mode reinstall -y && + dfx deploy evm_rpc_staging --mode reinstall -y && dfx deploy e2e_rust && dfx canister call e2e_rust test && dfx deploy e2e_motoko && diff --git a/src/accounting.rs b/src/accounting.rs index a92be24a..a3a399dc 100644 --- a/src/accounting.rs +++ b/src/accounting.rs @@ -1,202 +1,56 @@ -use cketh_common::eth_rpc_client::providers::RpcApi; - -use crate::{ - constants::{ - CANISTER_OVERHEAD, COLLATERAL_CYCLES_PER_NODE, HTTP_OUTCALL_REQUEST_BASE_COST, - HTTP_OUTCALL_REQUEST_COST_PER_BYTE, HTTP_OUTCALL_REQUEST_PER_NODE_COST, - HTTP_OUTCALL_RESPONSE_COST_PER_BYTE, INGRESS_MESSAGE_BYTE_RECEIVED_COST, - INGRESS_MESSAGE_RECEIVED_COST, INGRESS_OVERHEAD_BYTES, RPC_URL_MIN_COST_BYTES, - }, - memory::get_nodes_in_subnet, - types::{Provider, ResolvedRpcService}, +use crate::constants::{ + CANISTER_OVERHEAD, COLLATERAL_CYCLES_PER_NODE, HTTP_OUTCALL_REQUEST_BASE_COST, + HTTP_OUTCALL_REQUEST_COST_PER_BYTE, HTTP_OUTCALL_REQUEST_PER_NODE_COST, + HTTP_OUTCALL_RESPONSE_COST_PER_BYTE, INGRESS_MESSAGE_BYTE_RECEIVED_COST, + INGRESS_MESSAGE_RECEIVED_COST, INGRESS_OVERHEAD_BYTES, NODES_IN_SUBNET, RPC_URL_COST_BYTES, }; -/// Returns the cycles cost of an RPC request. -pub fn get_rpc_cost( - service: &ResolvedRpcService, - payload_size_bytes: u64, - max_response_bytes: u64, -) -> u128 { - match service { - ResolvedRpcService::Api(api) => { - get_http_request_cost(api, payload_size_bytes, max_response_bytes) - } - ResolvedRpcService::Provider(provider) => { - let http_cost = - get_http_request_cost(&provider.api(), payload_size_bytes, max_response_bytes); - let provider_cost = get_provider_cost(provider, payload_size_bytes); - http_cost + provider_cost - } - } -} - -/// Calculates the baseline cost of sending a JSON-RPC request using HTTP outcalls. -pub fn get_http_request_cost( - api: &RpcApi, - payload_size_bytes: u64, - max_response_bytes: u64, -) -> u128 { - let nodes_in_subnet = get_nodes_in_subnet(); - let ingress_bytes = payload_size_bytes as u128 - + u32::max(RPC_URL_MIN_COST_BYTES, api.url.len() as u32) as u128 - + INGRESS_OVERHEAD_BYTES; +/// Calculates the cost of sending a JSON-RPC request using HTTP outcalls. +pub fn get_http_request_cost(payload_size_bytes: u64, max_response_bytes: u64) -> u128 { + let nodes_in_subnet = NODES_IN_SUBNET as u128; + let ingress_bytes = + payload_size_bytes as u128 + RPC_URL_COST_BYTES as u128 + INGRESS_OVERHEAD_BYTES; let cost_per_node = INGRESS_MESSAGE_RECEIVED_COST + INGRESS_MESSAGE_BYTE_RECEIVED_COST * ingress_bytes + HTTP_OUTCALL_REQUEST_BASE_COST - + HTTP_OUTCALL_REQUEST_PER_NODE_COST * nodes_in_subnet as u128 + + HTTP_OUTCALL_REQUEST_PER_NODE_COST * nodes_in_subnet + HTTP_OUTCALL_REQUEST_COST_PER_BYTE * payload_size_bytes as u128 + HTTP_OUTCALL_RESPONSE_COST_PER_BYTE * max_response_bytes as u128 + CANISTER_OVERHEAD; - cost_per_node * (nodes_in_subnet as u128) -} - -/// Calculate the additional cost for calling a registered JSON-RPC provider. -pub fn get_provider_cost(provider: &Provider, payload_size_bytes: u64) -> u128 { - let nodes_in_subnet = get_nodes_in_subnet(); - let cost_per_node = provider.cycles_per_call as u128 - + provider.cycles_per_message_byte as u128 * payload_size_bytes as u128; - cost_per_node * (nodes_in_subnet as u128) + cost_per_node * nodes_in_subnet } /// Calculate the cost + collateral cycles for an HTTP request. pub fn get_cost_with_collateral(cycles_cost: u128) -> u128 { - cycles_cost + COLLATERAL_CYCLES_PER_NODE * get_nodes_in_subnet() as u128 + cycles_cost + COLLATERAL_CYCLES_PER_NODE * NODES_IN_SUBNET as u128 } #[cfg(test)] mod test { use super::*; - use crate::{ - accounting::{get_provider_cost, get_rpc_cost}, - constants::{NODES_IN_FIDUCIARY_SUBNET, NODES_IN_STANDARD_SUBNET}, - memory::{set_nodes_in_subnet, PROVIDERS}, - providers::do_register_provider, - types::{Provider, RegisterProviderArgs, ResolvedRpcService}, - }; - use candid::Principal; + use crate::{accounting::get_http_request_cost, constants::NODES_IN_SUBNET}; #[test] fn test_request_cost() { - for nodes_in_subnet in [1, NODES_IN_STANDARD_SUBNET, NODES_IN_FIDUCIARY_SUBNET] { - println!("Nodes in subnet: {nodes_in_subnet}"); - - set_nodes_in_subnet(nodes_in_subnet); - - let url = "https://cloudflare-eth.com"; - let payload = - "{\"jsonrpc\":\"2.0\",\"method\":\"eth_gasPrice\",\"params\":[],\"id\":1}"; - let base_cost = get_rpc_cost( - &ResolvedRpcService::Api(RpcApi { - url: url.to_string(), - headers: None, - }), - payload.len() as u64, - 1000, - ); - let base_cost_10_extra_bytes = get_rpc_cost( - &ResolvedRpcService::Api(RpcApi { - url: url.to_string(), - headers: None, - }), - payload.len() as u64 + 10, - 1000, - ); - let estimated_cost_10_extra_bytes = base_cost - + 10 * (INGRESS_MESSAGE_BYTE_RECEIVED_COST + HTTP_OUTCALL_REQUEST_COST_PER_BYTE) - * nodes_in_subnet as u128; - assert_eq!(base_cost_10_extra_bytes, estimated_cost_10_extra_bytes,); - } - } - - #[test] - fn test_provider_cost() { - for nodes_in_subnet in [1, NODES_IN_STANDARD_SUBNET, NODES_IN_FIDUCIARY_SUBNET] { - println!("Nodes in subnet: {nodes_in_subnet}"); - - set_nodes_in_subnet(nodes_in_subnet); - - let provider = Provider { - provider_id: 0, - hostname: "".to_string(), - credential_path: "".to_string(), - credential_headers: vec![], - owner: Principal::anonymous(), - chain_id: 1, - cycles_owed: 0, - cycles_per_call: 0, - cycles_per_message_byte: 2, - primary: false, - }; - let base_cost = get_provider_cost( - &provider, - "{\"jsonrpc\":\"2.0\",\"method\":\"eth_gasPrice\",\"params\":[],\"id\":1}".len() - as u64, - ); - - let provider_10_extra_bytes = Provider { - provider_id: 0, - hostname: "".to_string(), - credential_path: "".to_string(), - credential_headers: vec![], - owner: Principal::anonymous(), - chain_id: 1, - cycles_owed: 0, - cycles_per_call: 1000, - cycles_per_message_byte: 2, - primary: false, - }; - let base_cost_10_extra_bytes = get_provider_cost( - &provider_10_extra_bytes, - "{\"jsonrpc\":\"2.0\",\"method\":\"eth_gasPrice\",\"params\":[],\"id\":1}".len() - as u64 - + 10, - ); - assert_eq!( - base_cost + (10 * 2 + 1000) * nodes_in_subnet as u128, - base_cost_10_extra_bytes - ) - } + let payload = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_gasPrice\",\"params\":[],\"id\":1}"; + let base_cost = get_http_request_cost(payload.len() as u64, 1000); + let base_cost_10_extra_bytes = get_http_request_cost(payload.len() as u64 + 10, 1000); + let estimated_cost_10_extra_bytes = base_cost + + 10 * (INGRESS_MESSAGE_BYTE_RECEIVED_COST + HTTP_OUTCALL_REQUEST_COST_PER_BYTE) + * NODES_IN_SUBNET as u128; + assert_eq!(base_cost_10_extra_bytes, estimated_cost_10_extra_bytes); } #[test] fn test_candid_rpc_cost() { - let provider_id = do_register_provider( - Principal::anonymous(), - RegisterProviderArgs { - chain_id: 0, - hostname: "rpc.example.com".to_string(), - credential_headers: None, - credential_path: "".to_string(), - cycles_per_call: 999, - cycles_per_message_byte: 1000, - }, - ); - let service = ResolvedRpcService::Provider( - PROVIDERS.with(|providers| providers.borrow().get(&provider_id).unwrap()), - ); - - // 13-node subnet - set_nodes_in_subnet(NODES_IN_STANDARD_SUBNET); - assert_eq!( - [ - get_rpc_cost(&service, 0, 0), - get_rpc_cost(&service, 123, 123), - get_rpc_cost(&service, 123, 4567890), - get_rpc_cost(&service, 890, 4567890), - ], - [87008987, 93724787, 47598501587, 47632402987] - ); - - // Fiduciary subnet - set_nodes_in_subnet(NODES_IN_FIDUCIARY_SUBNET); assert_eq!( [ - get_rpc_cost(&service, 0, 0), - get_rpc_cost(&service, 123, 123), - get_rpc_cost(&service, 123, 4567890), - get_rpc_cost(&service, 890, 4567890), + get_http_request_cost(0, 0), + get_http_request_cost(123, 123), + get_http_request_cost(123, 4567890), + get_http_request_cost(890, 4567890), ], - [212603972, 227068772, 102545049572, 102618067972] + [212576000, 223596800, 102541577600, 102593120000] ); } } diff --git a/src/auth.rs b/src/auth.rs deleted file mode 100644 index 645e6d82..00000000 --- a/src/auth.rs +++ /dev/null @@ -1,117 +0,0 @@ -use candid::Principal; - -use crate::{ - memory::{AUTH, METADATA}, - types::{Auth, PrincipalStorable}, -}; - -pub fn is_authorized(principal: &Principal, auth: Auth) -> bool { - AUTH.with(|a| { - if let Some(v) = a.borrow().get(&PrincipalStorable(*principal)) { - v.is_authorized(auth) - } else { - false - } - }) -} - -pub fn require_manage_or_controller() -> Result<(), String> { - let caller = ic_cdk::caller(); - if is_authorized(&caller, Auth::Manage) || ic_cdk::api::is_controller(&caller) { - Ok(()) - } else { - Err("You are not authorized".to_string()) - } -} - -pub fn require_register_provider() -> Result<(), String> { - if is_authorized(&ic_cdk::caller(), Auth::RegisterProvider) { - Ok(()) - } else { - Err("You are not authorized".to_string()) - } -} - -pub fn is_rpc_allowed(caller: &Principal) -> bool { - METADATA.with(|m| m.borrow().get().open_rpc_access) || is_authorized(caller, Auth::PriorityRpc) -} - -pub fn do_authorize(principal: Principal, auth: Auth) -> bool { - if principal == Principal::anonymous() { - false - } else { - AUTH.with(|a| { - let mut auth_map = a.borrow_mut(); - let principal = PrincipalStorable(principal); - let mut auth_set = auth_map.get(&principal).unwrap_or_default(); - if auth_set.authorize(auth) { - auth_map.insert(principal, auth_set); - true - } else { - false - } - }) - } -} - -pub fn do_deauthorize(principal: Principal, auth: Auth) -> bool { - AUTH.with(|a| { - let mut auth_map = a.borrow_mut(); - let principal = PrincipalStorable(principal); - if let Some(mut auth_set) = auth_map.get(&principal) { - let changed = auth_set.deauthorize(auth); - if auth_set.is_empty() { - auth_map.remove(&principal); - } else { - auth_map.insert(principal, auth_set); - } - changed - } else { - false - } - }) -} - -#[cfg(test)] -mod test { - use candid::Principal; - - use crate::{ - auth::{do_authorize, do_deauthorize, is_authorized}, - types::Auth, - }; - - #[test] - fn test_authorization() { - let principal1 = - Principal::from_text("k5dlc-ijshq-lsyre-qvvpq-2bnxr-pb26c-ag3sc-t6zo5-rdavy-recje-zqe") - .unwrap(); - let principal2 = - Principal::from_text("yxhtl-jlpgx-wqnzc-ysego-h6yqe-3zwfo-o3grn-gvuhm-nz3kv-ainub-6ae") - .unwrap(); - assert!(!is_authorized(&principal1, Auth::PriorityRpc)); - assert!(!is_authorized(&principal2, Auth::PriorityRpc)); - - do_authorize(principal1, Auth::PriorityRpc); - assert!(is_authorized(&principal1, Auth::PriorityRpc)); - assert!(!is_authorized(&principal2, Auth::PriorityRpc)); - - do_deauthorize(principal1, Auth::PriorityRpc); - assert!(!is_authorized(&principal1, Auth::PriorityRpc)); - assert!(!is_authorized(&principal2, Auth::PriorityRpc)); - - do_authorize(principal1, Auth::RegisterProvider); - assert!(is_authorized(&principal1, Auth::RegisterProvider)); - assert!(!is_authorized(&principal2, Auth::RegisterProvider)); - - do_deauthorize(principal1, Auth::RegisterProvider); - assert!(!is_authorized(&principal1, Auth::RegisterProvider)); - - do_authorize(principal2, Auth::Manage); - assert!(!is_authorized(&principal1, Auth::Manage)); - assert!(is_authorized(&principal2, Auth::Manage)); - - assert!(!is_authorized(&principal2, Auth::PriorityRpc)); - assert!(!is_authorized(&principal2, Auth::RegisterProvider)); - } -} diff --git a/src/candid_rpc.rs b/src/candid_rpc.rs index aae1dc7f..9dc0ca53 100644 --- a/src/candid_rpc.rs +++ b/src/candid_rpc.rs @@ -18,14 +18,13 @@ use evm_rpc_types::Hex32; use ic_cdk::api::management_canister::http_request::{CanisterHttpRequestArgument, HttpResponse}; use crate::{ - accounting::get_rpc_cost, - add_metric, add_metric_entry, - auth::is_rpc_allowed, + accounting::get_http_request_cost, + add_metric_entry, constants::{ DEFAULT_ETH_MAINNET_SERVICES, DEFAULT_ETH_SEPOLIA_SERVICES, DEFAULT_L2_MAINNET_SERVICES, ETH_GET_LOGS_MAX_BLOCKS, }, - http::do_http_request, + http::http_request, providers::resolve_rpc_service, types::{ candid_types::{self, SendRawTransactionStatus}, @@ -52,8 +51,7 @@ impl RpcTransport for CanisterTransport { effective_response_size_estimate: u64, ) -> RpcResult { let service = resolve_rpc_service(service.clone())?; - let cycles_cost = get_rpc_cost( - &service, + let cycles_cost = get_http_request_cost( request .body .as_ref() @@ -62,7 +60,7 @@ impl RpcTransport for CanisterTransport { effective_response_size_estimate, ); let rpc_method = MetricRpcMethod(method.to_string()); - do_http_request(ic_cdk::caller(), rpc_method, service, request, cycles_cost).await + http_request(rpc_method, service, request, cycles_cost).await } } @@ -77,10 +75,6 @@ fn get_rpc_client( source: RpcServices, config: RpcConfig, ) -> RpcResult> { - if !is_rpc_allowed(&ic_cdk::caller()) { - add_metric!(err_no_permission, 1); - return Err(ProviderError::NoPermission.into()); - } Ok(match source { RpcServices::Custom { chain_id, services } => CkEthRpcClient::new( EthereumNetwork(chain_id), @@ -157,7 +151,14 @@ fn process_result(method: RpcMethod, result: Result>) -> { add_metric_entry!( inconsistent_responses, - (method.into(), MetricRpcHost(provider.hostname)), + ( + method.into(), + MetricRpcHost( + provider + .hostname() + .unwrap_or_else(|| "(unknown)".to_string()) + ) + ), 1 ) } @@ -292,73 +293,78 @@ fn get_transaction_hash(raw_signed_transaction_hex: &str) -> Option { Some(Hash(transaction.hash.0)) } -#[test] -fn test_process_result_mapping() { - use cketh_common::eth_rpc_client::{providers::EthMainnetService, MultiCallResults}; +#[cfg(test)] +mod test { + use super::*; - let method = RpcMethod::EthGetTransactionCount; + #[test] + fn test_process_result_mapping() { + use cketh_common::eth_rpc_client::{providers::EthMainnetService, MultiCallResults}; - assert_eq!( - process_result(method, Ok(5)), - MultiRpcResult::Consistent(Ok(5)) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::<()>::ConsistentError( - RpcError::ProviderError(ProviderError::MissingRequiredProvider) - )) - ), - MultiRpcResult::Consistent(Err(RpcError::ProviderError( - ProviderError::MissingRequiredProvider - ))) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::<()>::InconsistentResults( - MultiCallResults { - results: Default::default() - } - )) - ), - MultiRpcResult::Inconsistent(vec![]) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::InconsistentResults(MultiCallResults { - results: vec![(RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5))] + let method = RpcMethod::EthGetTransactionCount; + + assert_eq!( + process_result(method, Ok(5)), + MultiRpcResult::Consistent(Ok(5)) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::<()>::ConsistentError( + RpcError::ProviderError(ProviderError::MissingRequiredProvider) + )) + ), + MultiRpcResult::Consistent(Err(RpcError::ProviderError( + ProviderError::MissingRequiredProvider + ))) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::<()>::InconsistentResults( + MultiCallResults { + results: Default::default() + } + )) + ), + MultiRpcResult::Inconsistent(vec![]) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::InconsistentResults(MultiCallResults { + results: vec![(RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5))] + .into_iter() + .collect(), + })) + ), + MultiRpcResult::Inconsistent(vec![( + RpcService::EthMainnet(EthMainnetService::Ankr), + Ok(5) + )]) + ); + assert_eq!( + process_result( + method, + Err(MultiCallError::InconsistentResults(MultiCallResults { + results: vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + ( + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Err(RpcError::ProviderError(ProviderError::NoPermission)) + ) + ] .into_iter() .collect(), - })) - ), - MultiRpcResult::Inconsistent(vec![( - RpcService::EthMainnet(EthMainnetService::Ankr), - Ok(5) - )]) - ); - assert_eq!( - process_result( - method, - Err(MultiCallError::InconsistentResults(MultiCallResults { - results: vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), - ( - RpcService::EthMainnet(EthMainnetService::Cloudflare), - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ) - ] - .into_iter() - .collect(), - })) - ), - MultiRpcResult::Inconsistent(vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), - ( - RpcService::EthMainnet(EthMainnetService::Cloudflare), - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ) - ]) - ); + })) + ), + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + ( + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Err(RpcError::ProviderError(ProviderError::NoPermission)) + ) + ]) + ); + } } diff --git a/src/constants.rs b/src/constants.rs index 5edfd42f..a278a309 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -20,11 +20,12 @@ pub const CANISTER_OVERHEAD: u128 = 1_000_000; pub const COLLATERAL_CYCLES_PER_NODE: u128 = 10_000_000; // Minimum number of bytes charged for a URL; improves consistency of costs between providers -pub const RPC_URL_MIN_COST_BYTES: u32 = 256; +pub const RPC_URL_COST_BYTES: u32 = 256; pub const MINIMUM_WITHDRAWAL_CYCLES: u128 = 1_000_000_000; pub const STRING_STORABLE_MAX_SIZE: u32 = 100; +pub const API_KEY_MAX_SIZE: u32 = 512; pub const PROVIDER_MAX_SIZE: u32 = 256; pub const RPC_SERVICE_MAX_SIZE: u32 = 256; pub const AUTH_SET_STORABLE_MAX_SIZE: u32 = 1000; @@ -32,9 +33,11 @@ pub const WASM_PAGE_SIZE: u64 = 65536; pub const ETH_GET_LOGS_MAX_BLOCKS: u32 = 500; -pub const NODES_IN_STANDARD_SUBNET: u32 = 13; -pub const NODES_IN_FIDUCIARY_SUBNET: u32 = 28; -pub const DEFAULT_OPEN_RPC_ACCESS: bool = true; +pub const NODES_IN_SUBNET: u32 = 28; + +pub const API_KEY_REPLACE_STRING: &str = "{API_KEY}"; +pub const VALID_API_KEY_CHARS: &str = + "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZabcdefghijklmnopqrstuvwxyz$-_.+!*"; // Providers used by default (when passing `null` with `RpcServices`) pub const DEFAULT_ETH_MAINNET_SERVICES: &[EthMainnetService] = &[ @@ -53,7 +56,7 @@ pub const DEFAULT_L2_MAINNET_SERVICES: &[L2MainnetService] = &[ L2MainnetService::PublicNode, ]; -pub const CONTENT_TYPE_HEADER: &str = "Content-Type"; +pub const CONTENT_TYPE_HEADER_LOWERCASE: &str = "content-type"; pub const CONTENT_TYPE_VALUE: &str = "application/json"; pub const ETH_MAINNET_CHAIN_ID: u64 = 1; diff --git a/src/http.rs b/src/http.rs index 219579ae..4414f690 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,4 +1,3 @@ -use candid::Principal; use cketh_common::eth_rpc::{HttpOutcallError, ProviderError, RpcError, ValidationError}; use ic_cdk::api::management_canister::http_request::{ CanisterHttpRequestArgument, HttpHeader, HttpMethod, HttpResponse, TransformArgs, @@ -7,34 +6,31 @@ use ic_cdk::api::management_canister::http_request::{ use num_traits::ToPrimitive; use crate::{ - accounting::{get_cost_with_collateral, get_provider_cost, get_rpc_cost}, - add_metric, add_metric_entry, - auth::{is_authorized, is_rpc_allowed}, - constants::{CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE, SERVICE_HOSTS_BLOCKLIST}, - memory::PROVIDERS, - types::{Auth, MetricRpcHost, MetricRpcMethod, ResolvedRpcService, RpcResult}, + accounting::{get_cost_with_collateral, get_http_request_cost}, + add_metric_entry, + constants::{CONTENT_TYPE_HEADER_LOWERCASE, CONTENT_TYPE_VALUE, SERVICE_HOSTS_BLOCKLIST}, + memory::is_demo_active, + types::{MetricRpcHost, MetricRpcMethod, ResolvedRpcService, RpcResult}, util::canonicalize_json, }; -pub async fn do_json_rpc_request( - caller: Principal, +pub async fn json_rpc_request( service: ResolvedRpcService, rpc_method: MetricRpcMethod, json_rpc_payload: &str, max_response_bytes: u64, ) -> RpcResult { - if !is_rpc_allowed(&caller) { - add_metric!(err_no_permission, 1); - return Err(ProviderError::NoPermission.into()); - } - let cycles_cost = get_rpc_cost(&service, json_rpc_payload.len() as u64, max_response_bytes); + let cycles_cost = get_http_request_cost(json_rpc_payload.len() as u64, max_response_bytes); let api = service.api(); - let mut request_headers = vec![HttpHeader { - name: CONTENT_TYPE_HEADER.to_string(), - value: CONTENT_TYPE_VALUE.to_string(), - }]; - if let Some(headers) = api.headers { - request_headers.extend(headers); + let mut request_headers = api.headers.unwrap_or_default(); + if !request_headers + .iter() + .any(|header| header.name.to_lowercase() == CONTENT_TYPE_HEADER_LOWERCASE) + { + request_headers.push(HttpHeader { + name: CONTENT_TYPE_HEADER_LOWERCASE.to_string(), + value: CONTENT_TYPE_VALUE.to_string(), + }); } let request = CanisterHttpRequestArgument { url: api.url, @@ -47,35 +43,42 @@ pub async fn do_json_rpc_request( vec![], )), }; - do_http_request(caller, rpc_method, service, request, cycles_cost).await + http_request(rpc_method, service, request, cycles_cost).await } -pub async fn do_http_request( - caller: Principal, +pub async fn http_request( rpc_method: MetricRpcMethod, service: ResolvedRpcService, request: CanisterHttpRequestArgument, cycles_cost: u128, ) -> RpcResult { let api = service.api(); - let provider = match service { - ResolvedRpcService::Api(_) => None, - ResolvedRpcService::Provider(provider) => Some(provider), - }; let parsed_url = match url::Url::parse(&api.url) { Ok(url) => url, - Err(_) => return Err(ValidationError::UrlParseError(api.url).into()), + Err(_) => { + return Err(ValidationError::Custom(format!("Error parsing URL: {}", api.url)).into()) + } }; let host = match parsed_url.host_str() { Some(host) => host, - None => return Err(ValidationError::UrlParseError(api.url).into()), + None => { + return Err(ValidationError::Custom(format!( + "Error parsing hostname from URL: {}", + api.url + )) + .into()) + } }; let rpc_host = MetricRpcHost(host.to_string()); if SERVICE_HOSTS_BLOCKLIST.contains(&rpc_host.0.as_str()) { add_metric_entry!(err_host_not_allowed, rpc_host.clone(), 1); - return Err(ValidationError::HostNotAllowed(rpc_host.0).into()); + return Err(ValidationError::Custom(format!( + "Disallowed RPC service host: {}", + rpc_host.0 + )) + .into()); } - if !is_authorized(&caller, Auth::FreeRpc) { + if !is_demo_active() { let cycles_available = ic_cdk::api::call::msg_cycles_available128(); let cycles_cost_with_collateral = get_cost_with_collateral(cycles_cost); if cycles_available < cycles_cost_with_collateral { @@ -86,22 +89,6 @@ pub async fn do_http_request( .into()); } ic_cdk::api::call::msg_cycles_accept128(cycles_cost); - if let Some(mut provider) = provider { - provider.cycles_owed += get_provider_cost( - &provider, - request - .body - .as_ref() - .map(|bytes| bytes.len() as u64) - .unwrap_or_default(), - ); - PROVIDERS.with(|p| { - // Error should not happen here as it was checked before - p.borrow_mut() - .insert(provider.provider_id, provider) - .expect("unable to update Provider"); - }); - } add_metric_entry!( cycles_charged, (rpc_method.clone(), rpc_host.clone()), @@ -122,7 +109,7 @@ pub async fn do_http_request( } } -pub fn do_transform_http_request(args: TransformArgs) -> HttpResponse { +pub fn transform_http_request(args: TransformArgs) -> HttpResponse { HttpResponse { status: args.response.status, body: canonicalize_json(&args.response.body).unwrap_or(args.response.body), diff --git a/src/lib.rs b/src/lib.rs index c3773b8b..8691ff58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ pub use candid::Principal; pub mod accounting; -pub mod auth; pub mod candid_rpc; pub mod constants; pub mod http; diff --git a/src/main.rs b/src/main.rs index befda3aa..9caf97b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,19 @@ -use candid::{candid_method, Principal}; +use candid::candid_method; use cketh_common::eth_rpc::{Block, RpcError}; use cketh_common::eth_rpc_client::providers::RpcService; use cketh_common::eth_rpc_client::RpcConfig; use cketh_common::logs::INFO; -use evm_rpc::accounting::{get_cost_with_collateral, get_rpc_cost}; +use evm_rpc::accounting::{get_cost_with_collateral, get_http_request_cost}; use evm_rpc::candid_rpc::CandidRpcClient; +use evm_rpc::constants::NODES_IN_SUBNET; use evm_rpc::http::get_http_response_body; -use evm_rpc::memory::{get_nodes_in_subnet, set_nodes_in_subnet}; -use evm_rpc::metrics::encode_metrics; -use evm_rpc::providers::{ - do_get_accumulated_cycle_count, do_withdraw_accumulated_cycles, find_provider, - get_default_providers, get_default_service_provider_hostnames, get_known_chain_id, - resolve_rpc_service, set_service_provider, +use evm_rpc::memory::{ + insert_api_key, is_api_key_principal, remove_api_key, set_api_key_principals, set_demo_active, }; +use evm_rpc::metrics::encode_metrics; +use evm_rpc::providers::{find_provider, resolve_rpc_service, PROVIDERS, SERVICE_PROVIDER_MAP}; +use evm_rpc::types::{Provider, ProviderId, RpcAccess}; use ic_canister_log::log; use ic_canisters_http_types::{ HttpRequest as AssetHttpRequest, HttpResponse as AssetHttpResponse, HttpResponseBuilder, @@ -24,20 +24,21 @@ use ic_cdk::{query, update}; use ic_nervous_system_common::serve_metrics; use evm_rpc::{ - auth::{do_authorize, do_deauthorize, require_manage_or_controller, require_register_provider}, - constants::WASM_PAGE_SIZE, - http::{do_json_rpc_request, do_transform_http_request}, - memory::{AUTH, METADATA, PROVIDERS, SERVICE_PROVIDER_MAP, UNSTABLE_METRICS}, - providers::{ - do_manage_provider, do_register_provider, do_unregister_provider, do_update_provider, - }, - types::{ - candid_types, Auth, InitArgs, ManageProviderArgs, MetricRpcMethod, Metrics, MultiRpcResult, - ProviderView, RegisterProviderArgs, RpcServices, UpdateProviderArgs, - }, + http::{json_rpc_request, transform_http_request}, + memory::UNSTABLE_METRICS, + types::{candid_types, InitArgs, MetricRpcMethod, Metrics, MultiRpcResult, RpcServices}, }; use evm_rpc_types::Hex32; +pub fn require_api_key_principal_or_controller() -> Result<(), String> { + let caller = ic_cdk::caller(); + if is_api_key_principal(&caller) || is_controller(&caller) { + Ok(()) + } else { + Err("You are not authorized".to_string()) + } +} + #[update(name = "eth_getLogs")] #[candid_method(rename = "eth_getLogs")] pub async fn eth_get_logs( @@ -127,8 +128,7 @@ async fn request( json_rpc_payload: String, max_response_bytes: u64, ) -> Result { - let response = do_json_rpc_request( - ic_cdk::caller(), + let response = json_rpc_request( resolve_rpc_service(service)?, MetricRpcMethod("request".to_string()), &json_rpc_payload, @@ -141,12 +141,11 @@ async fn request( #[query(name = "requestCost")] #[candid_method(query, rename = "requestCost")] fn request_cost( - service: RpcService, + _service: RpcService, json_rpc_payload: String, max_response_bytes: u64, ) -> Result { - Ok(get_cost_with_collateral(get_rpc_cost( - &resolve_rpc_service(service)?, + Ok(get_cost_with_collateral(get_http_request_cost( json_rpc_payload.len() as u64, max_response_bytes, ))) @@ -154,107 +153,81 @@ fn request_cost( #[query(name = "getProviders")] #[candid_method(query, rename = "getProviders")] -fn get_providers() -> Vec { - PROVIDERS.with(|p| { - p.borrow() - .iter() - .map(|(_, provider)| provider.into()) - .collect::>() - }) -} - -#[update(name = "registerProvider", guard = "require_register_provider")] -#[candid_method(rename = "registerProvider")] -fn register_provider(provider: RegisterProviderArgs) -> u64 { - do_register_provider(ic_cdk::caller(), provider) +fn get_providers() -> Vec { + PROVIDERS.to_vec() } -#[update(name = "unregisterProvider")] -#[candid_method(rename = "unregisterProvider")] -fn unregister_provider(provider_id: u64) -> bool { - let caller = ic_cdk::caller(); - do_unregister_provider(caller, is_controller(&caller), provider_id) +#[query(name = "getServiceProviderMap")] +#[candid_method(query, rename = "getServiceProviderMap")] +fn get_service_provider_map() -> Vec<(RpcService, ProviderId)> { + SERVICE_PROVIDER_MAP.with(|map| map.iter().map(|(k, v)| (k.clone(), *v)).collect()) } -#[update(name = "updateProvider")] -#[candid_method(rename = "updateProvider")] -fn update_provider(provider: UpdateProviderArgs) { - let caller = ic_cdk::caller(); - do_update_provider(caller, is_controller(&caller), provider) +#[query(name = "getNodesInSubnet")] +#[candid_method(query, rename = "getNodesInSubnet")] +fn get_nodes_in_subnet() -> u32 { + NODES_IN_SUBNET } -#[update(name = "manageProvider", guard = "require_manage_or_controller")] -#[candid_method(rename = "manageProvider")] -fn manage_provider(args: ManageProviderArgs) { +#[update( + name = "updateApiKeys", + guard = "require_api_key_principal_or_controller" +)] +#[candid_method(rename = "updateApiKeys")] +/// Inserts or removes RPC provider API keys. +/// +/// For each element of `api_keys`, passing `(id, Some(key))` corresponds to inserting or updating +/// an API key, while passing `(id, None)` indicates that the key should be removed from the canister. +/// +/// Panics if the list of provider IDs includes a nonexistent or "unauthenticated" (fully public) provider. +async fn update_api_keys(api_keys: Vec<(ProviderId, Option)>) { log!( INFO, - "[{}] Managing provider: {}", + "[{}] Updating API keys for providers: {}", ic_cdk::caller(), - args.provider_id - ); - do_manage_provider(args) -} - -#[query(name = "getServiceProviderMap", guard = "require_manage_or_controller")] -#[candid_method(query, rename = "getServiceProviderMap")] -fn get_service_provider_map() -> Vec<(RpcService, u64)> { - SERVICE_PROVIDER_MAP.with(|map| { - map.borrow() + api_keys .iter() - .filter_map(|(k, v)| Some((k.try_into().ok()?, v))) - .collect() - }) -} - -#[query(name = "getNodesInSubnet")] -#[candid_method(query, rename = "getNodesInSubnet")] -async fn get_nodes_in_subnet_() -> u32 { - get_nodes_in_subnet() -} - -#[query(name = "getAccumulatedCycleCount")] -#[candid_method(query, rename = "getAccumulatedCycleCount")] -fn get_accumulated_cycle_count(provider_id: u64) -> u128 { - let caller = ic_cdk::caller(); - do_get_accumulated_cycle_count(caller, is_controller(&caller), provider_id) -} - -#[update(name = "withdrawAccumulatedCycles")] -#[candid_method(rename = "withdrawAccumulatedCycles")] -async fn withdraw_accumulated_cycles(provider_id: u64, canister_id: Principal) { - let caller = ic_cdk::caller(); - do_withdraw_accumulated_cycles(caller, is_controller(&caller), provider_id, canister_id).await + .map(|(id, _)| id.to_string()) + .collect::>() + .join(", ") + ); + for (provider_id, api_key) in api_keys { + let provider = find_provider(|provider| provider.provider_id == provider_id) + .unwrap_or_else(|| panic!("Provider not found: {}", provider_id)); + match provider.access { + RpcAccess::Authenticated { .. } => {} + RpcAccess::Unauthenticated { .. } => { + panic!( + "Trying to set API key for unauthenticated provider: {}", + provider_id + ) + } + }; + match api_key { + Some(key) => insert_api_key(provider_id, key.try_into().expect("Invalid API key")), + None => remove_api_key(provider_id), + } + } } #[query(name = "__transform_json_rpc")] fn transform(args: TransformArgs) -> HttpResponse { - do_transform_http_request(args) + transform_http_request(args) } #[ic_cdk::init] fn init(args: InitArgs) { post_upgrade(args); - - for provider in get_default_providers() { - do_register_provider(ic_cdk::caller(), provider); - } - for (service, hostname) in get_default_service_provider_hostnames() { - let provider = find_provider(|p| { - Some(p.chain_id) == get_known_chain_id(&service) && p.hostname == hostname - }) - .unwrap_or_else(|| { - panic!( - "Missing default provider for service {:?} with hostname {:?}", - service, hostname - ) - }); - set_service_provider(&service, &provider); - } } #[ic_cdk::post_upgrade] fn post_upgrade(args: InitArgs) { - set_nodes_in_subnet(args.nodes_in_subnet); + if let Some(demo) = args.demo { + set_demo_active(demo); + } + if let Some(principals) = args.manage_api_keys { + set_api_key_principals(principals); + } } #[query] @@ -337,80 +310,6 @@ fn get_metrics() -> Metrics { UNSTABLE_METRICS.with(|metrics| (*metrics.borrow()).clone()) } -#[query(name = "stableSize", guard = "require_manage_or_controller")] -fn stable_size() -> u64 { - ic_cdk::api::stable::stable64_size() * WASM_PAGE_SIZE -} - -#[query(name = "stableRead", guard = "require_manage_or_controller")] -fn stable_read(offset: u64, length: u64) -> Vec { - let mut buffer = vec![0; length as usize]; - ic_cdk::api::stable::stable64_read(offset, &mut buffer); - buffer -} - -#[update(guard = "require_manage_or_controller")] -#[candid_method] -fn authorize(principal: Principal, auth: Auth) -> bool { - log!( - INFO, - "[{}] Authorizing `{:?}` for principal: {}", - ic_cdk::caller(), - auth, - principal - ); - do_authorize(principal, auth) -} - -#[query(name = "getAuthorized")] -#[candid_method(query, rename = "getAuthorized")] -fn get_authorized(auth: Auth) -> Vec { - AUTH.with(|a| { - let mut result = Vec::new(); - for (k, v) in a.borrow().iter() { - if v.is_authorized(auth) { - result.push(k.0); - } - } - result - }) -} - -#[update(guard = "require_manage_or_controller")] -#[candid_method] -fn deauthorize(principal: Principal, auth: Auth) -> bool { - log!( - INFO, - "[{}] Deauthorizing `{:?}` for principal: {}", - ic_cdk::caller(), - auth, - principal - ); - do_deauthorize(principal, auth) -} - -#[query(name = "getOpenRpcAccess", guard = "require_manage_or_controller")] -#[candid_method(query, rename = "getOpenRpcAccess")] -fn get_open_rpc_access() -> bool { - METADATA.with(|m| m.borrow().get().open_rpc_access) -} - -#[update(name = "setOpenRpcAccess", guard = "require_manage_or_controller")] -#[candid_method(rename = "setOpenRpcAccess")] -fn set_open_rpc_access(open_rpc_access: bool) { - log!( - INFO, - "[{}] Setting open RPC access to `{}`", - ic_cdk::caller(), - open_rpc_access - ); - METADATA.with(|m| { - let mut metadata = m.borrow().get().clone(); - metadata.open_rpc_access = open_rpc_access; - m.borrow_mut().set(metadata).unwrap(); - }); -} - #[cfg(not(any(target_arch = "wasm32", test)))] fn main() { candid::export_service!(); @@ -420,52 +319,57 @@ fn main() { #[cfg(any(target_arch = "wasm32", test))] fn main() {} -#[test] -fn test_candid_interface() { - fn source_to_str(source: &candid::utils::CandidSource) -> String { - match source { - candid::utils::CandidSource::File(f) => { - std::fs::read_to_string(f).unwrap_or_else(|_| "".to_string()) +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_candid_interface() { + fn source_to_str(source: &candid::utils::CandidSource) -> String { + match source { + candid::utils::CandidSource::File(f) => { + std::fs::read_to_string(f).unwrap_or_else(|_| "".to_string()) + } + candid::utils::CandidSource::Text(t) => t.to_string(), } - candid::utils::CandidSource::Text(t) => t.to_string(), } - } - fn check_service_compatible( - new_name: &str, - new: candid::utils::CandidSource, - old_name: &str, - old: candid::utils::CandidSource, - ) { - let new_str = source_to_str(&new); - let old_str = source_to_str(&old); - match candid::utils::service_compatible(new, old) { - Ok(_) => {} - Err(e) => { - eprintln!( - "{} is not compatible with {}!\n\n\ + fn check_service_compatible( + new_name: &str, + new: candid::utils::CandidSource, + old_name: &str, + old: candid::utils::CandidSource, + ) { + let new_str = source_to_str(&new); + let old_str = source_to_str(&old); + match candid::utils::service_compatible(new, old) { + Ok(_) => {} + Err(e) => { + eprintln!( + "{} is not compatible with {}!\n\n\ {}:\n\ {}\n\n\ {}:\n\ {}\n", - new_name, old_name, new_name, new_str, old_name, old_str - ); - panic!("{:?}", e); + new_name, old_name, new_name, new_str, old_name, old_str + ); + panic!("{:?}", e); + } } } - } - candid::export_service!(); - let new_interface = __export_service(); + candid::export_service!(); + let new_interface = __export_service(); - // check the public interface against the actual one - let old_interface = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("candid/evm_rpc.did"); + // check the public interface against the actual one + let old_interface = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("candid/evm_rpc.did"); - check_service_compatible( - "actual ledger candid interface", - candid::utils::CandidSource::Text(&new_interface), - "declared candid interface in evm_rpc.did file", - candid::utils::CandidSource::File(old_interface.as_path()), - ); + check_service_compatible( + "actual ledger candid interface", + candid::utils::CandidSource::Text(&new_interface), + "declared candid interface in evm_rpc.did file", + candid::utils::CandidSource::File(old_interface.as_path()), + ); + } } diff --git a/src/memory.rs b/src/memory.rs index 97c17e79..457d07ab 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,48 +1,110 @@ -use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory}; -#[cfg(target_arch = "wasm32")] -use ic_stable_structures::DefaultMemoryImpl; -#[cfg(not(target_arch = "wasm32"))] -use ic_stable_structures::VectorMemory; +use candid::Principal; +use ic_stable_structures::memory_manager::VirtualMemory; +use ic_stable_structures::{ + memory_manager::{MemoryId, MemoryManager}, + DefaultMemoryImpl, +}; use ic_stable_structures::{Cell, StableBTreeMap}; use std::cell::RefCell; -use crate::{ - constants::NODES_IN_FIDUCIARY_SUBNET, - types::{AuthSet, Metadata, Metrics, PrincipalStorable, Provider, StorableRpcService}, -}; +use crate::types::{ApiKey, BoolStorable, Metrics, PrincipalStorable, ProviderId}; + +const IS_DEMO_ACTIVE_ID: MemoryId = MemoryId::new(4); +const API_KEY_MAP_MEMORY_ID: MemoryId = MemoryId::new(5); +const MANAGE_API_KEYS_MEMORY_ID: MemoryId = MemoryId::new(6); -#[cfg(not(target_arch = "wasm32"))] -type Memory = VirtualMemory; -#[cfg(target_arch = "wasm32")] -type Memory = VirtualMemory; +type StableMemory = VirtualMemory; thread_local! { - // Unstable static data: this is reset when the canister is upgraded. + // Unstable static data: these are reset when the canister is upgraded. pub static UNSTABLE_METRICS: RefCell = RefCell::new(Metrics::default()); - static UNSTABLE_SUBNET_SIZE: RefCell = RefCell::new(NODES_IN_FIDUCIARY_SUBNET); - - // Stable static data: this is preserved when the canister is upgraded. - #[cfg(not(target_arch = "wasm32"))] - pub static MEMORY_MANAGER: RefCell> = - RefCell::new(MemoryManager::init(VectorMemory::new(RefCell::new(vec![])))); - #[cfg(target_arch = "wasm32")] - pub static MEMORY_MANAGER: RefCell> = + + // Stable static data: these are preserved when the canister is upgraded. + static MEMORY_MANAGER: RefCell> = RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); - pub static METADATA: RefCell> = RefCell::new(Cell::init( - MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))), - Metadata::default()).unwrap()); - pub static AUTH: RefCell> = RefCell::new( - StableBTreeMap::init(MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(1))))); - pub static PROVIDERS: RefCell> = RefCell::new( - StableBTreeMap::init(MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(2))))); - pub static SERVICE_PROVIDER_MAP: RefCell> = RefCell::new( - StableBTreeMap::init(MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(3))))); + static IS_DEMO_ACTIVE: RefCell> = + RefCell::new(Cell::init(MEMORY_MANAGER.with_borrow(|m| m.get(IS_DEMO_ACTIVE_ID)), BoolStorable(false)).expect("Unable to read demo status from stable memory")); + static API_KEY_MAP: RefCell> = + RefCell::new(StableBTreeMap::init(MEMORY_MANAGER.with_borrow(|m| m.get(API_KEY_MAP_MEMORY_ID)))); + static MANAGE_API_KEYS: RefCell> = + RefCell::new(ic_stable_structures::Vec::init(MEMORY_MANAGER.with_borrow(|m| m.get(MANAGE_API_KEYS_MEMORY_ID))).expect("Unable to read API key principals from stable memory")); +} + +pub fn get_api_key(provider_id: ProviderId) -> Option { + API_KEY_MAP.with_borrow_mut(|map| map.get(&provider_id)) +} + +pub fn insert_api_key(provider_id: ProviderId, api_key: ApiKey) { + API_KEY_MAP.with_borrow_mut(|map| map.insert(provider_id, api_key)); +} + +pub fn remove_api_key(provider_id: ProviderId) { + API_KEY_MAP.with_borrow_mut(|map| map.remove(&provider_id)); +} + +pub fn is_api_key_principal(principal: &Principal) -> bool { + MANAGE_API_KEYS.with_borrow(|principals| { + principals + .iter() + .any(|PrincipalStorable(other)| &other == principal) + }) +} + +pub fn set_api_key_principals(new_principals: Vec) { + MANAGE_API_KEYS.with_borrow_mut(|principals| { + while !principals.is_empty() { + principals.pop(); + } + for principal in new_principals { + principals + .push(&PrincipalStorable(principal)) + .expect("Error while adding API key principal"); + } + }); } -pub fn get_nodes_in_subnet() -> u32 { - UNSTABLE_SUBNET_SIZE.with_borrow(|n| *n) +pub fn is_demo_active() -> bool { + IS_DEMO_ACTIVE.with_borrow(|demo| demo.get().0) } -pub fn set_nodes_in_subnet(nodes_in_subnet: u32) { - UNSTABLE_SUBNET_SIZE.with_borrow_mut(|n| *n = nodes_in_subnet) +pub fn set_demo_active(is_active: bool) { + IS_DEMO_ACTIVE.with_borrow_mut(|demo| { + demo.set(BoolStorable(is_active)) + .expect("Error while storing new demo status") + }); +} + +#[cfg(test)] +mod test { + use candid::Principal; + + use crate::memory::{is_api_key_principal, set_api_key_principals}; + + #[test] + fn test_api_key_principals() { + let principal1 = + Principal::from_text("k5dlc-ijshq-lsyre-qvvpq-2bnxr-pb26c-ag3sc-t6zo5-rdavy-recje-zqe") + .unwrap(); + let principal2 = + Principal::from_text("yxhtl-jlpgx-wqnzc-ysego-h6yqe-3zwfo-o3grn-gvuhm-nz3kv-ainub-6ae") + .unwrap(); + assert!(!is_api_key_principal(&principal1)); + assert!(!is_api_key_principal(&principal2)); + + set_api_key_principals(vec![principal1]); + assert!(is_api_key_principal(&principal1)); + assert!(!is_api_key_principal(&principal2)); + + set_api_key_principals(vec![principal2]); + assert!(!is_api_key_principal(&principal1)); + assert!(is_api_key_principal(&principal2)); + + set_api_key_principals(vec![principal1, principal2]); + assert!(is_api_key_principal(&principal1)); + assert!(is_api_key_principal(&principal2)); + + set_api_key_principals(vec![]); + assert!(!is_api_key_principal(&principal1)); + assert!(!is_api_key_principal(&principal2)); + } } diff --git a/src/metrics.rs b/src/metrics.rs index 4931384b..c6446e7d 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -5,18 +5,17 @@ use crate::types::{MetricLabels, MetricValue}; #[macro_export] macro_rules! add_metric { ($metric:ident, $amount:expr) => {{ - $crate::memory::UNSTABLE_METRICS.with(|m| m.borrow_mut().$metric += $amount); + $crate::memory::UNSTABLE_METRICS.with_borrow_mut(|m| m.$metric += $amount); }}; } #[macro_export] macro_rules! add_metric_entry { ($metric:ident, $key:expr, $amount:expr) => {{ - $crate::memory::UNSTABLE_METRICS.with(|m| { + $crate::memory::UNSTABLE_METRICS.with_borrow_mut(|m| { let amount = $amount; if amount != 0 { - m.borrow_mut() - .$metric + m.$metric .entry($key) .and_modify(|counter| *counter += amount) .or_insert(amount); diff --git a/src/providers.rs b/src/providers.rs index 8ca2513b..663bbf8d 100644 --- a/src/providers.rs +++ b/src/providers.rs @@ -1,406 +1,302 @@ -use candid::{CandidType, Principal}; +use std::collections::HashMap; + use cketh_common::{ eth_rpc::ProviderError, eth_rpc_client::providers::{ EthMainnetService, EthSepoliaService, L2MainnetService, RpcApi, RpcService, }, - logs::INFO, }; -use ic_canister_log::log; use crate::{ - add_metric, - auth::do_deauthorize, constants::{ ARBITRUM_ONE_CHAIN_ID, BASE_MAINNET_CHAIN_ID, ETH_MAINNET_CHAIN_ID, ETH_SEPOLIA_CHAIN_ID, - MINIMUM_WITHDRAWAL_CYCLES, OPTIMISM_MAINNET_CHAIN_ID, - }, - memory::{METADATA, PROVIDERS, SERVICE_PROVIDER_MAP}, - types::{ - Auth, ManageProviderArgs, Provider, RegisterProviderArgs, ResolvedRpcService, - StorableRpcService, UpdateProviderArgs, + OPTIMISM_MAINNET_CHAIN_ID, }, - validate::{validate_credential_headers, validate_credential_path, validate_hostname}, + types::{Provider, ProviderId, ResolvedRpcService, RpcAccess, RpcAuth}, }; -pub const ANKR_HOSTNAME: &str = "rpc.ankr.com"; -pub const ALCHEMY_ETH_MAINNET_HOSTNAME: &str = "eth-mainnet.g.alchemy.com"; -pub const ALCHEMY_ETH_SEPOLIA_HOSTNAME: &str = "eth-sepolia.g.alchemy.com"; -pub const CLOUDFLARE_HOSTNAME: &str = "cloudflare-eth.com"; -pub const BLOCKPI_ETH_MAINNET_HOSTNAME: &str = "ethereum.blockpi.network"; -pub const BLOCKPI_ETH_SEPOLIA_HOSTNAME: &str = "ethereum-sepolia.blockpi.network"; -pub const PUBLICNODE_ETH_MAINNET_HOSTNAME: &str = "ethereum-rpc.publicnode.com"; -pub const PUBLICNODE_ETH_SEPOLIA_HOSTNAME: &str = "ethereum-sepolia-rpc.publicnode.com"; -pub const ETH_SEPOLIA_HOSTNAME: &str = "rpc.sepolia.org"; -pub const ALCHEMY_ARBITRUM_ONE_HOSTNAME: &str = "arb-mainnet.g.alchemy.com"; -pub const BLOCKPI_ARBITRUM_ONE_HOSTNAME: &str = "arbitrum.blockpi.network"; -pub const PUBLICNODE_ARBITRUM_ONE_HOSTNAME: &str = "arbitrum-one-rpc.publicnode.com"; -pub const ALCHEMY_BASE_MAINNET_HOSTNAME: &str = "base-mainnet.g.alchemy.com"; -pub const BLOCKPI_BASE_MAINNET_HOSTNAME: &str = "base.blockpi.network"; -pub const PUBLICNODE_BASE_MAINNET_HOSTNAME: &str = "base-rpc.publicnode.com"; -pub const ALCHEMY_OPT_MAINNET_HOSTNAME: &str = "opt-mainnet.g.alchemy.com"; -pub const BLOCKPI_OPTIMISM_MAINNET_HOSTNAME: &str = "optimism.blockpi.network"; -pub const PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME: &str = "optimism-rpc.publicnode.com"; -pub const LLAMA_ETH_MAINNET_HOSTNAME: &str = "eth.llamarpc.com"; -pub const LLAMA_ARBITRUM_ONE_HOSTNAME: &str = "arbitrum.llamarpc.com"; -pub const LLAMA_BASE_MAINNET_HOSTNAME: &str = "base.llamarpc.com"; -pub const LLAMA_OPTIMISM_MAINNET_HOSTNAME: &str = "optimism.llamarpc.com"; - -// Limited API credentials for local testing. -// Use `dfx canister call evm_rpc updateProvider ...` to pass your own keys. -pub const ALCHEMY_ETH_MAINNET_CREDENTIAL: &str = "/v2/zBxaSBUMfuH8XnA-uLIWeXfCx1T8ItkM"; -pub const ALCHEMY_ETH_SEPOLIA_CREDENTIAL: &str = "/v2/Mbow19DWsfPXiTpdgvRu4HQq63iYycU-"; -pub const ALCHEMY_ARBITRUM_ONE_CREDENTIAL: &str = "/v2"; -pub const ALCHEMY_BASE_MAINNET_CREDENTIAL: &str = "/v2"; -pub const ALCHEMY_OPTIMISM_MAINNET_CREDENTIAL: &str = "/v2"; -pub const BLOCKPI_ETH_MAINNET_CREDENTIAL: &str = "/v1/rpc/0edc81e20be23ddff051f61a97bb457ec7284a58"; -pub const BLOCKPI_ETH_SEPOLIA_CREDENTIAL: &str = "/v1/rpc/1fe987fddded17db50862311720ff444991d4dab"; -pub const BLOCKPI_ARBITRUM_ONE_CREDENTIAL: &str = - "/v1/rpc/a8b89a41d2a341e32ee7aefcb20820a7cbb65f35"; -pub const BLOCKPI_BASE_MAINNET_CREDENTIAL: &str = - "/v1/rpc/bd458bf9f28ed45c77823814a937c812d2efd260"; -pub const BLOCKPI_OPTIMISM_MAINNET_CREDENTIAL: &str = - "/v1/rpc/d54bfe59299d56b0cbb8b3c69bd122f4ab5ac654"; - -pub fn get_default_providers() -> Vec { - vec![ - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: CLOUDFLARE_HOSTNAME.to_string(), - credential_path: "/v1/mainnet".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, +pub const PROVIDERS: &[Provider] = &[ + Provider { + provider_id: 0, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://cloudflare-eth.com/v1/mainnet", + }, + public_url: Some("https://cloudflare-eth.com/v1/mainnet"), }, - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "/eth".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::Cloudflare)), + }, + Provider { + provider_id: 1, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://rpc.ankr.com/eth/{API_KEY}", + }, + public_url: Some("https://rpc.ankr.com/eth"), }, - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: PUBLICNODE_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::Ankr)), + }, + Provider { + provider_id: 2, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://ethereum-rpc.publicnode.com", }, - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: BLOCKPI_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: BLOCKPI_ETH_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::PublicNode)), + }, + Provider { + provider_id: 3, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://ethereum.blockpi.network/v1/rpc/{API_KEY}", + }, + public_url: Some("https://ethereum.blockpi.network/v1/rpc/public"), }, - RegisterProviderArgs { - chain_id: ETH_SEPOLIA_CHAIN_ID, - hostname: ETH_SEPOLIA_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::BlockPi)), + }, + Provider { + provider_id: 4, + chain_id: ETH_SEPOLIA_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://rpc.sepolia.org", }, - RegisterProviderArgs { - chain_id: ETH_SEPOLIA_CHAIN_ID, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "/eth_sepolia".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthSepolia(EthSepoliaService::Sepolia)), + }, + Provider { + provider_id: 5, + chain_id: ETH_SEPOLIA_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://rpc.ankr.com/eth_sepolia/{API_KEY}", + }, + public_url: Some("https://rpc.ankr.com/eth_sepolia"), }, - RegisterProviderArgs { - chain_id: ETH_SEPOLIA_CHAIN_ID, - hostname: BLOCKPI_ETH_SEPOLIA_HOSTNAME.to_string(), - credential_path: BLOCKPI_ETH_SEPOLIA_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthSepolia(EthSepoliaService::Ankr)), + }, + Provider { + provider_id: 6, + chain_id: ETH_SEPOLIA_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://ethereum-sepolia.blockpi.network/v1/rpc/{API_KEY}", + }, + public_url: Some("https://ethereum-sepolia.blockpi.network/v1/rpc/public"), }, - RegisterProviderArgs { - chain_id: ETH_SEPOLIA_CHAIN_ID, - hostname: PUBLICNODE_ETH_SEPOLIA_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthSepolia(EthSepoliaService::BlockPi)), + }, + Provider { + provider_id: 7, + chain_id: ETH_SEPOLIA_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://ethereum-sepolia-rpc.publicnode.com", }, - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: ALCHEMY_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: ALCHEMY_ETH_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthSepolia(EthSepoliaService::PublicNode)), + }, + Provider { + provider_id: 8, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://eth-mainnet.g.alchemy.com/v2", + }, + public_url: Some("https://eth-mainnet.g.alchemy.com/v2/demo"), }, - RegisterProviderArgs { - chain_id: ETH_SEPOLIA_CHAIN_ID, - hostname: ALCHEMY_ETH_SEPOLIA_HOSTNAME.to_string(), - credential_path: ALCHEMY_ETH_SEPOLIA_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::Alchemy)), + }, + Provider { + provider_id: 9, + chain_id: ETH_SEPOLIA_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://eth-sepolia.g.alchemy.com/v2", + }, + public_url: Some("https://eth-sepolia.g.alchemy.com/v2/demo"), }, - RegisterProviderArgs { - chain_id: ARBITRUM_ONE_CHAIN_ID, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "/arbitrum".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthSepolia(EthSepoliaService::Alchemy)), + }, + Provider { + provider_id: 10, + chain_id: ARBITRUM_ONE_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://rpc.ankr.com/arbitrum/{API_KEY}", + }, + public_url: Some("https://rpc.ankr.com/arbitrum"), }, - RegisterProviderArgs { - chain_id: ARBITRUM_ONE_CHAIN_ID, - hostname: ALCHEMY_ARBITRUM_ONE_HOSTNAME.to_string(), - credential_path: ALCHEMY_ARBITRUM_ONE_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::ArbitrumOne(L2MainnetService::Ankr)), + }, + Provider { + provider_id: 11, + chain_id: ARBITRUM_ONE_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://arb-mainnet.g.alchemy.com/v2", + }, + public_url: Some("https://arb-mainnet.g.alchemy.com/v2/demo"), }, - RegisterProviderArgs { - chain_id: ARBITRUM_ONE_CHAIN_ID, - hostname: BLOCKPI_ARBITRUM_ONE_HOSTNAME.to_string(), - credential_path: BLOCKPI_ARBITRUM_ONE_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::ArbitrumOne(L2MainnetService::Alchemy)), + }, + Provider { + provider_id: 12, + chain_id: ARBITRUM_ONE_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://arbitrum.blockpi.network/v1/rpc/{API_KEY}", + }, + public_url: Some("https://arbitrum.blockpi.network/v1/rpc/public"), }, - RegisterProviderArgs { - chain_id: ARBITRUM_ONE_CHAIN_ID, - hostname: PUBLICNODE_ARBITRUM_ONE_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::ArbitrumOne(L2MainnetService::BlockPi)), + }, + Provider { + provider_id: 13, + chain_id: ARBITRUM_ONE_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://arbitrum-one-rpc.publicnode.com", }, - RegisterProviderArgs { - chain_id: BASE_MAINNET_CHAIN_ID, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "/base".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::ArbitrumOne(L2MainnetService::PublicNode)), + }, + Provider { + provider_id: 14, + chain_id: BASE_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://rpc.ankr.com/base/{API_KEY}", + }, + public_url: Some("https://rpc.ankr.com/base"), }, - RegisterProviderArgs { - chain_id: BASE_MAINNET_CHAIN_ID, - hostname: ALCHEMY_BASE_MAINNET_HOSTNAME.to_string(), - credential_path: ALCHEMY_BASE_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::BaseMainnet(L2MainnetService::Ankr)), + }, + Provider { + provider_id: 15, + chain_id: BASE_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://base-mainnet.g.alchemy.com/v2", + }, + public_url: Some("https://base-mainnet.g.alchemy.com/v2/demo"), }, - RegisterProviderArgs { - chain_id: BASE_MAINNET_CHAIN_ID, - hostname: BLOCKPI_BASE_MAINNET_HOSTNAME.to_string(), - credential_path: BLOCKPI_BASE_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::BaseMainnet(L2MainnetService::Alchemy)), + }, + Provider { + provider_id: 16, + chain_id: BASE_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://base.blockpi.network/v1/rpc/{API_KEY}", + }, + public_url: Some("https://base.blockpi.network/v1/rpc/public"), }, - RegisterProviderArgs { - chain_id: BASE_MAINNET_CHAIN_ID, - hostname: PUBLICNODE_BASE_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::BaseMainnet(L2MainnetService::BlockPi)), + }, + Provider { + provider_id: 17, + chain_id: BASE_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://base-rpc.publicnode.com", }, - RegisterProviderArgs { - chain_id: OPTIMISM_MAINNET_CHAIN_ID, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "/optimism".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::BaseMainnet(L2MainnetService::PublicNode)), + }, + Provider { + provider_id: 18, + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://rpc.ankr.com/optimism/{API_KEY}", + }, + public_url: Some("https://rpc.ankr.com/optimism"), }, - RegisterProviderArgs { - chain_id: OPTIMISM_MAINNET_CHAIN_ID, - hostname: ALCHEMY_OPT_MAINNET_HOSTNAME.to_string(), - credential_path: ALCHEMY_OPTIMISM_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::OptimismMainnet(L2MainnetService::Ankr)), + }, + Provider { + provider_id: 19, + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::BearerToken { + url: "https://opt-mainnet.g.alchemy.com/v2", + }, + public_url: Some("https://opt-mainnet.g.alchemy.com/v2/demo"), }, - RegisterProviderArgs { - chain_id: OPTIMISM_MAINNET_CHAIN_ID, - hostname: BLOCKPI_OPTIMISM_MAINNET_HOSTNAME.to_string(), - credential_path: BLOCKPI_OPTIMISM_MAINNET_CREDENTIAL.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::OptimismMainnet(L2MainnetService::Alchemy)), + }, + Provider { + provider_id: 20, + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + access: RpcAccess::Authenticated { + auth: RpcAuth::UrlParameter { + url_pattern: "https://optimism.blockpi.network/v1/rpc/{API_KEY}", + }, + public_url: Some("https://optimism.blockpi.network/v1/rpc/public"), }, - RegisterProviderArgs { - chain_id: OPTIMISM_MAINNET_CHAIN_ID, - hostname: PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::OptimismMainnet(L2MainnetService::BlockPi)), + }, + Provider { + provider_id: 21, + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://optimism-rpc.publicnode.com", }, - RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: LLAMA_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::OptimismMainnet(L2MainnetService::PublicNode)), + }, + Provider { + provider_id: 22, + chain_id: ETH_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://eth.llamarpc.com", }, - RegisterProviderArgs { - chain_id: ARBITRUM_ONE_CHAIN_ID, - hostname: LLAMA_ARBITRUM_ONE_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::EthMainnet(EthMainnetService::Llama)), + }, + Provider { + provider_id: 23, + chain_id: ARBITRUM_ONE_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://arbitrum.llamarpc.com", }, - RegisterProviderArgs { - chain_id: BASE_MAINNET_CHAIN_ID, - hostname: LLAMA_BASE_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::ArbitrumOne(L2MainnetService::Llama)), + }, + Provider { + provider_id: 24, + chain_id: BASE_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://base.llamarpc.com", }, - RegisterProviderArgs { - chain_id: OPTIMISM_MAINNET_CHAIN_ID, - hostname: LLAMA_OPTIMISM_MAINNET_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, + alias: Some(RpcService::BaseMainnet(L2MainnetService::Llama)), + }, + Provider { + provider_id: 25, + chain_id: OPTIMISM_MAINNET_CHAIN_ID, + access: RpcAccess::Unauthenticated { + public_url: "https://optimism.llamarpc.com", }, - ] -} + alias: Some(RpcService::OptimismMainnet(L2MainnetService::Llama)), + }, +]; -pub fn get_default_service_provider_hostnames() -> Vec<(RpcService, &'static str)> { - vec![ - ( - RpcService::EthMainnet(EthMainnetService::Alchemy), - ALCHEMY_ETH_MAINNET_HOSTNAME, - ), - ( - RpcService::EthMainnet(EthMainnetService::Ankr), - ANKR_HOSTNAME, - ), - ( - RpcService::EthMainnet(EthMainnetService::BlockPi), - BLOCKPI_ETH_MAINNET_HOSTNAME, - ), - ( - RpcService::EthMainnet(EthMainnetService::Cloudflare), - CLOUDFLARE_HOSTNAME, - ), - ( - RpcService::EthMainnet(EthMainnetService::PublicNode), - PUBLICNODE_ETH_MAINNET_HOSTNAME, - ), - ( - RpcService::EthSepolia(EthSepoliaService::Alchemy), - ALCHEMY_ETH_SEPOLIA_HOSTNAME, - ), - ( - RpcService::EthSepolia(EthSepoliaService::Ankr), - ANKR_HOSTNAME, - ), - ( - RpcService::EthSepolia(EthSepoliaService::BlockPi), - BLOCKPI_ETH_SEPOLIA_HOSTNAME, - ), - ( - RpcService::EthSepolia(EthSepoliaService::PublicNode), - PUBLICNODE_ETH_SEPOLIA_HOSTNAME, - ), - ( - RpcService::ArbitrumOne(L2MainnetService::Alchemy), - ALCHEMY_ARBITRUM_ONE_HOSTNAME, - ), - ( - RpcService::ArbitrumOne(L2MainnetService::Ankr), - ANKR_HOSTNAME, - ), - ( - RpcService::ArbitrumOne(L2MainnetService::BlockPi), - BLOCKPI_ARBITRUM_ONE_HOSTNAME, - ), - ( - RpcService::ArbitrumOne(L2MainnetService::PublicNode), - PUBLICNODE_ARBITRUM_ONE_HOSTNAME, - ), - ( - RpcService::BaseMainnet(L2MainnetService::Alchemy), - ALCHEMY_BASE_MAINNET_HOSTNAME, - ), - ( - RpcService::BaseMainnet(L2MainnetService::Ankr), - ANKR_HOSTNAME, - ), - ( - RpcService::BaseMainnet(L2MainnetService::BlockPi), - BLOCKPI_BASE_MAINNET_HOSTNAME, - ), - ( - RpcService::BaseMainnet(L2MainnetService::PublicNode), - PUBLICNODE_BASE_MAINNET_HOSTNAME, - ), - ( - RpcService::OptimismMainnet(L2MainnetService::Alchemy), - ALCHEMY_OPT_MAINNET_HOSTNAME, - ), - ( - RpcService::OptimismMainnet(L2MainnetService::Ankr), - ANKR_HOSTNAME, - ), - ( - RpcService::OptimismMainnet(L2MainnetService::BlockPi), - BLOCKPI_OPTIMISM_MAINNET_HOSTNAME, - ), - ( - RpcService::OptimismMainnet(L2MainnetService::PublicNode), - PUBLICNODE_OPTIMISM_MAINNET_HOSTNAME, - ), - ( - RpcService::EthMainnet(EthMainnetService::Llama), - LLAMA_ETH_MAINNET_HOSTNAME, - ), - ( - RpcService::ArbitrumOne(L2MainnetService::Llama), - LLAMA_ARBITRUM_ONE_HOSTNAME, - ), - ( - RpcService::BaseMainnet(L2MainnetService::Llama), - LLAMA_BASE_MAINNET_HOSTNAME, - ), - ( - RpcService::OptimismMainnet(L2MainnetService::Llama), - LLAMA_OPTIMISM_MAINNET_HOSTNAME, - ), - ] +thread_local! { + pub static PROVIDER_MAP: HashMap = + PROVIDERS.iter() + .map(|provider| (provider.provider_id, provider.clone())).collect(); + + pub static SERVICE_PROVIDER_MAP: HashMap = + PROVIDERS.iter() + .filter_map(|provider| Some((provider.alias.clone()?, provider.provider_id))) + .collect(); } -pub fn find_provider(f: impl Fn(&Provider) -> bool) -> Option { - PROVIDERS.with(|providers| { - let providers = providers.borrow(); - Some( - providers - .iter() - .find(|(_, p)| p.primary && f(p)) - .or_else(|| providers.iter().find(|(_, p)| f(p)))? - .1, - ) - }) +pub fn find_provider(f: impl Fn(&Provider) -> bool) -> Option<&'static Provider> { + PROVIDERS.iter().find(|&provider| f(provider)) } fn lookup_provider_for_service(service: &RpcService) -> Result { let provider_id = SERVICE_PROVIDER_MAP.with(|map| { - map.borrow() - .get(&StorableRpcService::new(service)) + map.get(service) + .copied() .ok_or(ProviderError::MissingRequiredProvider) })?; - PROVIDERS - .with(|providers| providers.borrow().get(&provider_id)) + PROVIDER_MAP + .with(|map| map.get(&provider_id).cloned()) .ok_or(ProviderError::ProviderNotFound) } @@ -417,255 +313,18 @@ pub fn get_known_chain_id(service: &RpcService) -> Option { } } -pub fn do_register_provider(caller: Principal, args: RegisterProviderArgs) -> u64 { - validate_hostname(&args.hostname).unwrap(); - validate_credential_path(&args.credential_path).unwrap(); - let provider_id = METADATA.with(|m| { - let mut metadata = m.borrow().get().clone(); - let id = metadata.next_provider_id; - metadata.next_provider_id += 1; - m.borrow_mut().set(metadata).unwrap(); - id - }); - do_deauthorize(caller, Auth::RegisterProvider); - log!(INFO, "[{}] Registering provider: {:?}", caller, provider_id); - PROVIDERS.with(|providers| { - providers.borrow_mut().insert( - provider_id, - Provider { - provider_id, - owner: caller, - chain_id: args.chain_id, - hostname: args.hostname, - credential_path: args.credential_path, - credential_headers: args.credential_headers.unwrap_or_default(), - cycles_per_call: args.cycles_per_call, - cycles_per_message_byte: args.cycles_per_message_byte, - cycles_owed: 0, - primary: false, - }, - ) - }); - provider_id -} - -pub fn do_unregister_provider(caller: Principal, is_controller: bool, provider_id: u64) -> bool { - PROVIDERS.with(|providers| { - let mut providers = providers.borrow_mut(); - if let Some(provider) = providers.get(&provider_id) { - if provider.owner == caller || is_controller { - log!( - INFO, - "[{}] Unregistering provider: {:?}", - caller, - provider_id - ); - providers.remove(&provider_id).is_some() - } else { - ic_cdk::trap("You are not authorized: check provider owner"); - } - } else { - false - } - }) -} - -/// Changes provider details. The caller must be the owner of the provider. -pub fn do_update_provider(caller: Principal, is_controller: bool, args: UpdateProviderArgs) { - PROVIDERS.with(|providers| { - let mut providers = providers.borrow_mut(); - match providers.get(&args.provider_id) { - Some(mut provider) => { - if provider.owner == caller || is_controller { - log!(INFO, "[{}] Updating provider: {}", caller, args.provider_id); - if let Some(path) = args.credential_path { - validate_credential_path(&path).unwrap(); - provider.credential_path = path; - } - if let Some(headers) = args.credential_headers { - validate_credential_headers(&headers).unwrap(); - provider.credential_headers = headers; - } - if let Some(cycles_per_call) = args.cycles_per_call { - provider.cycles_per_call = cycles_per_call; - } - if let Some(cycles_per_message_byte) = args.cycles_per_message_byte { - provider.cycles_per_message_byte = cycles_per_message_byte; - } - providers.insert(args.provider_id, provider); - } else { - ic_cdk::trap("You are not authorized: check provider owner"); - } - } - None => ic_cdk::trap("Provider not found"), - } - }); -} - -/// Changes administrative details for a provider. The caller must have the `Auth::Manage` permission. -pub fn do_manage_provider(args: ManageProviderArgs) { - PROVIDERS.with(|providers| { - let mut providers = providers.borrow_mut(); - match providers.get(&args.provider_id) { - Some(mut provider) => { - if let Some(chain_id) = args.chain_id { - log!( - INFO, - "Updating provider {:?} to use chain id: {} (original value: {})", - provider.provider_id, - chain_id, - provider.chain_id, - ); - provider.chain_id = chain_id; - } - if let Some(primary) = args.primary { - log!( - INFO, - "Updating provider {:?} to use primary status: {} (original value: {})", - provider.provider_id, - primary, - provider.primary, - ); - provider.primary = primary; - } - if let Some(service) = args.service { - set_service_provider(&service, &provider); - } - providers.insert(args.provider_id, provider); - } - None => ic_cdk::trap("Provider not found"), - } - }) -} - -pub fn do_get_accumulated_cycle_count( - caller: Principal, - is_controller: bool, - provider_id: u64, -) -> u128 { - let provider = PROVIDERS - .with(|p| { - p.borrow() - .get(&provider_id) - .ok_or(ProviderError::ProviderNotFound) - }) - .expect("Provider not found"); - if caller == provider.owner || is_controller { - provider.cycles_owed - } else { - ic_cdk::trap("You are not authorized: check provider owner"); - } -} - -pub async fn do_withdraw_accumulated_cycles( - caller: Principal, - is_controller: bool, - provider_id: u64, - canister_id: Principal, -) { - let mut provider = PROVIDERS - .with(|p| { - p.borrow() - .get(&provider_id) - .ok_or(ProviderError::ProviderNotFound) - }) - .expect("Provider not found"); - if caller == provider.owner || is_controller { - let amount = provider.cycles_owed; - if amount < MINIMUM_WITHDRAWAL_CYCLES { - ic_cdk::trap("Too few cycles to withdraw"); - } - PROVIDERS.with(|p| { - provider.cycles_owed = 0; - p.borrow_mut().insert(provider_id, provider) - }); - log!( - INFO, - "[{}] Withdrawing {} cycles from provider {} to canister: {}", - caller, - amount, - provider_id, - canister_id, - ); - #[derive(CandidType)] - struct DepositCyclesArgs { - canister_id: Principal, - } - match ic_cdk::api::call::call_with_payment128( - Principal::management_canister(), - "deposit_cycles", - (DepositCyclesArgs { canister_id },), - amount, - ) - .await - { - Ok(()) => add_metric!(cycles_withdrawn, amount), - Err(err) => { - // Refund on failure to send cycles - log!( - INFO, - "[{}] Unable to send {} cycles from provider {}: {:?}", - canister_id, - amount, - provider_id, - err - ); - // Protect against re-entrancy - let provider = PROVIDERS.with(|p| { - p.borrow() - .get(&provider_id) - .ok_or(ProviderError::ProviderNotFound) - }); - let mut provider = provider.expect("Provider not found during refund, cycles lost"); - PROVIDERS.with(|p| { - provider.cycles_owed += amount; - p.borrow_mut().insert(provider_id, provider) - }); - } - }; - } else { - ic_cdk::trap("You are not authorized: check provider owner"); - } -} - -pub fn set_service_provider(service: &RpcService, provider: &Provider) { - log!( - INFO, - "Updating service {:?} to use provider: {}", - service, - provider.provider_id - ); - if let Some(chain_id) = get_known_chain_id(service) { - if chain_id != provider.chain_id { - ic_cdk::trap(&format!( - "Mismatch between service and provider chain ids ({} != {})", - chain_id, provider.chain_id - )) - } - } - SERVICE_PROVIDER_MAP.with(|mappings| { - mappings - .borrow_mut() - .insert(StorableRpcService::new(service), provider.provider_id); - }); -} - pub fn resolve_rpc_service(service: RpcService) -> Result { Ok(match service { - RpcService::Chain(id) => ResolvedRpcService::Provider(PROVIDERS.with(|providers| { - let providers = providers.borrow(); - Ok(providers - .iter() - .find(|(_, p)| p.primary && p.chain_id == id) - .or_else(|| providers.iter().find(|(_, p)| p.chain_id == id)) + RpcService::Chain(id) => ResolvedRpcService::Provider( + find_provider(|p| p.chain_id == id) .ok_or(ProviderError::ProviderNotFound)? - .1) - })?), + .clone(), + ), RpcService::Provider(id) => ResolvedRpcService::Provider({ - PROVIDERS.with(|providers| { - providers - .borrow() + PROVIDER_MAP.with(|provider_map| { + provider_map .get(&id) + .cloned() .ok_or(ProviderError::ProviderNotFound) })? }), @@ -689,3 +348,88 @@ pub fn resolve_rpc_service(service: RpcService) -> Result { + match auth { + RpcAuth::BearerToken { url } => assert_not_url_pattern(url, provider), + RpcAuth::UrlParameter { url_pattern } => { + assert_url_pattern(url_pattern, provider) + } + } + if let Some(public_url) = public_url { + assert_not_url_pattern(public_url, provider); + } + } + RpcAccess::Unauthenticated { public_url } => { + assert_not_url_pattern(public_url, provider); + } + } + } + } + + #[test] + fn test_no_duplicate_service_providers() { + SERVICE_PROVIDER_MAP.with(|map| { + assert_eq!( + map.len(), + map.keys().collect::>().len(), + "Duplicate service in mapping" + ); + assert_eq!( + map.len(), + map.values().collect::>().len(), + "Duplicate provider in mapping" + ); + }) + } + + #[test] + fn test_service_provider_coverage() { + SERVICE_PROVIDER_MAP.with(|map| { + let inverse_map: HashMap<_, _> = map.iter().map(|(k, v)| (v, k)).collect(); + for provider in PROVIDERS { + assert!( + inverse_map.contains_key(&provider.provider_id), + "Missing service mapping for provider with ID: {}", + provider.provider_id + ); + } + }) + } +} diff --git a/src/types.rs b/src/types.rs index 157add7b..bbc3c14f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use candid::{CandidType, Decode, Deserialize, Encode, Principal}; +use candid::{CandidType, Principal}; use cketh_common::eth_rpc::RpcError; use cketh_common::eth_rpc_client::providers::{ EthMainnetService, EthSepoliaService, L2MainnetService, RpcApi, RpcService, @@ -6,20 +6,23 @@ use cketh_common::eth_rpc_client::providers::{ use ic_cdk::api::management_canister::http_request::HttpHeader; use ic_stable_structures::{BoundedStorable, Storable}; +use serde::{Deserialize, Serialize}; +use zeroize::{Zeroize, ZeroizeOnDrop}; -use serde::Serialize; use std::borrow::Cow; use std::collections::HashMap; +use std::fmt; -use crate::constants::{ - AUTH_SET_STORABLE_MAX_SIZE, DEFAULT_OPEN_RPC_ACCESS, PROVIDER_MAX_SIZE, RPC_SERVICE_MAX_SIZE, - STRING_STORABLE_MAX_SIZE, -}; +use crate::constants::{API_KEY_MAX_SIZE, API_KEY_REPLACE_STRING, STRING_STORABLE_MAX_SIZE}; +use crate::memory::get_api_key; +use crate::util::hostname_from_url; +use crate::validate::validate_api_key; #[derive(Clone, Debug, CandidType, Deserialize)] pub struct InitArgs { - #[serde(rename = "nodesInSubnet")] - pub nodes_in_subnet: u32, + pub demo: Option, + #[serde(rename = "manageApiKeys")] + pub manage_api_keys: Option>, } pub enum ResolvedRpcService { @@ -165,91 +168,26 @@ impl RpcMethod { } } -#[derive(Clone, Copy, Debug, PartialEq, CandidType, Serialize, Deserialize)] -pub enum Auth { - Manage, - RegisterProvider, - PriorityRpc, - FreeRpc, -} - -#[derive(Clone, Debug, PartialEq, CandidType, Serialize, Deserialize, Default)] -pub struct AuthSet(Vec); - -impl AuthSet { - pub fn new(auths: Vec) -> Self { - let mut auth_set = Self(Vec::with_capacity(auths.len())); - for auth in auths { - // Deduplicate - auth_set.authorize(auth); - } - auth_set - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn is_authorized(&self, auth: Auth) -> bool { - self.0.contains(&auth) - } - - pub fn authorize(&mut self, auth: Auth) -> bool { - if !self.is_authorized(auth) { - self.0.push(auth); - true - } else { - false - } - } - - pub fn deauthorize(&mut self, auth: Auth) -> bool { - if let Some(index) = self.0.iter().position(|a| *a == auth) { - self.0.remove(index); - true - } else { - false - } - } -} +#[derive(Clone, PartialEq, Eq)] +pub struct BoolStorable(pub bool); -// Using explicit JSON representation in place of enum indices for security -impl Storable for AuthSet { +impl Storable for BoolStorable { fn from_bytes(bytes: Cow<[u8]>) -> Self { - serde_json::from_slice(&bytes).expect("Unable to deserialize AuthSet") + assert!( + bytes.len() == 1, + "Unexpected byte length for `BoolStorable`" + ); + BoolStorable(bytes[0] == 0) } fn to_bytes(&self) -> Cow<[u8]> { - Cow::Owned(serde_json::to_vec(self).expect("Unable to serialize AuthSet")) + vec![self.0 as u8].into() } } -impl BoundedStorable for AuthSet { - const MAX_SIZE: u32 = AUTH_SET_STORABLE_MAX_SIZE; - const IS_FIXED_SIZE: bool = false; -} - -#[derive(Clone, Debug, CandidType, Deserialize)] -pub struct Metadata { - pub next_provider_id: u64, - pub open_rpc_access: bool, -} - -impl Default for Metadata { - fn default() -> Self { - Self { - next_provider_id: 0, - open_rpc_access: DEFAULT_OPEN_RPC_ACCESS, - } - } -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct StringStorable(pub String); -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct PrincipalStorable(pub Principal); - impl Storable for StringStorable { fn to_bytes(&self) -> Cow<[u8]> { // String already implements `Storable`. @@ -266,6 +204,9 @@ impl BoundedStorable for StringStorable { const IS_FIXED_SIZE: bool = false; } +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PrincipalStorable(pub Principal); + impl Storable for PrincipalStorable { fn to_bytes(&self) -> Cow<[u8]> { Cow::from(self.0.as_slice()) @@ -281,165 +222,153 @@ impl BoundedStorable for PrincipalStorable { const IS_FIXED_SIZE: bool = false; } -#[derive(Clone, Debug, Eq, PartialEq, CandidType, Deserialize)] -pub struct ProviderView { - #[serde(rename = "providerId")] - pub provider_id: u64, - pub owner: Principal, - #[serde(rename = "chainId")] - pub chain_id: u64, - pub hostname: String, - #[serde(rename = "cyclesPerCall")] - pub cycles_per_call: u64, - #[serde(rename = "cyclesPerMessageByte")] - pub cycles_per_message_byte: u64, - pub primary: bool, -} - -impl From for ProviderView { - fn from(provider: Provider) -> Self { - ProviderView { - provider_id: provider.provider_id, - owner: provider.owner, - chain_id: provider.chain_id, - hostname: provider.hostname, - cycles_per_call: provider.cycles_per_call, - cycles_per_message_byte: provider.cycles_per_message_byte, - primary: provider.primary, - } - } -} +#[derive(Zeroize, ZeroizeOnDrop)] +pub struct ApiKey(String); -#[derive(Clone, CandidType, Deserialize)] -pub struct RegisterProviderArgs { - #[serde(rename = "chainId")] - pub chain_id: u64, - pub hostname: String, - #[serde(rename = "credentialPath")] - pub credential_path: String, - #[serde(rename = "credentialHeaders")] - pub credential_headers: Option>, - #[serde(rename = "cyclesPerCall")] - pub cycles_per_call: u64, - #[serde(rename = "cyclesPerMessageByte")] - pub cycles_per_message_byte: u64, -} - -#[derive(Clone, CandidType, Deserialize)] -pub struct UpdateProviderArgs { - #[serde(rename = "providerId")] - pub provider_id: u64, - #[serde(rename = "credentialPath")] - pub credential_path: Option, - #[serde(rename = "credentialHeaders")] - pub credential_headers: Option>, - #[serde(rename = "cyclesPerCall")] - pub cycles_per_call: Option, - #[serde(rename = "cyclesPerMessageByte")] - pub cycles_per_message_byte: Option, -} - -#[derive(Clone, Debug, CandidType, Deserialize)] -pub struct ManageProviderArgs { - #[serde(rename = "providerId")] - pub provider_id: u64, - #[serde(rename = "chainId")] - pub chain_id: Option, - pub primary: Option, - pub service: Option, -} - -#[derive(Clone, CandidType, Deserialize)] -pub struct Provider { - #[serde(rename = "providerId")] - pub provider_id: u64, - pub owner: Principal, - #[serde(rename = "chainId")] - pub chain_id: u64, - pub hostname: String, - #[serde(rename = "credentialPath")] - pub credential_path: String, - #[serde(rename = "credentialHeaders")] - pub credential_headers: Vec, - #[serde(rename = "cyclesPerCall")] - pub cycles_per_call: u64, - #[serde(rename = "cyclesPerMessageByte")] - pub cycles_per_message_byte: u64, - #[serde(rename = "cyclesOwed")] - pub cycles_owed: u128, - pub primary: bool, +impl ApiKey { + /// Explicitly read API key (use sparingly) + pub fn read(&self) -> &str { + &self.0 + } } -impl Provider { - pub fn api(&self) -> RpcApi { - RpcApi { - url: format!("https://{}{}", self.hostname, self.credential_path), - headers: if self.credential_headers.is_empty() { - None - } else { - Some(self.credential_headers.clone()) - }, - } +// Enable printing data structures which include an API key +impl fmt::Debug for ApiKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{API_KEY_REPLACE_STRING}") } } -impl Storable for Metadata { - fn to_bytes(&self) -> Cow<[u8]> { - Cow::Owned(Encode!(self).unwrap()) - } - fn from_bytes(bytes: Cow<[u8]>) -> Self { - Decode!(&bytes, Self).unwrap() +impl TryFrom for ApiKey { + type Error = String; + fn try_from(key: String) -> Result { + validate_api_key(&key)?; + Ok(ApiKey(key)) } } -impl Storable for Provider { +impl Storable for ApiKey { fn to_bytes(&self) -> Cow<[u8]> { - Cow::Owned(Encode!(self).unwrap()) + self.0.to_bytes() } + fn from_bytes(bytes: Cow<[u8]>) -> Self { - Decode!(&bytes, Self).unwrap() + Self(String::from_bytes(bytes)) } } -impl BoundedStorable for Provider { - const MAX_SIZE: u32 = PROVIDER_MAX_SIZE; +impl BoundedStorable for ApiKey { + const MAX_SIZE: u32 = API_KEY_MAX_SIZE; const IS_FIXED_SIZE: bool = false; } -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub struct StorableRpcService(Vec); +pub type ProviderId = u64; -impl TryFrom for RpcService { - type Error = serde_json::Error; - fn try_from(value: StorableRpcService) -> Result { - serde_json::from_slice(&value.0) - } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstHeader { + pub name: &'static str, + pub value: &'static str, } -impl StorableRpcService { - pub fn new(service: &RpcService) -> Self { - // Store as JSON string to remove the possibility of RPC services getting mixed up - // if we make changes to `RpcService`, `EthMainnetService`, etc. - Self( - serde_json::to_vec(service) - .expect("BUG: unexpected error while serializing RpcService"), - ) +impl<'a> From<&'a ConstHeader> for HttpHeader { + fn from(header: &'a ConstHeader) -> Self { + HttpHeader { + name: header.name.to_string(), + value: header.value.to_string(), + } } } -impl Storable for StorableRpcService { - fn from_bytes(bytes: Cow<[u8]>) -> Self { - StorableRpcService(bytes.to_vec()) +/// Internal RPC provider representation. +#[derive(Debug, Clone, PartialEq, Eq, CandidType, Serialize)] +pub struct Provider { + #[serde(rename = "providerId")] + pub provider_id: ProviderId, + #[serde(rename = "chainId")] + pub chain_id: u64, + pub access: RpcAccess, + pub alias: Option, +} + +impl Provider { + pub fn api(&self) -> RpcApi { + match &self.access { + RpcAccess::Authenticated { auth, public_url } => match get_api_key(self.provider_id) { + Some(api_key) => match auth { + RpcAuth::BearerToken { url } => RpcApi { + url: url.to_string(), + headers: Some(vec![HttpHeader { + name: "Authorization".to_string(), + value: format!("Bearer {}", api_key.read()), + }]), + }, + RpcAuth::UrlParameter { url_pattern } => RpcApi { + url: url_pattern.replace(API_KEY_REPLACE_STRING, api_key.read()), + headers: None, + }, + }, + None => RpcApi { + url: public_url + .unwrap_or_else(|| { + panic!( + "API key not yet initialized for provider: {}", + self.provider_id + ) + }) + .to_string(), + headers: None, + }, + }, + RpcAccess::Unauthenticated { public_url } => RpcApi { + url: public_url.to_string(), + headers: None, + }, + } } - fn to_bytes(&self) -> Cow<[u8]> { - Cow::Owned(self.0.to_owned()) + pub fn hostname(&self) -> Option { + hostname_from_url(match &self.access { + RpcAccess::Authenticated { auth, .. } => match auth { + RpcAuth::BearerToken { url } => url, + RpcAuth::UrlParameter { url_pattern } => url_pattern, + }, + RpcAccess::Unauthenticated { public_url } => public_url, + }) } } -impl BoundedStorable for StorableRpcService { - const MAX_SIZE: u32 = RPC_SERVICE_MAX_SIZE; - const IS_FIXED_SIZE: bool = false; +#[derive(Debug, Clone, PartialEq, Eq, CandidType, Serialize)] +pub enum RpcAccess { + Authenticated { + auth: RpcAuth, + /// Public URL to use when the API key is not available. + #[serde(rename = "publicUrl")] + public_url: Option<&'static str>, + }, + Unauthenticated { + #[serde(rename = "publicUrl")] + public_url: &'static str, + }, +} + +impl RpcAccess { + pub fn public_url(&self) -> Option<&'static str> { + match self { + RpcAccess::Authenticated { public_url, .. } => *public_url, + RpcAccess::Unauthenticated { public_url } => Some(public_url), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, CandidType, Serialize)] +pub enum RpcAuth { + /// API key will be used in an Authorization header as Bearer token, e.g., + /// `Authorization: Bearer API_KEY` + BearerToken { url: &'static str }, + UrlParameter { + #[serde(rename = "urlPattern")] + url_pattern: &'static str, + }, } pub type RpcResult = Result; @@ -585,7 +514,7 @@ mod test { eth_rpc_client::providers::{EthMainnetService, RpcService}, }; - use crate::types::MultiRpcResult; + use crate::types::{ApiKey, MultiRpcResult}; #[test] fn test_multi_rpc_result_map() { @@ -646,4 +575,10 @@ mod test { ]) ); } + + #[test] + fn test_api_key_debug_output() { + let api_key = ApiKey("55555".to_string()); + assert!(format!("{api_key:?}") == "{API_KEY}"); + } } diff --git a/src/util.rs b/src/util.rs index 77124036..552b1072 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ use serde_json::Value; +use url::Host; pub fn hex_to_bytes(hex: &str) -> Option> { if !hex.starts_with("0x") { @@ -12,9 +13,22 @@ pub fn canonicalize_json(text: &[u8]) -> Option> { serde_json::to_vec(&json).ok() } +pub fn hostname_from_url(url: &str) -> Option { + url::Url::parse(url).ok().and_then(|url| match url.host() { + Some(Host::Domain(domain)) => { + if !domain.contains(['{', '}']) { + Some(domain.to_string()) + } else { + None + } + } + _ => None, + }) +} + #[cfg(test)] mod test { - use crate::util::{canonicalize_json, hex_to_bytes}; + use super::*; #[test] fn test_hex_to_bytes() { @@ -31,4 +45,34 @@ mod test { canonicalize_json(r#"{"B":2,"A":1}"#.as_bytes()).unwrap() ); } + + #[test] + fn test_hostname_from_url() { + assert_eq!( + hostname_from_url("https://example.com"), + Some("example.com".to_string()) + ); + assert_eq!( + hostname_from_url("https://example.com?k=v"), + Some("example.com".to_string()) + ); + assert_eq!( + hostname_from_url("https://example.com/{API_KEY}"), + Some("example.com".to_string()) + ); + assert_eq!( + hostname_from_url("https://example.com/path/{API_KEY}"), + Some("example.com".to_string()) + ); + assert_eq!( + hostname_from_url("https://example.com/path/{API_KEY}?k=v"), + Some("example.com".to_string()) + ); + assert_eq!(hostname_from_url("https://{API_KEY}"), None); + assert_eq!(hostname_from_url("https://{API_KEY}/path/"), None); + assert_eq!(hostname_from_url("https://{API_KEY}.com"), None); + assert_eq!(hostname_from_url("https://{API_KEY}.com/path/"), None); + assert_eq!(hostname_from_url("https://example.{API_KEY}"), None); + assert_eq!(hostname_from_url("https://example.{API_KEY}/path/"), None); + } } diff --git a/src/validate.rs b/src/validate.rs index cc1aff72..846ff36a 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -1,36 +1,80 @@ -use cketh_common::eth_rpc::ValidationError; -use ic_cdk::api::management_canister::http_request::HttpHeader; +use crate::{ + constants::{SERVICE_HOSTS_BLOCKLIST, VALID_API_KEY_CHARS}, + util::hostname_from_url, +}; -use crate::constants::{CONTENT_TYPE_HEADER, SERVICE_HOSTS_BLOCKLIST}; - -pub fn validate_hostname(hostname: &str) -> Result<(), ValidationError> { +pub fn validate_hostname(hostname: &str) -> Result<(), &'static str> { if SERVICE_HOSTS_BLOCKLIST.contains(&hostname) { - Err(ValidationError::HostNotAllowed(hostname.to_string())) + Err("Hostname not allowed") } else { Ok(()) } } -pub fn validate_credential_path(credential_path: &str) -> Result<(), ValidationError> { - if !(credential_path.is_empty() - || credential_path.starts_with('/') - || credential_path.starts_with('?')) +pub fn validate_url_pattern(url_pattern: &str) -> Result<(), &'static str> { + validate_hostname(&hostname_from_url(url_pattern).ok_or("Invalid hostname in URL")?) +} + +pub fn validate_api_key(api_key: &str) -> Result<(), &'static str> { + if api_key.is_empty() { + Err("API key must not be an empty string") + } else if api_key.len() > 200 { + Err("API key must be <= 200 characters") + } else if api_key + .chars() + .any(|char| !VALID_API_KEY_CHARS.contains(char)) { - Err(ValidationError::CredentialPathNotAllowed) + Err("Invalid character in API key") } else { Ok(()) } } -pub fn validate_credential_headers( - credential_headers: &[HttpHeader], -) -> Result<(), ValidationError> { - if credential_headers - .iter() - .any(|HttpHeader { name, .. }| name == CONTENT_TYPE_HEADER) - { - Err(ValidationError::CredentialHeaderNotAllowed) - } else { - Ok(()) +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn test_validate_url_pattern() { + assert_eq!(validate_url_pattern("https://example.com"), Ok(())); + assert_eq!(validate_url_pattern("https://example.com/v1/rpc"), Ok(())); + assert_eq!( + validate_url_pattern("https://example.com/{API_KEY}"), + Ok(()) + ); + assert_eq!( + validate_url_pattern("https://{API_KEY}"), + Err("Invalid hostname in URL") + ); + assert_eq!( + validate_url_pattern("https://{API_KEY}/v1/rpc"), + Err("Invalid hostname in URL") + ); + assert_eq!( + validate_url_pattern("https://{API_KEY}/{API_KEY}"), + Err("Invalid hostname in URL") + ); + } + + #[test] + pub fn test_validate_api_key() { + assert_eq!(validate_api_key("abc"), Ok(())); + assert_eq!( + validate_api_key("?a=b"), + Err("Invalid character in API key") + ); + assert_eq!(validate_api_key("/"), Err("Invalid character in API key")); + assert_eq!( + validate_api_key("abc/def"), + Err("Invalid character in API key") + ); + assert_eq!( + validate_api_key("../def"), + Err("Invalid character in API key") + ); + assert_eq!( + validate_api_key("abc/:key"), + Err("Invalid character in API key") + ); } } diff --git a/tests/tests.rs b/tests/tests.rs index c1c246fe..8e8a197d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -3,7 +3,7 @@ mod mock; use std::{marker::PhantomData, rc::Rc, str::FromStr, time::Duration}; use assert_matches::assert_matches; -use candid::{CandidType, Decode, Encode, Nat, Principal}; +use candid::{CandidType, Decode, Encode, Nat}; use cketh_common::{ address::Address, checked_amount::CheckedAmountOf, @@ -29,16 +29,11 @@ use maplit::hashmap; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use evm_rpc::{ - constants::{ - CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE, ETH_MAINNET_CHAIN_ID, NODES_IN_STANDARD_SUBNET, - }, - providers::{ - get_default_providers, ALCHEMY_ETH_MAINNET_HOSTNAME, ANKR_HOSTNAME, - BLOCKPI_ETH_SEPOLIA_HOSTNAME, CLOUDFLARE_HOSTNAME, PUBLICNODE_ETH_MAINNET_HOSTNAME, - }, + constants::{CONTENT_TYPE_HEADER_LOWERCASE, CONTENT_TYPE_VALUE}, + providers::PROVIDERS, types::{ - candid_types, Auth, InitArgs, ManageProviderArgs, Metrics, MultiRpcResult, ProviderView, - RegisterProviderArgs, RpcMethod, RpcResult, RpcServices, UpdateProviderArgs, + candid_types, InitArgs, Metrics, MultiRpcResult, ProviderId, RpcAccess, RpcMethod, + RpcResult, RpcServices, }, }; use evm_rpc_types::Nat256; @@ -46,6 +41,7 @@ use mock::{MockOutcall, MockOutcallBuilder}; const DEFAULT_CALLER_TEST_ID: u64 = 10352385; const DEFAULT_CONTROLLER_TEST_ID: u64 = 10352386; +const ADDITIONAL_TEST_ID: u64 = 10352387; const INITIAL_CYCLES: u128 = 100_000_000_000_000_000; @@ -68,6 +64,12 @@ const RPC_SERVICES: &[RpcServices] = &[ RpcServices::OptimismMainnet(None), ]; +const ANKR_HOSTNAME: &str = "rpc.ankr.com"; +const ALCHEMY_ETH_MAINNET_HOSTNAME: &str = "eth-mainnet.g.alchemy.com"; +const CLOUDFLARE_HOSTNAME: &str = "cloudflare-eth.com"; +const BLOCKPI_ETH_SEPOLIA_HOSTNAME: &str = "ethereum-sepolia.blockpi.network"; +const PUBLICNODE_ETH_MAINNET_HOSTNAME: &str = "ethereum-rpc.publicnode.com"; + fn evm_rpc_wasm() -> Vec { load_wasm(std::env::var("CARGO_MANIFEST_DIR").unwrap(), "evm_rpc", &[]) } @@ -97,16 +99,19 @@ impl Default for EvmRpcSetup { impl EvmRpcSetup { pub fn new() -> Self { + Self::with_args(InitArgs { + manage_api_keys: None, + demo: Some(true), + }) + } + + pub fn with_args(args: InitArgs) -> Self { let env = Rc::new( StateMachineBuilder::new() .with_default_canister_range() .build(), ); - let args = InitArgs { - nodes_in_subnet: NODES_IN_STANDARD_SUBNET, - }; - let controller = PrincipalId::new_user_test_id(DEFAULT_CONTROLLER_TEST_ID); let canister_id = env.create_canister_with_cycles( None, @@ -181,67 +186,14 @@ impl EvmRpcSetup { } } - pub fn authorize(&self, principal: &PrincipalId, auth: Auth) -> bool { - self.call_update("authorize", Encode!(&principal.0, &auth).unwrap()) - .wait() - } - - pub fn deauthorize(&self, principal: &PrincipalId, auth: Auth) -> bool { - self.call_update("deauthorize", Encode!(&principal.0, &auth).unwrap()) - .wait() - } - - pub fn get_authorized(&self, auth: Auth) -> Vec { - self.call_query("getAuthorized", Encode!(&auth).unwrap()) - } - pub fn get_metrics(&self) -> Metrics { self.call_query("getMetrics", Encode!().unwrap()) } - pub fn get_providers(&self) -> Vec { - self.call_query("getProviders", Encode!().unwrap()) - } - - pub fn get_service_provider_map(&self) -> Vec<(RpcService, u64)> { + pub fn get_service_provider_map(&self) -> Vec<(RpcService, ProviderId)> { self.call_query("getServiceProviderMap", Encode!().unwrap()) } - pub fn register_provider(&self, args: RegisterProviderArgs) -> u64 { - self.call_update("registerProvider", Encode!(&args).unwrap()) - .wait() - } - - pub fn unregister_provider(&self, provider_id: u64) -> bool { - self.call_update("unregisterProvider", Encode!(&provider_id).unwrap()) - .wait() - } - - pub fn update_provider(&self, args: UpdateProviderArgs) { - self.call_update("updateProvider", Encode!(&args).unwrap()) - .wait() - } - - pub fn manage_provider(&self, args: ManageProviderArgs) { - self.call_update("manageProvider", Encode!(&args).unwrap()) - .wait() - } - - pub fn authorize_caller(self, auth: Auth) -> Self { - self.clone().as_controller().authorize(&self.caller, auth); - self - } - - pub fn deauthorize_caller(self, auth: Auth) -> Self { - self.clone().as_controller().deauthorize(&self.caller, auth); - self - } - - pub fn set_open_rpc_access(&self, open_rpc_access: bool) { - self.call_update("setOpenRpcAccess", Encode!(&open_rpc_access).unwrap()) - .wait() - } - pub fn request_cost( &self, source: RpcService, @@ -331,6 +283,29 @@ impl EvmRpcSetup { Encode!(&source, &config, &signed_raw_transaction_hex).unwrap(), ) } + + pub fn update_api_keys(&self, api_keys: &[(ProviderId, Option)]) { + self.call_update("updateApiKeys", Encode!(&api_keys).unwrap()) + .wait() + } + + pub fn mock_api_keys(self) -> Self { + self.clone().as_controller().update_api_keys( + &PROVIDERS + .iter() + .filter_map(|provider| { + Some(( + provider.provider_id, + match provider.access { + RpcAccess::Authenticated { .. } => Some("mock-api-key".to_string()), + RpcAccess::Unauthenticated { .. } => None?, + }, + )) + }) + .collect::>(), + ); + self + } } pub struct CallFlow { @@ -484,7 +459,7 @@ impl CallFlow { } fn mock_request(builder_fn: impl Fn(MockOutcallBuilder) -> MockOutcallBuilder) { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new(); assert_matches!( setup .request( @@ -526,7 +501,7 @@ fn mock_request_should_succeed_with_method() { fn mock_request_should_succeed_with_request_headers() { mock_request(|builder| { builder.with_request_headers(vec![ - (CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE), + (CONTENT_TYPE_HEADER_LOWERCASE, CONTENT_TYPE_VALUE), ("Custom", "Value"), ]) }) @@ -549,7 +524,7 @@ fn mock_request_should_succeed_with_all() { .with_url(MOCK_REQUEST_URL) .with_method(HttpMethod::POST) .with_request_headers(vec![ - (CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE), + (CONTENT_TYPE_HEADER_LOWERCASE, CONTENT_TYPE_VALUE), ("Custom", "Value"), ]) .with_request_body(MOCK_REQUEST_PAYLOAD) @@ -580,404 +555,9 @@ fn mock_request_should_fail_with_request_body() { mock_request(|builder| builder.with_request_body(r#"{"different":"body"}"#)) } -#[test] -fn should_register_provider() { - let mut setup = EvmRpcSetup::new(); - assert_eq!( - setup - .get_providers() - .into_iter() - .map(|p| (p.chain_id, p.hostname)) - .collect::>(), - get_default_providers() - .into_iter() - .map(|p| (p.chain_id, p.hostname)) - .collect::>() - ); - let n_providers = 2; - setup = setup.authorize_caller(Auth::RegisterProvider); - assert_eq!( - setup - .clone() - .as_controller() - .get_authorized(Auth::RegisterProvider), - vec![setup.caller.0] - ); - let a_id = setup.register_provider(RegisterProviderArgs { - chain_id: 1, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - // Permission removed after registering - assert!(setup - .clone() - .as_controller() - .get_authorized(Auth::RegisterProvider) - .into_iter() - .all(|p| p != setup.caller.0)); - // Permission must be granted for each additional provider - setup = setup.authorize_caller(Auth::RegisterProvider); - let b_id = setup.register_provider(RegisterProviderArgs { - chain_id: 5, - hostname: CLOUDFLARE_HOSTNAME.to_string(), - credential_path: "/test-path".to_string(), - credential_headers: Some(vec![HttpHeader { - name: "Test-Authorization".to_string(), - value: "---".to_string(), - }]), - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - assert_eq!(a_id + 1, b_id); - let providers = setup.get_providers(); - assert_eq!(providers.len(), get_default_providers().len() + n_providers); - let first_new_id = (providers.len() - n_providers) as u64; - assert_eq!( - providers[providers.len() - n_providers..], - vec![ - ProviderView { - provider_id: first_new_id, - owner: setup.caller.0, - chain_id: 1, - hostname: ANKR_HOSTNAME.to_string(), - cycles_per_call: 0, - cycles_per_message_byte: 0, - primary: false, - }, - ProviderView { - provider_id: first_new_id + 1, - owner: setup.caller.0, - chain_id: 5, - hostname: CLOUDFLARE_HOSTNAME.to_string(), - cycles_per_call: 0, - cycles_per_message_byte: 0, - primary: false, - } - ] - ); - setup.unregister_provider(a_id); - setup.unregister_provider(b_id); -} - -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_anonymous_register_provider() { - let setup = EvmRpcSetup::new().as_anonymous(); - setup.register_provider(RegisterProviderArgs { - chain_id: 1, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_anonymous_update_provider() { - let setup = EvmRpcSetup::new().as_anonymous(); - setup.update_provider(UpdateProviderArgs { - provider_id: 3, - credential_path: None, - credential_headers: None, - cycles_per_call: None, - cycles_per_message_byte: None, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_unauthorized_register_provider() { - let setup = EvmRpcSetup::new(); - setup.register_provider(RegisterProviderArgs { - chain_id: 1, - hostname: ANKR_HOSTNAME.to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_unauthorized_manage_provider() { - let setup = EvmRpcSetup::new(); - setup.manage_provider(ManageProviderArgs { - provider_id: 2, - chain_id: None, - primary: Some(true), - service: None, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_anonymous_manage_provider() { - let setup = EvmRpcSetup::new().as_anonymous(); - setup.manage_provider(ManageProviderArgs { - provider_id: 3, - chain_id: None, - primary: Some(true), - service: None, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized: check provider owner")] -fn should_panic_if_unauthorized_update_provider() { - // Providers can only be updated by the original owner or canister controller - let setup = EvmRpcSetup::new(); - setup.update_provider(UpdateProviderArgs { - provider_id: 0, - credential_path: None, - credential_headers: None, - cycles_per_call: None, - cycles_per_message_byte: None, - }); -} - -#[test] -fn should_allow_controller_update_provider() { - let setup = EvmRpcSetup::new().as_controller(); - setup.update_provider(UpdateProviderArgs { - provider_id: 0, - credential_path: None, - credential_headers: None, - cycles_per_call: None, - cycles_per_message_byte: None, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized: check provider owner")] -fn should_panic_if_manage_auth_update_non_owned_provider() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::Manage); - let provider_id = setup - .clone() - .as_controller() - .authorize_caller(Auth::RegisterProvider) - .register_provider(RegisterProviderArgs { - chain_id: 123, - hostname: "example.com".to_string(), - credential_path: "".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - setup.update_provider(UpdateProviderArgs { - provider_id, - credential_path: None, - credential_headers: None, - cycles_per_call: None, - cycles_per_message_byte: None, - }); -} - -#[test] -#[should_panic(expected = "You are not authorized: check provider owner")] -fn should_panic_if_unauthorized_unregister_provider() { - // Only the `Manage` authorization may unregister a provider - let setup = EvmRpcSetup::new().authorize_caller(Auth::RegisterProvider); - setup.unregister_provider(0); -} - -#[test] -#[should_panic(expected = "You are not authorized: check provider owner")] -fn should_panic_if_manage_auth_unregister_provider() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::Manage); - setup.unregister_provider(3); -} - -#[test] -fn should_replace_service_provider() { - let setup = EvmRpcSetup::new() - .authorize_caller(Auth::RegisterProvider) - .authorize_caller(Auth::FreeRpc); - let provider_id = setup.register_provider(RegisterProviderArgs { - chain_id: ETH_MAINNET_CHAIN_ID, - hostname: "ankr2.com".to_string(), - credential_path: "/v2/mainnet".to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - setup - .clone() - .as_controller() - .manage_provider(ManageProviderArgs { - provider_id, - chain_id: None, - primary: Some(true), - service: Some(RpcService::EthMainnet(EthMainnetService::Ankr)), - }); - let result = setup - .eth_get_transaction_count( - RpcServices::EthMainnet(Some(vec![EthMainnetService::Ankr])), - None, - candid_types::GetTransactionCountArgs { - address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), - block: candid_types::BlockTag::Latest, - }, - ) - .mock_http( - MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#) - .with_url("https://ankr2.com/v2/mainnet"), - ) - .wait() - .expect_consistent(); - assert_eq!(result, Ok(1.into())); -} - -#[test] -fn should_set_primary_provider() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let new_chain_id = 12345; - let before_credential = "/before"; - let after_credential = "/after"; - setup - .clone() - .authorize_caller(Auth::RegisterProvider) - .register_provider(RegisterProviderArgs { - chain_id: new_chain_id, - hostname: ALCHEMY_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: before_credential.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - let provider_id = setup - .clone() - .authorize_caller(Auth::RegisterProvider) - .register_provider(RegisterProviderArgs { - chain_id: new_chain_id, - hostname: ALCHEMY_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: after_credential.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - assert_matches!( - setup - .request( - RpcService::Chain(new_chain_id), - MOCK_REQUEST_PAYLOAD, - MOCK_REQUEST_RESPONSE_BYTES, - ) - .mock_http( - MockOutcallBuilder::new(200, MOCK_REQUEST_RESPONSE).with_url(format!( - "https://{}{}", - ALCHEMY_ETH_MAINNET_HOSTNAME, before_credential - )) - ) - .wait(), - Ok(_) - ); - setup - .clone() - .as_controller() - .manage_provider(ManageProviderArgs { - provider_id, - chain_id: None, - primary: Some(true), - service: None, - }); - assert_matches!( - setup - .request( - RpcService::Chain(new_chain_id), - MOCK_REQUEST_PAYLOAD, - MOCK_REQUEST_RESPONSE_BYTES, - ) - .mock_http( - MockOutcallBuilder::new(200, MOCK_REQUEST_RESPONSE).with_url(format!( - "https://{}{}", - ALCHEMY_ETH_MAINNET_HOSTNAME, after_credential - )) - ) - .wait(), - Ok(_) - ); -} - -#[test] -fn should_set_provider_chain_id() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - let before_chain_id = 12345; - let after_chain_id = 56789; - let credential = "/credential"; - setup - .clone() - .authorize_caller(Auth::RegisterProvider) - .register_provider(RegisterProviderArgs { - chain_id: before_chain_id, - hostname: ALCHEMY_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: credential.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - let provider_id = setup - .clone() - .authorize_caller(Auth::RegisterProvider) - .register_provider(RegisterProviderArgs { - chain_id: before_chain_id, - hostname: ALCHEMY_ETH_MAINNET_HOSTNAME.to_string(), - credential_path: credential.to_string(), - credential_headers: None, - cycles_per_call: 0, - cycles_per_message_byte: 0, - }); - assert_matches!( - setup - .request( - RpcService::Chain(before_chain_id), - MOCK_REQUEST_PAYLOAD, - MOCK_REQUEST_RESPONSE_BYTES, - ) - .mock_http( - MockOutcallBuilder::new(200, MOCK_REQUEST_RESPONSE).with_url(format!( - "https://{}{}", - ALCHEMY_ETH_MAINNET_HOSTNAME, credential - )) - ) - .wait(), - Ok(_) - ); - setup - .clone() - .as_controller() - .manage_provider(ManageProviderArgs { - provider_id, - chain_id: Some(after_chain_id), - primary: None, - service: None, - }); - assert_matches!( - setup - .request( - RpcService::Chain(after_chain_id), - MOCK_REQUEST_PAYLOAD, - MOCK_REQUEST_RESPONSE_BYTES, - ) - .mock_http( - MockOutcallBuilder::new(200, MOCK_REQUEST_RESPONSE).with_url(format!( - "https://{}{}", - ALCHEMY_ETH_MAINNET_HOSTNAME, credential - )) - ) - .wait(), - Ok(_) - ); -} - #[test] fn should_canonicalize_json_response() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new(); let responses = [ r#"{"id":1,"jsonrpc":"2.0","result":"0x00112233"}"#, r#"{"result":"0x00112233","id":1,"jsonrpc":"2.0"}"#, @@ -1055,10 +635,74 @@ fn should_decode_transaction_receipt() { ); } +#[test] +fn should_use_fallback_public_url() { + let authorized_caller = PrincipalId::new_user_test_id(ADDITIONAL_TEST_ID); + let setup = EvmRpcSetup::with_args(InitArgs { + demo: Some(true), + manage_api_keys: Some(vec![authorized_caller.0]), + }); + let response = setup + .eth_get_transaction_count( + RpcServices::EthMainnet(Some(vec![EthMainnetService::Ankr])), + None, + candid_types::GetTransactionCountArgs { + address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), + block: candid_types::BlockTag::Latest, + }, + ) + .mock_http( + MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#) + .with_url("https://rpc.ankr.com/eth"), + ) + .wait() + .expect_consistent() + .unwrap(); + assert_eq!(response, 1); +} + +#[test] +fn should_insert_api_keys() { + let authorized_caller = PrincipalId::new_user_test_id(ADDITIONAL_TEST_ID); + let setup = EvmRpcSetup::with_args(InitArgs { + demo: Some(true), + manage_api_keys: Some(vec![authorized_caller.0]), + }); + let provider_id = 1; + setup + .clone() + .as_caller(authorized_caller) + .update_api_keys(&[(provider_id, Some("test-api-key".to_string()))]); + let response = setup + .eth_get_transaction_count( + RpcServices::EthMainnet(Some(vec![EthMainnetService::Ankr])), + None, + candid_types::GetTransactionCountArgs { + address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), + block: candid_types::BlockTag::Latest, + }, + ) + .mock_http( + MockOutcallBuilder::new(200, r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#) + .with_url("https://rpc.ankr.com/eth/test-api-key"), + ) + .wait() + .expect_consistent() + .unwrap(); + assert_eq!(response, 1); +} + +#[test] +#[should_panic(expected = "You are not authorized")] +fn should_prevent_unauthorized_update_api_keys() { + let setup = EvmRpcSetup::new(); + setup.update_api_keys(&[(0, Some("unauthorized-api-key".to_string()))]); +} + #[test] fn eth_get_logs_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_logs( source.clone(), @@ -1113,7 +757,7 @@ fn eth_get_logs_should_succeed() { #[test] fn eth_get_block_by_number_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_block_by_number( source.clone(), @@ -1156,7 +800,7 @@ fn eth_get_block_by_number_should_succeed() { #[test] fn eth_get_block_by_number_pre_london_fork_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_block_by_number( source.clone(), @@ -1199,7 +843,7 @@ fn eth_get_block_by_number_pre_london_fork_should_succeed() { #[test] fn eth_get_transaction_receipt_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_transaction_receipt( source.clone(), @@ -1234,7 +878,7 @@ fn eth_get_transaction_receipt_should_succeed() { #[test] fn eth_get_transaction_count_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_transaction_count( source.clone(), @@ -1258,7 +902,7 @@ fn eth_get_transaction_count_should_succeed() { #[test] fn eth_fee_history_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_fee_history( source.clone(), @@ -1294,7 +938,7 @@ fn eth_fee_history_should_succeed() { #[test] fn eth_send_raw_transaction_should_succeed() { for source in RPC_SERVICES { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_send_raw_transaction(source.clone(), None, MOCK_TRANSACTION) .mock_http(MockOutcallBuilder::new( @@ -1315,7 +959,7 @@ fn eth_send_raw_transaction_should_succeed() { #[test] fn candid_rpc_should_allow_unexpected_response_fields() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let response = setup .eth_get_transaction_receipt( RpcServices::EthMainnet(None), @@ -1337,7 +981,11 @@ fn candid_rpc_should_allow_unexpected_response_fields() { #[test] fn candid_rpc_should_err_without_cycles() { - let setup = EvmRpcSetup::new(); + let setup = EvmRpcSetup::with_args(InitArgs { + demo: None, + manage_api_keys: None, + }) + .mock_api_keys(); let result = setup .eth_get_transaction_receipt( RpcServices::EthMainnet(None), @@ -1355,37 +1003,9 @@ fn candid_rpc_should_err_without_cycles() { ); } -#[test] -fn candid_rpc_should_err_during_restricted_access() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - setup.clone().as_controller().set_open_rpc_access(false); - let result = setup - .eth_get_transaction_receipt( - RpcServices::EthMainnet(Some(vec![ - EthMainnetService::Cloudflare, - EthMainnetService::BlockPi, - ])), - None, - "0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f", - ) - .wait() - .expect_consistent(); - assert_eq!( - result, - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ); - assert_eq!( - setup.get_metrics(), - Metrics { - err_no_permission: 1, - ..Default::default() - } - ); -} - #[test] fn candid_rpc_should_err_when_service_unavailable() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_get_transaction_receipt( RpcServices::EthMainnet(None), @@ -1426,7 +1046,7 @@ fn candid_rpc_should_err_when_service_unavailable() { #[test] fn candid_rpc_should_recognize_json_error() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_get_transaction_receipt( RpcServices::EthSepolia(Some(vec![ @@ -1468,7 +1088,7 @@ fn candid_rpc_should_recognize_json_error() { #[test] fn candid_rpc_should_reject_empty_service_list() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_get_transaction_receipt( RpcServices::EthMainnet(Some(vec![])), @@ -1485,7 +1105,7 @@ fn candid_rpc_should_reject_empty_service_list() { #[test] fn candid_rpc_should_return_inconsistent_results() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let results = setup .eth_send_raw_transaction( RpcServices::EthMainnet(Some(vec![ @@ -1543,7 +1163,7 @@ fn candid_rpc_should_return_inconsistent_results() { #[test] fn candid_rpc_should_return_inconsistent_results_with_error() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_get_transaction_count( RpcServices::EthMainnet(Some(vec![ @@ -1605,7 +1225,7 @@ fn candid_rpc_should_return_inconsistent_results_with_error() { #[test] fn candid_rpc_should_return_inconsistent_results_with_unexpected_http_status() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_get_transaction_count( RpcServices::EthMainnet(Some(vec![ @@ -1668,7 +1288,7 @@ fn candid_rpc_should_return_inconsistent_results_with_unexpected_http_status() { #[test] fn candid_rpc_should_handle_already_known() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_send_raw_transaction( RpcServices::EthMainnet(Some(vec![ @@ -1713,7 +1333,7 @@ fn candid_rpc_should_handle_already_known() { #[test] fn candid_rpc_should_recognize_rate_limit() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let result = setup .eth_send_raw_transaction( RpcServices::EthMainnet(Some(vec![ @@ -1753,69 +1373,9 @@ fn candid_rpc_should_recognize_rate_limit() { ); } -#[test] -#[should_panic(expected = "You are not authorized")] -fn should_panic_if_unauthorized_set_rpc_access() { - // Only `Manage` can restrict RPC access - let setup = EvmRpcSetup::new(); - setup.set_open_rpc_access(false); -} - -#[test] -fn should_restrict_rpc_access() { - let mut setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); - setup.clone().as_controller().set_open_rpc_access(false); - let result = setup - .eth_get_transaction_count( - RpcServices::EthMainnet(Some(vec![EthMainnetService::Ankr])), - None, - candid_types::GetTransactionCountArgs { - address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), - block: candid_types::BlockTag::Latest, - }, - ) - .wait() - .expect_consistent(); - assert_eq!( - result, - Err(RpcError::ProviderError(ProviderError::NoPermission)) - ); - setup = setup.authorize_caller(Auth::PriorityRpc); - let result = setup - .eth_get_transaction_count( - RpcServices::EthMainnet(Some(vec![EthMainnetService::Ankr])), - None, - candid_types::GetTransactionCountArgs { - address: "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), - block: candid_types::BlockTag::Latest, - }, - ) - .mock_http(MockOutcallBuilder::new( - 200, - r#"{"jsonrpc":"2.0","id":0,"result":"0x1"}"#, - )) - .wait() - .expect_consistent(); - assert_eq!(result, Ok(1.into())); - let rpc_method = || RpcMethod::EthGetTransactionCount.into(); - assert_eq!( - setup.get_metrics(), - Metrics { - requests: hashmap! { - (rpc_method(), ANKR_HOSTNAME.into()) => 1, - }, - responses: hashmap! { - (rpc_method(), ANKR_HOSTNAME.into(), 200.into()) => 1, - }, - err_no_permission: 1, - ..Default::default() - } - ); -} - #[test] fn should_use_custom_response_size_estimate() { - let setup = EvmRpcSetup::new().authorize_caller(Auth::FreeRpc); + let setup = EvmRpcSetup::new().mock_api_keys(); let max_response_bytes = 1234; let expected_response = r#"{"id":0,"jsonrpc":"2.0","result":[{"address":"0xdac17f958d2ee523a2206206994597c13d831ec7","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a9d1e08c7793af67e9d92fe308d5697fb81d3e43","0x00000000000000000000000078cccfb3d517cd4ed6d045e263e134712288ace2"],"data":"0x000000000000000000000000000000000000000000000000000000003b9c6433","blockNumber":"0x11dc77e","transactionHash":"0xf3ed91a03ddf964281ac7a24351573efd535b80fc460a5c2ad2b9d23153ec678","transactionIndex":"0x65","blockHash":"0xd5c72ad752b2f0144a878594faf8bd9f570f2f72af8e7f0940d3545a6388f629","logIndex":"0xe8","removed":false}]}"#; let response = setup