From 2ea65e4f16b029d4adac2d04a52b7b130901fe3a Mon Sep 17 00:00:00 2001 From: Marko Petrlic Date: Sun, 4 Aug 2024 00:39:56 +0200 Subject: [PATCH] Bumped jsonrpsee to v0.22 and added rpc limiter from upstream --- Cargo.lock | 289 +++++++++++++----- .../client/relay-chain-interface/Cargo.toml | 2 +- .../relay-chain-rpc-interface/Cargo.toml | 2 +- .../src/light_client_worker.rs | 9 +- .../src/reconnecting_ws_client.rs | 2 +- .../src/rpc_client.rs | 2 +- cumulus/parachain-template/node/Cargo.toml | 2 +- cumulus/polkadot-parachain/Cargo.toml | 2 +- cumulus/test/service/Cargo.toml | 2 +- polkadot/rpc/Cargo.toml | 2 +- substrate/bin/minimal/node/Cargo.toml | 2 +- substrate/bin/node-template/node/Cargo.toml | 2 +- substrate/bin/node/cli/Cargo.toml | 2 +- .../bin/node/cli/benches/block_production.rs | 1 + .../bin/node/cli/benches/transaction_pool.rs | 1 + substrate/bin/node/rpc/Cargo.toml | 2 +- substrate/client/cli/src/commands/run_cmd.rs | 18 +- substrate/client/cli/src/config.rs | 15 +- substrate/client/cli/src/runner.rs | 1 + .../client/consensus/babe/rpc/Cargo.toml | 2 +- .../client/consensus/babe/rpc/src/lib.rs | 4 +- .../client/consensus/beefy/rpc/Cargo.toml | 2 +- .../client/consensus/beefy/rpc/src/lib.rs | 16 +- .../client/consensus/grandpa/rpc/Cargo.toml | 2 +- .../client/consensus/grandpa/rpc/src/lib.rs | 6 +- .../client/consensus/manual-seal/Cargo.toml | 2 +- .../merkle-mountain-range/rpc/Cargo.toml | 2 +- substrate/client/rpc-api/Cargo.toml | 2 +- substrate/client/rpc-servers/Cargo.toml | 6 +- substrate/client/rpc-servers/src/lib.rs | 125 ++++++-- .../client/rpc-servers/src/middleware.rs | 226 -------------- .../rpc-servers/src/middleware/metrics.rs | 281 +++++++++++++++++ .../client/rpc-servers/src/middleware/mod.rs | 27 ++ .../rpc-servers/src/middleware/rate_limit.rs | 107 +++++++ substrate/client/rpc-spec-v2/Cargo.toml | 2 +- .../client/rpc-spec-v2/src/chain_head/api.rs | 8 +- .../rpc-spec-v2/src/chain_head/chain_head.rs | 117 ++++--- .../rpc-spec-v2/src/chain_head/tests.rs | 27 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- substrate/client/service/src/config.rs | 3 + substrate/client/service/src/lib.rs | 23 +- substrate/client/service/test/src/lib.rs | 1 + substrate/client/sync-state-rpc/Cargo.toml | 2 +- .../frame/transaction-payment/rpc/Cargo.toml | 2 +- .../frame/remote-externalities/Cargo.toml | 2 +- substrate/utils/frame/rpc/client/Cargo.toml | 2 +- substrate/utils/frame/rpc/client/src/lib.rs | 2 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/rpc/support/Cargo.toml | 4 +- substrate/utils/frame/rpc/support/src/lib.rs | 4 +- substrate/utils/frame/rpc/system/Cargo.toml | 2 +- 52 files changed, 931 insertions(+), 444 deletions(-) delete mode 100644 substrate/client/rpc-servers/src/middleware.rs create mode 100644 substrate/client/rpc-servers/src/middleware/metrics.rs create mode 100644 substrate/client/rpc-servers/src/middleware/mod.rs create mode 100644 substrate/client/rpc-servers/src/middleware/rate_limit.rs diff --git a/Cargo.lock b/Cargo.lock index 341ae3985307..b1d8ca8b5ec2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,16 +86,16 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.9.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" dependencies = [ "aead 0.4.3", "aes 0.7.5", "cipher 0.3.0", - "ctr 0.8.0", + "ctr 0.7.0", "ghash 0.4.4", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -109,7 +109,7 @@ dependencies = [ "cipher 0.4.4", "ctr 0.9.2", "ghash 0.5.0", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -1342,6 +1342,12 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -3460,7 +3466,7 @@ checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -3492,24 +3498,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.7", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ "generic-array 0.14.7", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" dependencies = [ "cipher 0.3.0", ] @@ -4349,7 +4355,7 @@ dependencies = [ "byteorder", "digest 0.8.1", "rand_core 0.5.1", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -4362,7 +4368,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -4379,7 +4385,7 @@ dependencies = [ "fiat-crypto", "platforms", "rustc_version 0.4.0", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -4603,7 +4609,7 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -4809,7 +4815,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -4863,7 +4869,7 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -5237,7 +5243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -5854,9 +5860,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -5885,9 +5891,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -6151,6 +6157,26 @@ dependencies = [ "testnet-parachains-constants", ] +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot 0.12.1", + "portable-atomic", + "quanta", + "rand", + "smallvec", + "spinning_top", +] + [[package]] name = "group" version = "0.13.0" @@ -6159,7 +6185,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -6312,7 +6338,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac 0.11.1", + "crypto-mac 0.11.0", "digest 0.9.0", ] @@ -6440,9 +6466,9 @@ dependencies = [ "hyper", "log", "rustls 0.21.6", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -6770,9 +6796,9 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" [[package]] name = "jsonrpsee" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affdc52f7596ccb2d7645231fc6163bb314630c989b64998f3699a28b4d5d4dc" +checksum = "cfdb12a2381ea5b2e68c3469ec604a007b367778cdb14d09612c8069ebd616ad" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -6786,19 +6812,20 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b005c793122d03217da09af68ba9383363caa950b90d3436106df8cabce935" +checksum = "4978087a58c3ab02efc5b07c5e5e2803024536106fd5506f558db172c889b3aa" dependencies = [ "futures-util", "http", "jsonrpsee-core", "pin-project", - "rustls-native-certs", + "rustls-native-certs 0.7.1", + "rustls-pki-types", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tokio-util", "tracing", "url", @@ -6806,12 +6833,11 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2327ba8df2fdbd5e897e2b5ed25ce7f299d345b9736b6828814c3dbd1fd47b" +checksum = "b4b257e1ec385e07b0255dde0b933f948b5c8b8c28d42afda9587c3a967b896d" dependencies = [ "anyhow", - "async-lock", "async-trait", "beef", "futures-timer", @@ -6819,21 +6845,22 @@ dependencies = [ "hyper", "jsonrpsee-types", "parking_lot 0.12.1", + "pin-project", "rand", "rustc-hash", "serde", "serde_json", - "soketto", "thiserror", "tokio", + "tokio-stream", "tracing", ] [[package]] name = "jsonrpsee-http-client" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" +checksum = "1ccf93fc4a0bfe05d851d37d7c32b7f370fe94336b52a2f0efc5f1981895c2e5" dependencies = [ "async-trait", "hyper", @@ -6851,28 +6878,29 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29110019693a4fa2dbda04876499d098fa16d70eba06b1e6e2b3f1b251419515" +checksum = "7d0bb047e79a143b32ea03974a6bf59b62c2a4c5f5d42a381c907a8bbb3f75c0" dependencies = [ "heck", - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "jsonrpsee-server" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c39a00449c9ef3f50b84fc00fc4acba20ef8f559f07902244abf4c15c5ab9c" +checksum = "12d8b6a9674422a8572e0b0abb12feeb3f2aeda86528c80d0350c2bd0923ab41" dependencies = [ "futures-util", "http", "hyper", "jsonrpsee-core", "jsonrpsee-types", + "pin-project", "route-recognizer", "serde", "serde_json", @@ -6887,23 +6915,22 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9" +checksum = "150d6168405890a7a3231a3c74843f58b8959471f6df76078db2619ddee1d07d" dependencies = [ "anyhow", "beef", "serde", "serde_json", "thiserror", - "tracing", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.20.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca9cb3933ccae417eb6b08c3448eb1cb46e39834e5b503e395e5e5bd08546c0" +checksum = "58b9db2dfd5bb1194b0ce921504df9ceae210a345bc2f6c5a61432089bbab070" dependencies = [ "http", "jsonrpsee-client-transport", @@ -7631,7 +7658,7 @@ checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", "digest 0.9.0", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -8128,7 +8155,7 @@ dependencies = [ "rand", "rand_chacha 0.3.1", "rand_distr", - "subtle 2.4.1", + "subtle 2.6.1", "thiserror", "zeroize", ] @@ -8492,6 +8519,12 @@ dependencies = [ "libc", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "no-std-net" version = "0.6.0" @@ -8742,6 +8775,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -11513,7 +11552,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "crypto-mac 0.11.1", + "crypto-mac 0.11.0", ] [[package]] @@ -13763,7 +13802,7 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.4.0", ] [[package]] @@ -13786,7 +13825,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.4.0", ] [[package]] @@ -13803,9 +13842,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.4.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "portpicker" @@ -14225,6 +14264,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "quanta" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -14385,6 +14439,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "raw-cpuid" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +dependencies = [ + "bitflags 2.4.0", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -14609,12 +14672,12 @@ dependencies = [ "percent-encoding", "pin-project-lite 0.2.12", "rustls 0.21.6", - "rustls-pemfile", + "rustls-pemfile 1.0.3", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -14641,7 +14704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -15122,10 +15185,24 @@ checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" dependencies = [ "log", "ring 0.16.20", - "rustls-webpki", + "rustls-webpki 0.101.4", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.6", + "subtle 2.6.1", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -15133,7 +15210,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.3", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.3", + "rustls-pki-types", "schannel", "security-framework", ] @@ -15147,6 +15237,22 @@ dependencies = [ "base64 0.21.2", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.4" @@ -15157,6 +15263,17 @@ dependencies = [ "untrusted 0.7.1", ] +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -16316,9 +16433,13 @@ dependencies = [ name = "sc-rpc-server" version = "11.0.0" dependencies = [ + "futures", + "governor", "http", + "hyper", "jsonrpsee", "log", + "pin-project", "serde_json", "substrate-prometheus-endpoint", "tokio", @@ -16764,7 +16885,7 @@ dependencies = [ "merlin 2.0.1", "rand_core 0.5.1", "sha2 0.8.2", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -16799,7 +16920,7 @@ dependencies = [ "rand_core 0.6.4", "serde_bytes", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -16841,7 +16962,7 @@ dependencies = [ "der", "generic-array 0.14.7", "pkcs8", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -17479,7 +17600,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" dependencies = [ - "aes-gcm 0.9.4", + "aes-gcm 0.9.2", "blake2 0.10.6", "chacha20poly1305", "curve25519-dalek 4.1.1", @@ -17487,7 +17608,7 @@ dependencies = [ "ring 0.16.20", "rustc_version 0.4.0", "sha2 0.10.7", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -19001,6 +19122,15 @@ dependencies = [ "strum 0.24.1", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.2" @@ -19326,7 +19456,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "keccak", - "subtle 2.4.1", + "subtle 2.6.1", "zeroize", ] @@ -19657,9 +19787,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "subtle-ng" @@ -20238,6 +20368,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -20787,12 +20928,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ "generic-array 0.14.7", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] @@ -20802,7 +20943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", - "subtle 2.4.1", + "subtle 2.6.1", ] [[package]] diff --git a/cumulus/client/relay-chain-interface/Cargo.toml b/cumulus/client/relay-chain-interface/Cargo.toml index 004d30f7f94f..5cdfff23ae17 100644 --- a/cumulus/client/relay-chain-interface/Cargo.toml +++ b/cumulus/client/relay-chain-interface/Cargo.toml @@ -22,5 +22,5 @@ sc-client-api = { path = "../../../substrate/client/api" } futures = "0.3.28" async-trait = "0.1.74" thiserror = "1.0.48" -jsonrpsee-core = "0.20.3" +jsonrpsee-core = "0.22" parity-scale-codec = "3.6.4" diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index fef6f82537d7..fc89cdd5a9e5 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -33,7 +33,7 @@ tokio-util = { version = "0.7.8", features = ["compat"] } futures = "0.3.28" futures-timer = "3.0.2" parity-scale-codec = "3.6.4" -jsonrpsee = { version = "0.20.3", features = ["ws-client"] } +jsonrpsee = { version = "0.22", features = ["ws-client"] } tracing = "0.1.37" async-trait = "0.1.74" url = "2.4.0" diff --git a/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs b/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs index 6fd057e170b7..9a49b60281b3 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/light_client_worker.rs @@ -19,12 +19,9 @@ //! we treat the light-client as a normal JsonRPC target. use futures::{channel::mpsc::Sender, prelude::*, stream::FuturesUnordered}; -use jsonrpsee::core::{ - client::{ - Client as JsonRpseeClient, ClientBuilder, ClientT, ReceivedMessage, TransportReceiverT, - TransportSenderT, - }, - Error, +use jsonrpsee::core::client::{ + Client as JsonRpseeClient, ClientBuilder, ClientT, Error, ReceivedMessage, TransportReceiverT, + TransportSenderT, }; use smoldot_light::{ChainId, Client as SmoldotClient, JsonRpcResponses}; use std::{num::NonZeroU32, sync::Arc}; diff --git a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs index 322bcc93dae6..b716feef1c99 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs @@ -27,7 +27,7 @@ use jsonrpsee::{ core::{ client::{Client as JsonRpcClient, ClientT, Subscription}, params::ArrayParams, - Error as JsonRpseeError, JsonValue, + ClientError as JsonRpseeError, JsonValue, }, ws_client::WsClientBuilder, }; diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index c64fff77a29f..a912997e947d 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -19,7 +19,7 @@ use futures::channel::{ oneshot::Sender as OneshotSender, }; use jsonrpsee::{ - core::{params::ArrayParams, Error as JsonRpseeError}, + core::{params::ArrayParams, ClientError as JsonRpseeError}, rpc_params, }; use serde::de::DeserializeOwned; diff --git a/cumulus/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index c66c96056b95..03071a4cf151 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.4.18", features = ["derive"] } log = "0.4.20" codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { version = "1.0.195", features = ["derive"] } -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.28" serde_json = "1.0.111" diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index e540c430872a..adf77231684c 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -38,7 +38,7 @@ coretime-rococo-runtime = { path = "../parachains/runtimes/coretime/coretime-roc coretime-westend-runtime = { path = "../parachains/runtimes/coretime/coretime-westend" } bridge-hub-westend-runtime = { path = "../parachains/runtimes/bridge-hubs/bridge-hub-westend" } penpal-runtime = { path = "../parachains/runtimes/testing/penpal" } -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } people-rococo-runtime = { path = "../parachains/runtimes/people/people-rococo" } people-westend-runtime = { path = "../parachains/runtimes/people/people-westend" } parachains-common = { path = "../parachains/common" } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index c17cdf35419a..b710146ffe8e 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -17,7 +17,7 @@ async-trait = "0.1.74" clap = { version = "4.4.18", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } rand = "0.8.5" serde = { version = "1.0.195", features = ["derive"] } serde_json = "1.0.111" diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml index 98ce2e482ce1..5af5e63b1753 100644 --- a/polkadot/rpc/Cargo.toml +++ b/polkadot/rpc/Cargo.toml @@ -10,7 +10,7 @@ description = "Polkadot specific RPC functionality." workspace = true [dependencies] -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } polkadot-primitives = { path = "../primitives" } sc-client-api = { path = "../../substrate/client/api" } sp-blockchain = { path = "../../substrate/primitives/blockchain" } diff --git a/substrate/bin/minimal/node/Cargo.toml b/substrate/bin/minimal/node/Cargo.toml index 34476a72954d..2cea39d0df8e 100644 --- a/substrate/bin/minimal/node/Cargo.toml +++ b/substrate/bin/minimal/node/Cargo.toml @@ -23,7 +23,7 @@ name = "minimal-node" clap = { version = "4.4.18", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } serde_json = "1.0.111" sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index da601a665e9c..0dcf2fefc43c 100644 --- a/substrate/bin/node-template/node/Cargo.toml +++ b/substrate/bin/node-template/node/Cargo.toml @@ -48,7 +48,7 @@ frame-system = { path = "../../../frame/system" } pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } # These dependencies are used for the node template's RPCs -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } sp-api = { path = "../../../primitives/api" } sc-rpc-api = { path = "../../../client/rpc-api" } sp-blockchain = { path = "../../../primitives/blockchain" } diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 5dfe915b789d..fed725752244 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -44,7 +44,7 @@ array-bytes = "6.1" clap = { version = "4.4.18", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } serde = { version = "1.0.195", features = ["derive"] } -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.21" log = "0.4.17" rand = "0.8" diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index c17c12dfef13..d28cfbddfd22 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -84,6 +84,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_subs_per_conn: Default::default(), rpc_port: 9944, rpc_message_buffer_capacity: Default::default(), + rpc_rate_limit: None, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 0d0d3a072d89..1e25b7ce6fd8 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -80,6 +80,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { rpc_max_subs_per_conn: Default::default(), rpc_port: 9944, rpc_message_buffer_capacity: Default::default(), + rpc_rate_limit: None, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 63a30965a160..894dbf0da85c 100644 --- a/substrate/bin/node/rpc/Cargo.toml +++ b/substrate/bin/node/rpc/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } node-primitives = { path = "../primitives" } pallet-transaction-payment-rpc = { path = "../../../frame/transaction-payment/rpc" } mmr-rpc = { path = "../../../client/merkle-mountain-range/rpc" } diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index f7b0fc510491..ecff7eead7b6 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -34,7 +34,10 @@ use sc_service::{ ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + num::NonZeroU32, +}; /// The `run` command used to run a node. #[derive(Debug, Clone, Parser)] @@ -82,6 +85,15 @@ pub struct RunCmd { )] pub rpc_methods: RpcMethods, + /// RPC rate limiting (calls/minute) for each connection. + /// + /// This is disabled by default. + /// + /// For example `--rpc-rate-limit 10` will maximum allow + /// 10 calls per minute per connection. + #[arg(long)] + pub rpc_rate_limit: Option, + /// Set the maximum RPC request payload size for both HTTP and WS in megabytes. #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] pub rpc_max_request_size: u32, @@ -399,6 +411,10 @@ impl CliConfiguration for RunCmd { Ok(self.rpc_max_subscriptions_per_connection) } + fn rpc_rate_limit(&self) -> Result> { + Ok(self.rpc_rate_limit) + } + fn transaction_pool(&self, is_dev: bool) -> Result { Ok(self.pool_config.transaction_pool(is_dev)) } diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index defcc4a8a690..c21e81e1a0af 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -33,7 +33,7 @@ use sc_service::{ BlocksPruning, ChainSpec, TracingReceiver, }; use sc_tracing::logging::LoggerBuilder; -use std::{net::SocketAddr, path::PathBuf}; +use std::{net::SocketAddr, num::NonZeroU32, path::PathBuf}; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; @@ -338,6 +338,11 @@ pub trait CliConfiguration: Sized { Ok(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN) } + /// Rate limit calls per minute. + fn rpc_rate_limit(&self) -> Result> { + Ok(None) + } + /// Get the prometheus configuration (`None` if disabled) /// /// By default this is `None`. @@ -510,6 +515,7 @@ pub trait CliConfiguration: Sized { rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, rpc_port: DCV::rpc_listen_port(), rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, + rpc_rate_limit: self.rpc_rate_limit()?, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, @@ -615,14 +621,15 @@ pub trait CliConfiguration: Sized { logger.init()?; match fdlimit::raise_fd_limit() { - Ok(fdlimit::Outcome::LimitRaised { to, .. }) => + Ok(fdlimit::Outcome::LimitRaised { to, .. }) => { if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { warn!( "Low open file descriptor limit configured for the process. \ Current value: {:?}, recommended value: {:?}.", to, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT, ); - }, + } + }, Ok(fdlimit::Outcome::Unsupported) => { // Unsupported platform (non-Linux) }, @@ -648,7 +655,7 @@ pub fn generate_node_name() -> String { let count = node_name.chars().count(); if count < NODE_NAME_MAX_LENGTH { - return node_name + return node_name; } } } diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index e37c8ab0e551..228a4864008f 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -270,6 +270,7 @@ mod tests { rpc_id_provider: Default::default(), rpc_max_subs_per_conn: Default::default(), rpc_message_buffer_capacity: Default::default(), + rpc_rate_limit: None, rpc_port: 9944, prometheus_config: None, telemetry_endpoints: None, diff --git a/substrate/client/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index 2ca029444d07..49b4b1a4a6d8 100644 --- a/substrate/client/consensus/babe/rpc/Cargo.toml +++ b/substrate/client/consensus/babe/rpc/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" serde = { version = "1.0.195", features = ["derive"] } thiserror = "1.0" diff --git a/substrate/client/consensus/babe/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs index 307b1f955ba2..8b183e7dfdcd 100644 --- a/substrate/client/consensus/babe/rpc/src/lib.rs +++ b/substrate/client/consensus/babe/rpc/src/lib.rs @@ -260,7 +260,7 @@ mod tests { let (response, _) = api.raw_json_request(request, 1).await.unwrap(); let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#; - assert_eq!(&response.result, expected); + assert_eq!(response, expected); } #[tokio::test] @@ -272,6 +272,6 @@ mod tests { let (response, _) = api.raw_json_request(request, 1).await.unwrap(); let expected = r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"#; - assert_eq!(&response.result, expected); + assert_eq!(response, expected); } } diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 496aefac113a..b35a4d94c11b 100644 --- a/substrate/client/consensus/beefy/rpc/Cargo.toml +++ b/substrate/client/consensus/beefy/rpc/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } futures = "0.3.21" -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } log = "0.4" parking_lot = "0.12.1" serde = { version = "1.0.195", features = ["derive"] } diff --git a/substrate/client/consensus/beefy/rpc/src/lib.rs b/substrate/client/consensus/beefy/rpc/src/lib.rs index 03c83e92716c..f01baee2d6ec 100644 --- a/substrate/client/consensus/beefy/rpc/src/lib.rs +++ b/substrate/client/consensus/beefy/rpc/src/lib.rs @@ -184,10 +184,10 @@ mod tests { async fn uninitialized_rpc_handler() { let (rpc, _) = setup_io_handler(); let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#; - let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#.to_string(); + let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); - assert_eq!(expected_response, response.result); + assert_eq!(expected_response, response); } #[tokio::test] @@ -205,20 +205,18 @@ mod tests { \"jsonrpc\":\"2.0\",\ \"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\",\ \"id\":1\ - }" - .to_string(); + }"; let not_ready = "{\ \"jsonrpc\":\"2.0\",\ \"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"},\ \"id\":1\ - }" - .to_string(); + }"; let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); while std::time::Instant::now() < deadline { let (response, _) = io.raw_json_request(request, 1).await.expect("RPC requests work"); - if response.result != not_ready { - assert_eq!(response.result, expected); + if response != not_ready { + assert_eq!(response, expected); // Success return } @@ -249,7 +247,7 @@ mod tests { .unwrap(); let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(response.result, expected); + assert_eq!(response, expected); } fn create_finality_proof() -> BeefyVersionedFinalityProof { diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index ae9ab5d9d079..76fba9f9d7e2 100644 --- a/substrate/client/consensus/grandpa/rpc/Cargo.toml +++ b/substrate/client/consensus/grandpa/rpc/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [dependencies] finality-grandpa = { version = "0.16.2", features = ["derive-codec"] } futures = "0.3.16" -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } log = "0.4.8" parity-scale-codec = { version = "3.6.1", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] } diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index 878cefacc479..0557eab93e29 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -273,7 +273,7 @@ mod tests { let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); - assert_eq!(expected_response, response.result); + assert_eq!(expected_response, response); } #[tokio::test] @@ -295,7 +295,7 @@ mod tests { let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#; let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap(); - assert_eq!(expected_response, response.result); + assert_eq!(expected_response, response); } #[tokio::test] @@ -317,7 +317,7 @@ mod tests { .unwrap(); let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; - assert_eq!(response.result, expected); + assert_eq!(response, expected); } fn create_justification() -> GrandpaJustification { diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index 0094fb878009..6136d23a3462 100644 --- a/substrate/client/consensus/manual-seal/Cargo.toml +++ b/substrate/client/consensus/manual-seal/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } assert_matches = "1.3.0" async-trait = "0.1.74" codec = { package = "parity-scale-codec", version = "3.6.1" } diff --git a/substrate/client/merkle-mountain-range/rpc/Cargo.toml b/substrate/client/merkle-mountain-range/rpc/Cargo.toml index 99467e5468f0..79286cf961d2 100644 --- a/substrate/client/merkle-mountain-range/rpc/Cargo.toml +++ b/substrate/client/merkle-mountain-range/rpc/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } serde = { version = "1.0.195", features = ["derive"] } sp-api = { path = "../../../primitives/api" } sp-blockchain = { path = "../../../primitives/blockchain" } diff --git a/substrate/client/rpc-api/Cargo.toml b/substrate/client/rpc-api/Cargo.toml index 8e781151ba8d..45f47920e07b 100644 --- a/substrate/client/rpc-api/Cargo.toml +++ b/substrate/client/rpc-api/Cargo.toml @@ -28,4 +28,4 @@ sp-core = { path = "../../primitives/core" } sp-rpc = { path = "../../primitives/rpc" } sp-runtime = { path = "../../primitives/runtime" } sp-version = { path = "../../primitives/version" } -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index b624a14e263a..3aeadc1dec2a 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } log = "0.4.17" serde_json = "1.0.111" tokio = { version = "1.22.0", features = ["parking_lot"] } @@ -24,3 +24,7 @@ prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../.. tower-http = { version = "0.4.0", features = ["cors"] } tower = { version = "0.4.13", features = ["util"] } http = "0.2.8" +hyper = "0.14.27" +futures = "0.3.29" +pin-project = "1.1.3" +governor = "0.6.0" diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index 5d8da190f627..a22b7309ac19 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -22,21 +22,34 @@ pub mod middleware; -use std::{error::Error as StdError, net::SocketAddr, time::Duration}; +use std::{ + convert::Infallible, error::Error as StdError, net::SocketAddr, num::NonZeroU32, time::Duration, +}; use http::header::HeaderValue; +use hyper::{ + server::conn::AddrStream, + service::{make_service_fn, service_fn}, +}; use jsonrpsee::{ - server::middleware::{HostFilterLayer, ProxyGetRequestLayer}, - RpcModule, + server::{ + middleware::http::{HostFilterLayer, ProxyGetRequestLayer}, + stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder, + }, + Methods, RpcModule, }; use tokio::net::TcpListener; +use tower::Service; use tower_http::cors::{AllowOrigin, CorsLayer}; -pub use crate::middleware::RpcMetrics; -pub use jsonrpsee::core::{ - id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, - traits::IdProvider, +pub use jsonrpsee::{ + core::{ + id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, + traits::IdProvider, + }, + server::middleware::rpc::RpcServiceBuilder, }; +pub use middleware::{MetricsLayer, RateLimitLayer, RpcMetrics}; const MEGABYTE: u32 = 1024 * 1024; @@ -68,12 +81,26 @@ pub struct Config<'a, M: Send + Sync + 'static> { pub id_provider: Option>, /// Tokio runtime handle. pub tokio_handle: tokio::runtime::Handle, + /// Rate limit calls per minute. + pub rate_limit: Option, +} + +#[derive(Debug, Clone)] +struct PerConnection { + methods: Methods, + stop_handle: StopHandle, + metrics: Option, + tokio_handle: tokio::runtime::Handle, + service_builder: TowerServiceBuilder, } /// Start RPC server listening on given address. -pub async fn start_server( +pub async fn start_server( config: Config<'_, M>, -) -> Result> { +) -> Result> +where + M: Send + Sync, +{ let Config { addrs, cors, @@ -86,13 +113,14 @@ pub async fn start_server( id_provider, tokio_handle, rpc_api, + rate_limit, } = config; let std_listener = TcpListener::bind(addrs.as_slice()).await?.into_std()?; let local_addr = std_listener.local_addr().ok(); let host_filter = hosts_filtering(cors.is_some(), local_addr); - let middleware = tower::ServiceBuilder::new() + let http_middleware = tower::ServiceBuilder::new() .option_layer(host_filter) // Proxy `GET /health` requests to internal `system_health` method. .layer(ProxyGetRequestLayer::new("/health", "system_health")?) @@ -103,10 +131,15 @@ pub async fn start_server( .max_response_body_size(max_payload_out_mb.saturating_mul(MEGABYTE)) .max_connections(max_connections) .max_subscriptions_per_connection(max_subs_per_conn) - .ping_interval(Duration::from_secs(30)) - .set_middleware(middleware) + .enable_ws_ping( + PingConfig::new() + .ping_interval(Duration::from_secs(30)) + .inactive_limit(Duration::from_secs(60)) + .max_failures(3), + ) + .set_http_middleware(http_middleware) .set_message_buffer_capacity(message_buffer_capacity) - .custom_tokio_runtime(tokio_handle); + .custom_tokio_runtime(tokio_handle.clone()); if let Some(provider) = id_provider { builder = builder.set_id_provider(provider); @@ -114,22 +147,72 @@ pub async fn start_server( builder = builder.set_id_provider(RandomStringIdProvider::new(16)); }; - let rpc_api = build_rpc_api(rpc_api); - let handle = if let Some(metrics) = metrics { - let server = builder.set_logger(metrics).build_from_tcp(std_listener)?; - server.start(rpc_api) - } else { - let server = builder.build_from_tcp(std_listener)?; - server.start(rpc_api) + let (stop_handle, server_handle) = stop_channel(); + let cfg = PerConnection { + methods: build_rpc_api(rpc_api).into(), + service_builder: builder.to_service_builder(), + metrics, + tokio_handle, + stop_handle: stop_handle.clone(), }; + let make_service = make_service_fn(move |_conn: &AddrStream| { + let cfg = cfg.clone(); + + async move { + let cfg = cfg.clone(); + + Ok::<_, Infallible>(service_fn(move |req| { + let PerConnection { service_builder, metrics, tokio_handle, stop_handle, methods } = + cfg.clone(); + + let is_websocket = ws::is_upgrade_request(&req); + let transport_label = if is_websocket { "ws" } else { "http" }; + + let metrics = metrics.map(|m| MetricsLayer::new(m, transport_label)); + let rate_limit = rate_limit.map(|r| RateLimitLayer::per_minute(r)); + + // NOTE: The metrics needs to run first to include rate-limited calls in the + // metrics. + let rpc_middleware = + RpcServiceBuilder::new().option_layer(metrics.clone()).option_layer(rate_limit); + + let mut svc = + service_builder.set_rpc_middleware(rpc_middleware).build(methods, stop_handle); + + async move { + if is_websocket { + let on_disconnect = svc.on_session_closed(); + + // Spawn a task to handle when the connection is closed. + tokio_handle.spawn(async move { + let now = std::time::Instant::now(); + metrics.as_ref().map(|m| m.ws_connect()); + on_disconnect.await; + metrics.as_ref().map(|m| m.ws_disconnect(now)); + }); + } + + svc.call(req).await + } + })) + } + }); + + let server = hyper::Server::from_tcp(std_listener)?.serve(make_service); + + tokio::spawn(async move { + let graceful = server.with_graceful_shutdown(async move { stop_handle.shutdown().await }); + let _ = graceful.await; + }); + log::info!( "Running JSON-RPC server: addr={}, allowed origins={}", local_addr.map_or_else(|| "unknown".to_string(), |a| a.to_string()), format_cors(cors) ); - Ok(handle) + Ok(server_handle) } fn hosts_filtering(enabled: bool, addr: Option) -> Option { diff --git a/substrate/client/rpc-servers/src/middleware.rs b/substrate/client/rpc-servers/src/middleware.rs deleted file mode 100644 index fabb64eafa79..000000000000 --- a/substrate/client/rpc-servers/src/middleware.rs +++ /dev/null @@ -1,226 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! RPC middleware to collect prometheus metrics on RPC calls. - -use jsonrpsee::server::logger::{ - HttpRequest, Logger, MethodKind, Params, SuccessOrError, TransportProtocol, -}; -use prometheus_endpoint::{ - register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, - U64, -}; -use std::net::SocketAddr; - -/// Histogram time buckets in microseconds. -const HISTOGRAM_BUCKETS: [f64; 11] = [ - 5.0, - 25.0, - 100.0, - 500.0, - 1_000.0, - 2_500.0, - 10_000.0, - 25_000.0, - 100_000.0, - 1_000_000.0, - 10_000_000.0, -]; - -/// Metrics for RPC middleware storing information about the number of requests started/completed, -/// calls started/completed and their timings. -#[derive(Debug, Clone)] -pub struct RpcMetrics { - /// Number of RPC requests received since the server started. - requests_started: CounterVec, - /// Number of RPC requests completed since the server started. - requests_finished: CounterVec, - /// Histogram over RPC execution times. - calls_time: HistogramVec, - /// Number of calls started. - calls_started: CounterVec, - /// Number of calls completed. - calls_finished: CounterVec, - /// Number of Websocket sessions opened. - ws_sessions_opened: Option>, - /// Number of Websocket sessions closed. - ws_sessions_closed: Option>, -} - -impl RpcMetrics { - /// Create an instance of metrics - pub fn new(metrics_registry: Option<&Registry>) -> Result, PrometheusError> { - if let Some(metrics_registry) = metrics_registry { - Ok(Some(Self { - requests_started: register( - CounterVec::new( - Opts::new( - "substrate_rpc_requests_started", - "Number of RPC requests (not calls) received by the server.", - ), - &["protocol"], - )?, - metrics_registry, - )?, - requests_finished: register( - CounterVec::new( - Opts::new( - "substrate_rpc_requests_finished", - "Number of RPC requests (not calls) processed by the server.", - ), - &["protocol"], - )?, - metrics_registry, - )?, - calls_time: register( - HistogramVec::new( - HistogramOpts::new( - "substrate_rpc_calls_time", - "Total time [μs] of processed RPC calls", - ) - .buckets(HISTOGRAM_BUCKETS.to_vec()), - &["protocol", "method"], - )?, - metrics_registry, - )?, - calls_started: register( - CounterVec::new( - Opts::new( - "substrate_rpc_calls_started", - "Number of received RPC calls (unique un-batched requests)", - ), - &["protocol", "method"], - )?, - metrics_registry, - )?, - calls_finished: register( - CounterVec::new( - Opts::new( - "substrate_rpc_calls_finished", - "Number of processed RPC calls (unique un-batched requests)", - ), - &["protocol", "method", "is_error"], - )?, - metrics_registry, - )?, - ws_sessions_opened: register( - Counter::new( - "substrate_rpc_sessions_opened", - "Number of persistent RPC sessions opened", - )?, - metrics_registry, - )? - .into(), - ws_sessions_closed: register( - Counter::new( - "substrate_rpc_sessions_closed", - "Number of persistent RPC sessions closed", - )?, - metrics_registry, - )? - .into(), - })) - } else { - Ok(None) - } - } -} - -impl Logger for RpcMetrics { - type Instant = std::time::Instant; - - fn on_connect( - &self, - _remote_addr: SocketAddr, - _request: &HttpRequest, - transport: TransportProtocol, - ) { - if let TransportProtocol::WebSocket = transport { - self.ws_sessions_opened.as_ref().map(|counter| counter.inc()); - } - } - - fn on_request(&self, transport: TransportProtocol) -> Self::Instant { - let transport_label = transport_label_str(transport); - let now = std::time::Instant::now(); - self.requests_started.with_label_values(&[transport_label]).inc(); - now - } - - fn on_call(&self, name: &str, params: Params, kind: MethodKind, transport: TransportProtocol) { - let transport_label = transport_label_str(transport); - log::trace!( - target: "rpc_metrics", - "[{}] on_call name={} params={:?} kind={}", - transport_label, - name, - params, - kind, - ); - self.calls_started.with_label_values(&[transport_label, name]).inc(); - } - - fn on_result( - &self, - name: &str, - success_or_error: SuccessOrError, - started_at: Self::Instant, - transport: TransportProtocol, - ) { - let transport_label = transport_label_str(transport); - let micros = started_at.elapsed().as_micros(); - log::debug!( - target: "rpc_metrics", - "[{}] {} call took {} μs", - transport_label, - name, - micros, - ); - self.calls_time.with_label_values(&[transport_label, name]).observe(micros as _); - - self.calls_finished - .with_label_values(&[ - transport_label, - name, - // the label "is_error", so `success` should be regarded as false - // and vice-versa to be registrered correctly. - if success_or_error.is_success() { "false" } else { "true" }, - ]) - .inc(); - } - - fn on_response(&self, result: &str, started_at: Self::Instant, transport: TransportProtocol) { - let transport_label = transport_label_str(transport); - log::trace!(target: "rpc_metrics", "[{}] on_response started_at={:?}", transport_label, started_at); - log::trace!(target: "rpc_metrics::extra", "[{}] result={:?}", transport_label, result); - self.requests_finished.with_label_values(&[transport_label]).inc(); - } - - fn on_disconnect(&self, _remote_addr: SocketAddr, transport: TransportProtocol) { - if let TransportProtocol::WebSocket = transport { - self.ws_sessions_closed.as_ref().map(|counter| counter.inc()); - } - } -} - -fn transport_label_str(t: TransportProtocol) -> &'static str { - match t { - TransportProtocol::Http => "http", - TransportProtocol::WebSocket => "ws", - } -} diff --git a/substrate/client/rpc-servers/src/middleware/metrics.rs b/substrate/client/rpc-servers/src/middleware/metrics.rs new file mode 100644 index 000000000000..c2d1956c3b39 --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/metrics.rs @@ -0,0 +1,281 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! RPC middleware to collect prometheus metrics on RPC calls. + +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, + time::Instant, +}; + +use jsonrpsee::{server::middleware::rpc::RpcServiceT, types::Request, MethodResponse}; +use pin_project::pin_project; +use prometheus_endpoint::{ + register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, + U64, +}; + +/// Histogram time buckets in microseconds. +const HISTOGRAM_BUCKETS: [f64; 11] = [ + 5.0, + 25.0, + 100.0, + 500.0, + 1_000.0, + 2_500.0, + 10_000.0, + 25_000.0, + 100_000.0, + 1_000_000.0, + 10_000_000.0, +]; + +/// Metrics for RPC middleware storing information about the number of requests started/completed, +/// calls started/completed and their timings. +#[derive(Debug, Clone)] +pub struct RpcMetrics { + /// Histogram over RPC execution times. + calls_time: HistogramVec, + /// Number of calls started. + calls_started: CounterVec, + /// Number of calls completed. + calls_finished: CounterVec, + /// Number of Websocket sessions opened. + ws_sessions_opened: Option>, + /// Number of Websocket sessions closed. + ws_sessions_closed: Option>, + /// Histogram over RPC websocket sessions. + ws_sessions_time: HistogramVec, +} + +impl RpcMetrics { + /// Create an instance of metrics + pub fn new(metrics_registry: Option<&Registry>) -> Result, PrometheusError> { + if let Some(metrics_registry) = metrics_registry { + Ok(Some(Self { + calls_time: register( + HistogramVec::new( + HistogramOpts::new( + "substrate_rpc_calls_time", + "Total time [μs] of processed RPC calls", + ) + .buckets(HISTOGRAM_BUCKETS.to_vec()), + &["protocol", "method"], + )?, + metrics_registry, + )?, + calls_started: register( + CounterVec::new( + Opts::new( + "substrate_rpc_calls_started", + "Number of received RPC calls (unique un-batched requests)", + ), + &["protocol", "method"], + )?, + metrics_registry, + )?, + calls_finished: register( + CounterVec::new( + Opts::new( + "substrate_rpc_calls_finished", + "Number of processed RPC calls (unique un-batched requests)", + ), + &["protocol", "method", "is_error"], + )?, + metrics_registry, + )?, + ws_sessions_opened: register( + Counter::new( + "substrate_rpc_sessions_opened", + "Number of persistent RPC sessions opened", + )?, + metrics_registry, + )? + .into(), + ws_sessions_closed: register( + Counter::new( + "substrate_rpc_sessions_closed", + "Number of persistent RPC sessions closed", + )?, + metrics_registry, + )? + .into(), + ws_sessions_time: register( + HistogramVec::new( + HistogramOpts::new( + "substrate_rpc_sessions_time", + "Total time [s] for each websocket session", + ) + .buckets(HISTOGRAM_BUCKETS.to_vec()), + &["protocol"], + )?, + metrics_registry, + )?, + })) + } else { + Ok(None) + } + } + + pub(crate) fn ws_connect(&self) { + self.ws_sessions_opened.as_ref().map(|counter| counter.inc()); + } + + pub(crate) fn ws_disconnect(&self, now: Instant) { + let micros = now.elapsed().as_secs(); + + self.ws_sessions_closed.as_ref().map(|counter| counter.inc()); + self.ws_sessions_time.with_label_values(&["ws"]).observe(micros as _); + } +} + +/// Metrics layer. +#[derive(Clone)] +pub struct MetricsLayer { + inner: RpcMetrics, + transport_label: &'static str, +} + +impl MetricsLayer { + /// Create a new [`MetricsLayer`]. + pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self { + Self { inner: metrics, transport_label } + } + + pub(crate) fn ws_connect(&self) { + self.inner.ws_connect(); + } + + pub(crate) fn ws_disconnect(&self, now: Instant) { + self.inner.ws_disconnect(now) + } +} + +impl tower::Layer for MetricsLayer { + type Service = Metrics; + + fn layer(&self, inner: S) -> Self::Service { + Metrics::new(inner, self.inner.clone(), self.transport_label) + } +} + +/// Metrics middleware. +#[derive(Clone)] +pub struct Metrics { + service: S, + metrics: RpcMetrics, + transport_label: &'static str, +} + +impl Metrics { + /// Create a new metrics middleware. + pub fn new(service: S, metrics: RpcMetrics, transport_label: &'static str) -> Metrics { + Metrics { service, metrics, transport_label } + } +} + +impl<'a, S> RpcServiceT<'a> for Metrics +where + S: Send + Sync + RpcServiceT<'a>, +{ + type Future = ResponseFuture<'a, S::Future>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let now = Instant::now(); + + log::trace!( + target: "rpc_metrics", + "[{}] on_call name={} params={:?}", + self.transport_label, + req.method_name(), + req.params(), + ); + self.metrics + .calls_started + .with_label_values(&[self.transport_label, req.method_name()]) + .inc(); + + ResponseFuture { + fut: self.service.call(req.clone()), + metrics: self.metrics.clone(), + req, + now, + transport_label: self.transport_label, + } + } +} + +/// Response future for metrics. +#[pin_project] +pub struct ResponseFuture<'a, F> { + #[pin] + fut: F, + metrics: RpcMetrics, + req: Request<'a>, + now: Instant, + transport_label: &'static str, +} + +impl<'a, F> std::fmt::Debug for ResponseFuture<'a, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("ResponseFuture") + } +} + +impl<'a, F: Future> Future for ResponseFuture<'a, F> { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let res = this.fut.poll(cx); + if let Poll::Ready(rp) = &res { + let method_name = this.req.method_name(); + let transport_label = &this.transport_label; + let now = this.now; + let metrics = &this.metrics; + + log::trace!(target: "rpc_metrics", "[{transport_label}] on_response started_at={:?}", now); + log::trace!(target: "rpc_metrics::extra", "[{transport_label}] result={:?}", rp); + + let micros = now.elapsed().as_micros(); + log::debug!( + target: "rpc_metrics", + "[{transport_label}] {method_name} call took {} μs", + micros, + ); + metrics + .calls_time + .with_label_values(&[transport_label, method_name]) + .observe(micros as _); + metrics + .calls_finished + .with_label_values(&[ + transport_label, + method_name, + // the label "is_error", so `success` should be regarded as false + // and vice-versa to be registrered correctly. + if rp.is_success() { "false" } else { "true" }, + ]) + .inc(); + } + res + } +} diff --git a/substrate/client/rpc-servers/src/middleware/mod.rs b/substrate/client/rpc-servers/src/middleware/mod.rs new file mode 100644 index 000000000000..cac516913d32 --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/mod.rs @@ -0,0 +1,27 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! JSON-RPC specific middleware. + +/// Grafana metrics middleware. +pub mod metrics; +/// Rate limit middleware. +pub mod rate_limit; + +pub use metrics::*; +pub use rate_limit::*; diff --git a/substrate/client/rpc-servers/src/middleware/rate_limit.rs b/substrate/client/rpc-servers/src/middleware/rate_limit.rs new file mode 100644 index 000000000000..cdcc3ebf66f7 --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/rate_limit.rs @@ -0,0 +1,107 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! RPC rate limiting middleware. + +use std::{num::NonZeroU32, sync::Arc, time::Duration}; + +use futures::future::{BoxFuture, FutureExt}; +use governor::{ + clock::{Clock, DefaultClock, QuantaClock}, + middleware::NoOpMiddleware, + state::{InMemoryState, NotKeyed}, + Jitter, +}; +use jsonrpsee::{ + server::middleware::rpc::RpcServiceT, + types::{ErrorObject, Id, Request}, + MethodResponse, +}; + +type RateLimitInner = governor::RateLimiter; + +const MAX_JITTER: Duration = Duration::from_millis(50); +const MAX_RETRIES: usize = 10; + +/// JSON-RPC rate limit middleware layer. +#[derive(Debug, Clone)] +pub struct RateLimitLayer(governor::Quota); + +impl RateLimitLayer { + /// Create new rate limit enforced per minute. + pub fn per_minute(n: NonZeroU32) -> Self { + Self(governor::Quota::per_minute(n)) + } +} + +/// JSON-RPC rate limit middleware +pub struct RateLimit { + service: S, + rate_limit: Arc, + clock: QuantaClock, +} + +impl tower::Layer for RateLimitLayer { + type Service = RateLimit; + + fn layer(&self, service: S) -> Self::Service { + let clock = QuantaClock::default(); + RateLimit { + service, + rate_limit: Arc::new(RateLimitInner::direct_with_clock(self.0, &clock)), + clock, + } + } +} + +impl<'a, S> RpcServiceT<'a> for RateLimit +where + S: Send + Sync + RpcServiceT<'a> + Clone + 'static, +{ + type Future = BoxFuture<'a, MethodResponse>; + + fn call(&self, req: Request<'a>) -> Self::Future { + let service = self.service.clone(); + let rate_limit = self.rate_limit.clone(); + let clock = self.clock.clone(); + + async move { + let mut attempts = 0; + let jitter = Jitter::up_to(MAX_JITTER); + + loop { + if attempts >= MAX_RETRIES { + break reject_too_many_calls(req.id); + } + + if let Err(rejected) = rate_limit.check() { + tokio::time::sleep(jitter + rejected.wait_time_from(clock.now())).await; + } else { + break service.call(req).await; + } + + attempts += 1; + } + } + .boxed() + } +} + +fn reject_too_many_calls(id: Id) -> MethodResponse { + MethodResponse::error(id, ErrorObject::owned(-32999, "RPC rate limit exceeded", None::<()>)) +} diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 6d0e7e078485..9074a6fd11ac 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -16,7 +16,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } # Internal chain structures for "chain_spec". sc-chain-spec = { path = "../chain-spec" } # Pool for submitting extrinsics required by "transaction" diff --git a/substrate/client/rpc-spec-v2/src/chain_head/api.rs b/substrate/client/rpc-spec-v2/src/chain_head/api.rs index 7c3b8d81c82a..00000e1fb277 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/api.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/api.rs @@ -26,7 +26,7 @@ use crate::{ }, common::events::StorageQuery, }; -use jsonrpsee::proc_macros::rpc; +use jsonrpsee::{proc_macros::rpc, server::ResponsePayload}; use sp_rpc::list::ListOrValue; #[rpc(client, server)] @@ -59,7 +59,7 @@ pub trait ChainHeadApi { &self, follow_subscription: String, hash: Hash, - ) -> Result; + ) -> ResponsePayload<'static, MethodResponse>; /// Retrieves the header of a pinned block. /// @@ -92,7 +92,7 @@ pub trait ChainHeadApi { hash: Hash, items: Vec>, child_trie: Option, - ) -> Result; + ) -> ResponsePayload<'static, MethodResponse>; /// Call into the Runtime API at a specified block's state. /// @@ -106,7 +106,7 @@ pub trait ChainHeadApi { hash: Hash, function: String, call_parameters: String, - ) -> Result; + ) -> ResponsePayload<'static, MethodResponse>; /// Unpin a block or multiple blocks reported by the `follow` method. /// diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs index 0e207addcaeb..9362cfd4ccd4 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -36,7 +36,8 @@ use crate::{ use codec::Encode; use futures::future::FutureExt; use jsonrpsee::{ - core::async_trait, types::SubscriptionId, PendingSubscriptionSink, SubscriptionSink, + core::async_trait, server::ResponsePayload, types::SubscriptionId, MethodResponseFuture, + PendingSubscriptionSink, SubscriptionSink, }; use log::debug; use sc_client_api::{ @@ -218,16 +219,17 @@ where &self, follow_subscription: String, hash: Block::Hash, - ) -> Result { + ) -> ResponsePayload<'static, MethodResponse> { let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | - Err(SubscriptionManagementError::ExceededLimits) => return Ok(MethodResponse::LimitReached), + Err(SubscriptionManagementError::ExceededLimits) => + return ResponsePayload::success(MethodResponse::LimitReached), Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock); }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; let operation_id = block_guard.operation().operation_id(); @@ -254,7 +256,7 @@ where hash ); self.subscriptions.remove_subscription(&follow_subscription); - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, Err(error) => FollowEvent::::OperationError(OperationError { operation_id: operation_id.clone(), @@ -262,8 +264,20 @@ where }), }; - let _ = block_guard.response_sender().unbounded_send(event); - Ok(MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items: None })) + let (rp, rp_fut) = method_started_response(operation_id, None); + + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_fut.await.is_err() { + return; + } + let _ = block_guard.response_sender().unbounded_send(event); + }; + + self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + + rp } fn chain_head_unstable_header( @@ -294,31 +308,40 @@ where hash: Block::Hash, items: Vec>, child_trie: Option, - ) -> Result { + ) -> ResponsePayload<'static, MethodResponse> { // Gain control over parameter parsing and returned error. - let items = items + let items = match items .into_iter() .map(|query| { let key = StorageKey(parse_hex_param(query.key)?); Ok(StorageQuery { key, query_type: query.query_type }) }) - .collect::, ChainHeadRpcError>>()?; + .collect::, ChainHeadRpcError>>() + { + Ok(items) => items, + Err(err) => { + return ResponsePayload::error(err); + }, + }; - let child_trie = child_trie - .map(|child_trie| parse_hex_param(child_trie)) - .transpose()? - .map(ChildInfo::new_default_from_vec); + let child_trie = match child_trie.map(|child_trie| parse_hex_param(child_trie)).transpose() + { + Ok(c) => c.map(ChildInfo::new_default_from_vec), + Err(e) => return ResponsePayload::error(e), + }; let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, items.len()) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | - Err(SubscriptionManagementError::ExceededLimits) => return Ok(MethodResponse::LimitReached), + Err(SubscriptionManagementError::ExceededLimits) => { + return ResponsePayload::success(MethodResponse::LimitReached); + }, Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; let mut storage_client = ChainHeadStorage::::new( @@ -334,16 +357,21 @@ where let mut items = items; items.truncate(num_operations); + let (rp, rp_is_success) = method_started_response(operation_id, Some(discarded)); + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_is_success.await.is_err() { + return; + } storage_client.generate_events(block_guard, hash, items, child_trie).await; }; self.executor .spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - Ok(MethodResponse::Started(MethodResponseStarted { - operation_id, - discarded_items: Some(discarded), - })) + + rp } fn chain_head_unstable_call( @@ -352,29 +380,31 @@ where hash: Block::Hash, function: String, call_parameters: String, - ) -> Result { - let call_parameters = Bytes::from(parse_hex_param(call_parameters)?); + ) -> ResponsePayload<'static, MethodResponse> { + let call_parameters = match parse_hex_param(call_parameters) { + Ok(hex) => Bytes::from(hex), + Err(err) => return ResponsePayload::error(err), + }; let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | Err(SubscriptionManagementError::ExceededLimits) => { // Invalid invalid subscription ID. - return Ok(MethodResponse::LimitReached) + return ResponsePayload::success(MethodResponse::LimitReached) }, Err(SubscriptionManagementError::BlockHashAbsent) => { // Block is not part of the subscription. - return Err(ChainHeadRpcError::InvalidBlock.into()) + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) }, - Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; // Reject subscription if with_runtime is false. if !block_guard.has_runtime() { - return Err(ChainHeadRpcError::InvalidRuntimeCall( + return ResponsePayload::error(ChainHeadRpcError::InvalidRuntimeCall( "The runtime updates flag must be set".to_string(), - ) - .into()) + )); } let operation_id = block_guard.operation().operation_id(); @@ -395,11 +425,20 @@ where }) }); - let _ = block_guard.response_sender().unbounded_send(event); - Ok(MethodResponse::Started(MethodResponseStarted { - operation_id: operation_id.clone(), - discarded_items: None, - })) + let (rp, rp_fut) = method_started_response(operation_id, None); + + let fut = async move { + // Events should only by generated + // if the response was successfully propagated. + if rp_fut.await.is_err() { + return; + } + let _ = block_guard.response_sender().unbounded_send(event); + }; + + self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + + rp } fn chain_head_unstable_unpin( @@ -461,3 +500,11 @@ where Ok(()) } } + +fn method_started_response( + operation_id: String, + discarded_items: Option, +) -> (ResponsePayload<'static, MethodResponse>, MethodResponseFuture) { + let rp = MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items }); + ResponsePayload::success(rp).notify_on_completion() +} \ No newline at end of file diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 955a361e3ead..710d24a51f72 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -27,8 +27,7 @@ use assert_matches::assert_matches; use codec::{Decode, Encode}; use futures::Future; use jsonrpsee::{ - core::{error::Error, server::Subscription as RpcSubscription}, - rpc_params, RpcModule, + core::server::Subscription as RpcSubscription, rpc_params, MethodsError as Error, RpcModule, }; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; @@ -359,7 +358,7 @@ async fn get_header() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Obtain the valid header. @@ -388,7 +387,7 @@ async fn get_body() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call. @@ -473,7 +472,7 @@ async fn call_runtime() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Pass an invalid parameters that cannot be decode. @@ -486,7 +485,7 @@ async fn call_runtime() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter") + Error::JsonRpc(err) if err.code() == super::error::json_rpc_spec::INVALID_PARAM_ERROR && err.message().contains("Invalid parameter") ); // Valid call. @@ -589,7 +588,7 @@ async fn call_runtime_without_flag() { .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`") + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_RUNTIME_CALL && err.message().contains("subscription was started with `withRuntime` set to `false`") ); } @@ -627,7 +626,7 @@ async fn get_storage_hash() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call without storage at the key. @@ -895,7 +894,7 @@ async fn get_storage_value() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Valid call without storage at the key. @@ -1571,7 +1570,7 @@ async fn follow_with_unpin() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(ref err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // To not exceed the number of pinned blocks, we need to unpin before the next import. @@ -1720,7 +1719,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); let _res: () = api @@ -1737,7 +1736,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); // Unpin multiple blocks. @@ -1755,7 +1754,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); let err = api @@ -1766,7 +1765,7 @@ async fn follow_with_multiple_unpin_hashes() { .await .unwrap_err(); assert_matches!(err, - Error::Call(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" + Error::JsonRpc(err) if err.code() == super::error::rpc_spec_v2::INVALID_BLOCK_ERROR && err.message() == "Invalid block hash" ); } diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 6917eb0b551a..defa631d0e7f 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } log = "0.4.17" parking_lot = "0.12.1" serde_json = "1.0.111" diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 1c95112aa6b0..06e09fe6d512 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -28,7 +28,7 @@ runtime-benchmarks = [ ] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } thiserror = "1.0.48" futures = "0.3.21" rand = "0.8.5" diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 3e68f5b58def..74c5dd775b0e 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -39,6 +39,7 @@ use sp_core::crypto::SecretString; use std::{ io, iter, net::SocketAddr, + num::NonZeroU32, path::{Path, PathBuf}, }; use tempfile::TempDir; @@ -102,6 +103,8 @@ pub struct Configuration { pub rpc_port: u16, /// The number of messages the JSON-RPC server is allowed to keep in memory. pub rpc_message_buffer_capacity: u32, + /// RPC rate limit per minute. + pub rpc_rate_limit: Option, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 875cb9ca79e2..75eeaf03292f 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -38,7 +38,7 @@ use std::{collections::HashMap, net::SocketAddr}; use codec::{Decode, Encode}; use futures::{pin_mut, FutureExt, StreamExt}; -use jsonrpsee::{core::Error as JsonRpseeError, RpcModule}; +use jsonrpsee::RpcModule; use log::{debug, error, warn}; use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider}; use sc_network::{ @@ -109,17 +109,14 @@ impl RpcHandlers { pub async fn rpc_query( &self, json_query: &str, - ) -> Result<(String, tokio::sync::mpsc::Receiver), JsonRpseeError> { + ) -> Result<(String, tokio::sync::mpsc::Receiver), serde_json::Error> { // Because `tokio::sync::mpsc::channel` is used under the hood // it will panic if it's set to usize::MAX. // // This limit is used to prevent panics and is large enough. const TOKIO_MPSC_MAX_SIZE: usize = tokio::sync::Semaphore::MAX_PERMITS; - self.0 - .raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE) - .await - .map(|(method_res, recv)| (method_res.result, recv)) + self.0.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await } /// Provides access to the underlying `RpcModule` @@ -241,7 +238,7 @@ pub async fn build_system_rpc_future< // Answer incoming RPC requests. let Some(req) = rpc_rx.next().await else { debug!("RPC requests stream has terminated, shutting down the system RPC future."); - return + return; }; match req { @@ -290,7 +287,7 @@ pub async fn build_system_rpc_future< let _ = sender.send(network_state); } } else { - break + break; } }, sc_rpc::system::Request::NetworkAddReservedPeer(peer_addr, sender) => { @@ -319,7 +316,7 @@ pub async fn build_system_rpc_future< reserved_peers.iter().map(|peer_id| peer_id.to_base58()).collect(); let _ = sender.send(reserved_peers); } else { - break + break; } }, sc_rpc::system::Request::NodeRoles(sender) => { @@ -406,6 +403,7 @@ where id_provider: rpc_id_provider, cors: config.rpc_cors.as_ref(), tokio_handle: config.tokio_handle.clone(), + rate_limit: config.rpc_rate_limit, }; // TODO: https://github.com/paritytech/substrate/issues/13773 @@ -482,7 +480,7 @@ where Ok(uxt) => uxt, Err(e) => { debug!("Transaction invalid: {:?}", e); - return Box::pin(futures::future::ready(TransactionImport::Bad)) + return Box::pin(futures::future::ready(TransactionImport::Bad)); }, }; @@ -495,8 +493,9 @@ where match import_future.await { Ok(_) => TransactionImport::NewGood, Err(e) => match e.into_pool_error() { - Ok(sc_transaction_pool_api::error::Error::AlreadyImported(_)) => - TransactionImport::KnownGood, + Ok(sc_transaction_pool_api::error::Error::AlreadyImported(_)) => { + TransactionImport::KnownGood + }, Ok(e) => { debug!("Error adding transaction to the pool: {:?}", e); TransactionImport::Bad diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index 9b88300bf530..6148bb05fcfb 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -254,6 +254,7 @@ fn node_config< rpc_max_subs_per_conn: Default::default(), rpc_port: 9944, rpc_message_buffer_capacity: Default::default(), + rpc_rate_limit: None, prometheus_config: None, telemetry_endpoints: None, default_heap_pages: None, diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 328ecd17f75b..0d22bb105a68 100644 --- a/substrate/client/sync-state-rpc/Cargo.toml +++ b/substrate/client/sync-state-rpc/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } serde = { version = "1.0.195", features = ["derive"] } serde_json = "1.0.111" thiserror = "1.0.48" diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index e7edf8a4b879..6d7f632af828 100644 --- a/substrate/frame/transaction-payment/rpc/Cargo.toml +++ b/substrate/frame/transaction-payment/rpc/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } pallet-transaction-payment-rpc-runtime-api = { path = "runtime-api" } sp-api = { path = "../../../primitives/api" } sp-blockchain = { path = "../../../primitives/blockchain" } diff --git a/substrate/utils/frame/remote-externalities/Cargo.toml b/substrate/utils/frame/remote-externalities/Cargo.toml index fead2d20c705..44260c89085d 100644 --- a/substrate/utils/frame/remote-externalities/Cargo.toml +++ b/substrate/utils/frame/remote-externalities/Cargo.toml @@ -15,7 +15,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["http-client"] } +jsonrpsee = { version = "0.22", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.6.1" } log = "0.4.17" serde = "1.0.195" diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index a97bc77b00f5..6b085c75735a 100644 --- a/substrate/utils/frame/rpc/client/Cargo.toml +++ b/substrate/utils/frame/rpc/client/Cargo.toml @@ -15,7 +15,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpsee = { version = "0.20.3", features = ["ws-client"] } +jsonrpsee = { version = "0.22", features = ["ws-client"] } sc-rpc-api = { path = "../../../../client/rpc-api" } async-trait = "0.1.74" serde = "1" diff --git a/substrate/utils/frame/rpc/client/src/lib.rs b/substrate/utils/frame/rpc/client/src/lib.rs index 9349ee2d357b..2c449fd2db47 100644 --- a/substrate/utils/frame/rpc/client/src/lib.rs +++ b/substrate/utils/frame/rpc/client/src/lib.rs @@ -46,7 +46,7 @@ pub use jsonrpsee::{ core::{ client::{ClientT, Subscription, SubscriptionClientT}, params::BatchRequestBuilder, - Error, RpcResult, + ClientError, RpcResult, }, rpc_params, ws_client::{WsClient, WsClientBuilder}, diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 83afff94a8dc..d42228eefaeb 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -23,7 +23,7 @@ sp-core = { path = "../../../../primitives/core" } sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } trie-db = "0.28.0" -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } # Substrate Dependencies sc-client-api = { path = "../../../../client/api" } diff --git a/substrate/utils/frame/rpc/support/Cargo.toml b/substrate/utils/frame/rpc/support/Cargo.toml index 3750e3272c90..a4b0a7777fbf 100644 --- a/substrate/utils/frame/rpc/support/Cargo.toml +++ b/substrate/utils/frame/rpc/support/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.20.3", features = ["jsonrpsee-types"] } +jsonrpsee = { version = "0.22", features = ["jsonrpsee-types"] } serde = "1" frame-support = { path = "../../../../frame/support" } sc-rpc-api = { path = "../../../../client/rpc-api" } @@ -24,7 +24,7 @@ sp-storage = { path = "../../../../primitives/storage" } [dev-dependencies] scale-info = "2.10.0" -jsonrpsee = { version = "0.20.3", features = ["jsonrpsee-types", "ws-client"] } +jsonrpsee = { version = "0.22", features = ["jsonrpsee-types", "ws-client"] } tokio = "1.22.0" sp-core = { path = "../../../../primitives/core" } sp-runtime = { path = "../../../../primitives/runtime" } diff --git a/substrate/utils/frame/rpc/support/src/lib.rs b/substrate/utils/frame/rpc/support/src/lib.rs index e3ccbd696589..8280c46aadf2 100644 --- a/substrate/utils/frame/rpc/support/src/lib.rs +++ b/substrate/utils/frame/rpc/support/src/lib.rs @@ -23,7 +23,7 @@ use codec::{DecodeAll, FullCodec, FullEncode}; use core::marker::PhantomData; use frame_support::storage::generator::{StorageDoubleMap, StorageMap, StorageValue}; -use jsonrpsee::core::Error as RpcError; +use jsonrpsee::core::ClientError as RpcError; use sc_rpc_api::state::StateApiClient; use serde::{de::DeserializeOwned, Serialize}; use sp_storage::{StorageData, StorageKey}; @@ -31,7 +31,7 @@ use sp_storage::{StorageData, StorageKey}; /// A typed query on chain state usable from an RPC client. /// /// ```no_run -/// # use jsonrpsee::core::Error as RpcError; +/// # use jsonrpsee::core::ClientError as RpcError; /// # use jsonrpsee::ws_client::WsClientBuilder; /// # use codec::Encode; /// # use frame_support::{construct_runtime, derive_impl, traits::ConstU32}; diff --git a/substrate/utils/frame/rpc/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index 3f06ffe2bc2e..67849220ddf3 100644 --- a/substrate/utils/frame/rpc/system/Cargo.toml +++ b/substrate/utils/frame/rpc/system/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -jsonrpsee = { version = "0.20.3", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" log = "0.4.17" frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" }