From d99e17397a94b9c2a25b14d44b09c10ec5cae950 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 23 Feb 2024 10:31:42 +0200 Subject: [PATCH 01/36] Update jsonrpsee to branch PR Signed-off-by: Alexandru Vasile --- Cargo.lock | 79 ++++++++++++------- .../relay-chain-rpc-interface/Cargo.toml | 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 +- substrate/bin/node/rpc/Cargo.toml | 2 +- .../client/consensus/babe/rpc/Cargo.toml | 2 +- .../client/consensus/beefy/rpc/Cargo.toml | 2 +- .../client/consensus/grandpa/rpc/Cargo.toml | 2 +- .../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 | 2 +- substrate/client/rpc-spec-v2/Cargo.toml | 2 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- 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 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/rpc/support/Cargo.toml | 4 +- substrate/utils/frame/rpc/system/Cargo.toml | 2 +- 27 files changed, 77 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 950b4e0889a1..052e9ff2f5fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4114,7 +4114,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core", + "jsonrpsee-core 0.22.0", "parity-scale-codec", "polkadot-overseer", "sc-client-api", @@ -6825,15 +6825,14 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" [[package]] name = "jsonrpsee" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a95f7cc23d5fab0cdeeaf6bad8c8f5e7a3aa7f0d211957ea78232b327ab27b0" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ - "jsonrpsee-core", + "jsonrpsee-core 0.22.1", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types", + "jsonrpsee-types 0.22.1", "jsonrpsee-ws-client", "tokio", "tracing", @@ -6841,13 +6840,12 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1736cfa3845fd9f8f43751f2b8e0e83f7b6081e754502f7d63b6587692cc83" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "futures-util", "http", - "jsonrpsee-core", + "jsonrpsee-core 0.22.1", "pin-project", "rustls-native-certs 0.7.0", "rustls-pki-types", @@ -6865,6 +6863,21 @@ name = "jsonrpsee-core" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82030d038658974732103e623ba2e0abec03bbbe175b39c0a2fafbada60c5868" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "jsonrpsee-types 0.22.0", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "anyhow", "async-lock 3.3.0", @@ -6873,7 +6886,7 @@ dependencies = [ "futures-timer", "futures-util", "hyper", - "jsonrpsee-types", + "jsonrpsee-types 0.22.1", "parking_lot 0.12.1", "pin-project", "rand", @@ -6888,15 +6901,14 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a06ef0de060005fddf772d54597bb6a8b0413da47dcffd304b0306147b9678" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.22.1", + "jsonrpsee-types 0.22.1", "serde", "serde_json", "thiserror", @@ -6908,9 +6920,8 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fc56131589f82e57805f7338b87023db4aafef813555708b159787e34ad6bc" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "heck", "proc-macro-crate 3.0.0", @@ -6921,15 +6932,14 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85be77fe5b2a94589e3164fb780017f7aff7d646b49278c0d0346af16975c8e" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.22.1", + "jsonrpsee-types 0.22.1", "pin-project", "route-recognizer", "serde", @@ -6956,16 +6966,27 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jsonrpsee-types" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "jsonrpsee-ws-client" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ce25d70a8e4d3cc574bbc3cad0137c326ad64b194793d5e7bbdd3fa4504181" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.22.1", + "jsonrpsee-types 0.22.1", "url", ] diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 801712b1ad15..a8200d67030f 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.22", features = ["ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["ws-client"] } tracing = "0.1.37" async-trait = "0.1.74" url = "2.4.0" diff --git a/cumulus/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index 0ef678c4cbae..8a672a376dce 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.5.1", features = ["derive"] } log = { workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { version = "0.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } futures = "0.3.28" serde_json = { workspace = true, default-features = true } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 84a232e954fc..8e146dd3c807 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 b26f0b9967cf..f08f48447063 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.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } -jsonrpsee = { version = "0.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } rand = "0.8.5" serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml index 5af5e63b1753..613d641583e1 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 65644861c9ac..09005a54f1b9 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.5.1", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" -jsonrpsee = { version = "0.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } serde_json = { workspace = true, default-features = true } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index 9cba2b5a3660..13ced0fd08bb 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 abe284c41da1..9958fdac806b 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.5.1", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { version = "0.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } futures = "0.3.21" log = { workspace = true, default-features = true } rand = "0.8" diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 894dbf0da85c..f306e98381e7 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index 043b566673e7..fe5f026933e0 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } futures = "0.3.21" serde = { features = ["derive"], workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index bb2ae4a08966..1a9607c1c477 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index f7e87415448e..59623cddfaaa 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", features = ["derive"] } serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index ac32fed72289..b07d3c77c859 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 9b391b76ea00..678a0084650c 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } 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 1b7af6a4a52f..28697a45507a 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 7f7a86799e0c..df9af63ac291 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } log = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index c62b3e789d38..2e1defd7eb7f 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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/Cargo.toml b/substrate/client/rpc/Cargo.toml index f65e6c9a59ec..d09502f7c0e1 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.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde_json = { workspace = true, default-features = true } diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 73edceb2ef36..3722b9ad4cb8 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -28,7 +28,7 @@ runtime-benchmarks = [ ] [dependencies] -jsonrpsee = { version = "0.22", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } thiserror = { workspace = true } futures = "0.3.21" rand = "0.8.5" diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 09dc611caa04..06cafbeca487 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index 6d7f632af828..81a46b6f53aa 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 61e0a861ee0e..5c5a2fe9291c 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.22", features = ["http-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.6.1" } log = { workspace = true, default-features = true } serde = { workspace = true, default-features = true } diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index b51e3f44f4e6..06932221af49 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.22", features = ["ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["ws-client"] } sc-rpc-api = { path = "../../../../client/rpc-api" } async-trait = "0.1.74" serde = { workspace = true, default-features = true } 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 f9a45e21ce13..518938351103 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -24,7 +24,7 @@ sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } trie-db = "0.28.0" -jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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 2e4bb6a10578..a736b62e7da8 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.22", features = ["jsonrpsee-types"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["jsonrpsee-types"] } serde = { workspace = true, default-features = true } 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.22", features = ["jsonrpsee-types", "ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", 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/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index f9a84a01af82..ac98b6a61a67 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.22", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } futures = "0.3.21" log = { workspace = true, default-features = true } frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" } From dd0a68fe60b5a466a547c4ea456bb7c69d321559 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 11:22:24 +0200 Subject: [PATCH 02/36] rpc-v2: Implement connection limiter struct Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/common/connections.rs | 60 +++++++++++++++++++ .../client/rpc-spec-v2/src/common/mod.rs | 1 + 2 files changed, 61 insertions(+) create mode 100644 substrate/client/rpc-spec-v2/src/common/connections.rs diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs new file mode 100644 index 000000000000..81086438b66d --- /dev/null +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -0,0 +1,60 @@ +// 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 . + +use jsonrpsee::ConnectionId; +use parking_lot::Mutex; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; + +/// Limit the RPC functionality to a single connection. +#[derive(Default, Clone)] +pub struct RpcConnections { + data: Arc>>>, +} + +impl RpcConnections { + /// Constructs a new instance of [`RpcConnections`]. + pub fn new() -> Self { + RpcConnections { data: Default::default() } + } + + /// Register a token for the given connection. + pub fn register_token(&self, connection_id: ConnectionId, token: String) { + let mut data = self.data.lock(); + data.entry(connection_id).or_insert_with(HashSet::new).insert(token); + } + + /// Unregister a token for the given connection. + pub fn unregister_token(&self, connection_id: ConnectionId, token: &str) { + let mut data = self.data.lock(); + if let Some(tokens) = data.get_mut(&connection_id) { + tokens.remove(token); + if tokens.is_empty() { + data.remove(&connection_id); + } + } + } + + /// Check if the given connection contains the given token. + pub fn contains_token(&self, connection_id: ConnectionId, token: &str) -> bool { + let data = self.data.lock(); + data.get(&connection_id).map(|tokens| tokens.contains(token)).unwrap_or(false) + } +} diff --git a/substrate/client/rpc-spec-v2/src/common/mod.rs b/substrate/client/rpc-spec-v2/src/common/mod.rs index ac1af8fce3c9..3167561d649a 100644 --- a/substrate/client/rpc-spec-v2/src/common/mod.rs +++ b/substrate/client/rpc-spec-v2/src/common/mod.rs @@ -13,5 +13,6 @@ //! Common types and functionality for the RPC-V2 spec. +pub mod connections; pub mod events; pub mod storage; From 09b7fc64ced230b4ad566c3ab4fb33a4c41319c3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 11:23:54 +0200 Subject: [PATCH 03/36] chainHead/api: Register raw methods for chainHead methods Signed-off-by: Alexandru Vasile --- .../client/rpc-spec-v2/src/chain_head/api.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 00000e1fb277..cd30fb78d8d8 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/api.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/api.rs @@ -54,8 +54,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_body", blocking)] - fn chain_head_unstable_body( + #[method(name = "chainHead_unstable_body", raw_method)] + async fn chain_head_unstable_body( &self, follow_subscription: String, hash: Hash, @@ -73,8 +73,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_header", blocking)] - fn chain_head_unstable_header( + #[method(name = "chainHead_unstable_header", raw_method)] + async fn chain_head_unstable_header( &self, follow_subscription: String, hash: Hash, @@ -85,8 +85,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_storage", blocking)] - fn chain_head_unstable_storage( + #[method(name = "chainHead_unstable_storage", raw_method)] + async fn chain_head_unstable_storage( &self, follow_subscription: String, hash: Hash, @@ -99,8 +99,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_call", blocking)] - fn chain_head_unstable_call( + #[method(name = "chainHead_unstable_call", raw_method)] + async fn chain_head_unstable_call( &self, follow_subscription: String, hash: Hash, @@ -118,8 +118,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_unpin", blocking)] - fn chain_head_unstable_unpin( + #[method(name = "chainHead_unstable_unpin", raw_method)] + async fn chain_head_unstable_unpin( &self, follow_subscription: String, hash_or_hashes: ListOrValue, @@ -131,8 +131,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_continue", blocking)] - fn chain_head_unstable_continue( + #[method(name = "chainHead_unstable_continue", raw_method)] + async fn chain_head_unstable_continue( &self, follow_subscription: String, operation_id: String, @@ -145,8 +145,8 @@ pub trait ChainHeadApi { /// # Unstable /// /// This method is unstable and subject to change in the future. - #[method(name = "chainHead_unstable_stopOperation", blocking)] - fn chain_head_unstable_stop_operation( + #[method(name = "chainHead_unstable_stopOperation", raw_method)] + async fn chain_head_unstable_stop_operation( &self, follow_subscription: String, operation_id: String, From 7c682a4f8a66a5a813577f0ff9bc8db13ad13bbd Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 11:25:40 +0200 Subject: [PATCH 04/36] chainHead: Allow chainHead to be called from a single connection Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 274 ++++++++++++------ 1 file changed, 178 insertions(+), 96 deletions(-) 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 2bda22b45239..ff3133e765d8 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 @@ -30,14 +30,14 @@ use crate::{ event::{FollowEvent, MethodResponse, OperationError}, subscription::{SubscriptionManagement, SubscriptionManagementError}, }, - common::events::StorageQuery, + common::{connections::RpcConnections, events::StorageQuery}, hex_string, SubscriptionTaskExecutor, }; use codec::Encode; -use futures::future::FutureExt; +use futures::{channel::oneshot, future::FutureExt}; use jsonrpsee::{ - core::async_trait, server::ResponsePayload, types::SubscriptionId, MethodResponseFuture, - PendingSubscriptionSink, SubscriptionSink, + core::async_trait, server::ResponsePayload, types::SubscriptionId, ConnectionId, + MethodResponseFuture, PendingSubscriptionSink, SubscriptionSink, }; use log::debug; use sc_client_api::{ @@ -110,6 +110,8 @@ pub struct ChainHead, Block: BlockT, Client> { /// The maximum number of items reported by the `chainHead_storage` before /// pagination is required. operation_max_storage_items: usize, + /// Limit the RPC functionality to a single connection. + rpc_connections: RpcConnections, /// Phantom member to pin the block type. _phantom: PhantomData, } @@ -133,6 +135,7 @@ impl, Block: BlockT, Client> ChainHead { backend, )), operation_max_storage_items: config.operation_max_storage_items, + rpc_connections: RpcConnections::new(), _phantom: PhantomData, } } @@ -180,6 +183,7 @@ where let subscriptions = self.subscriptions.clone(); let backend = self.backend.clone(); let client = self.client.clone(); + let rpc_connections = self.rpc_connections.clone(); let fut = async move { let Ok(sink) = pending.accept().await else { return }; @@ -197,6 +201,8 @@ where return }; debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); + let connection_id = sink.connection_id(); + rpc_connections.register_token(connection_id, sub_id.clone()); let mut chain_head_follow = ChainHeadFollower::new( client, @@ -209,83 +215,104 @@ where chain_head_follow.generate_events(sink, sub_data).await; subscriptions.remove_subscription(&sub_id); + rpc_connections.unregister_token(connection_id, &sub_id); debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription removed", sub_id); }; self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); } - fn chain_head_unstable_body( + async fn chain_head_unstable_body( &self, + connection_id: ConnectionId, follow_subscription: String, hash: Block::Hash, ) -> 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 ResponsePayload::success(MethodResponse::LimitReached), - Err(SubscriptionManagementError::BlockHashAbsent) => { - // Block is not part of the subscription. - return ResponsePayload::error(ChainHeadRpcError::InvalidBlock); - }, - Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), - }; + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return ResponsePayload::success(MethodResponse::LimitReached); + } - let operation_id = block_guard.operation().operation_id(); + let client = self.client.clone(); + let subscriptions = self.subscriptions.clone(); + let executor = self.executor.clone(); + + let result = spawn_blocking(&self.executor, async move { + let mut block_guard = match subscriptions.lock_block(&follow_subscription, hash, 1) { + Ok(block) => block, + Err(SubscriptionManagementError::SubscriptionAbsent) | + Err(SubscriptionManagementError::ExceededLimits) => + return ResponsePayload::success(MethodResponse::LimitReached), + Err(SubscriptionManagementError::BlockHashAbsent) => { + // Block is not part of the subscription. + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock); + }, + Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), + }; - let event = match self.client.block(hash) { - Ok(Some(signed_block)) => { - let extrinsics = signed_block - .block - .extrinsics() - .iter() - .map(|extrinsic| hex_string(&extrinsic.encode())) - .collect(); - FollowEvent::::OperationBodyDone(OperationBodyDone { + let operation_id = block_guard.operation().operation_id(); + + let event = match client.block(hash) { + Ok(Some(signed_block)) => { + let extrinsics = signed_block + .block + .extrinsics() + .iter() + .map(|extrinsic| hex_string(&extrinsic.encode())) + .collect(); + FollowEvent::::OperationBodyDone(OperationBodyDone { + operation_id: operation_id.clone(), + value: extrinsics, + }) + }, + Ok(None) => { + // The block's body was pruned. This subscription ID has become invalid. + debug!( + target: LOG_TARGET, + "[body][id={:?}] Stopping subscription because hash={:?} was pruned", + &follow_subscription, + hash + ); + subscriptions.remove_subscription(&follow_subscription); + return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) + }, + Err(error) => FollowEvent::::OperationError(OperationError { operation_id: operation_id.clone(), - value: extrinsics, - }) - }, - Ok(None) => { - // The block's body was pruned. This subscription ID has become invalid. - debug!( - target: LOG_TARGET, - "[body][id={:?}] Stopping subscription because hash={:?} was pruned", - &follow_subscription, - hash - ); - self.subscriptions.remove_subscription(&follow_subscription); - return ResponsePayload::error(ChainHeadRpcError::InvalidBlock) - }, - Err(error) => FollowEvent::::OperationError(OperationError { - operation_id: operation_id.clone(), - error: error.to_string(), - }), - }; + error: error.to_string(), + }), + }; - let (rp, rp_fut) = method_started_response(operation_id, None); + let (rp, rp_fut) = method_started_response(operation_id, None); + let fut = async move { + // This leaves on a seperate task, because the `rp` needs to be acknowledged by the + // server. Events should only by generated if the response was successfully + // propagated. + if rp_fut.await.is_err() { + return; + } - 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); - }; + let _ = block_guard.response_sender().unbounded_send(event); + }; + executor.spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + rp + }); - rp + result + .await + .unwrap_or_else(|_| ResponsePayload::success(MethodResponse::LimitReached)) } - fn chain_head_unstable_header( + async fn chain_head_unstable_header( &self, + connection_id: ConnectionId, follow_subscription: String, hash: Block::Hash, ) -> Result, ChainHeadRpcError> { - let _block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return Ok(None); + } + + let block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | Err(SubscriptionManagementError::ExceededLimits) => return Ok(None), @@ -296,19 +323,30 @@ where Err(_) => return Err(ChainHeadRpcError::InvalidBlock.into()), }; - self.client - .header(hash) - .map(|opt_header| opt_header.map(|h| hex_string(&h.encode()))) - .map_err(|err| ChainHeadRpcError::InternalError(err.to_string())) + let client = self.client.clone(); + let result = spawn_blocking(&self.executor, async move { + let _block_guard = block_guard; + + client + .header(hash) + .map(|opt_header| opt_header.map(|h| hex_string(&h.encode()))) + .map_err(|err| ChainHeadRpcError::InternalError(err.to_string())) + }); + result.await.unwrap_or_else(|_| Ok(None)) } - fn chain_head_unstable_storage( + async fn chain_head_unstable_storage( &self, + connection_id: ConnectionId, follow_subscription: String, hash: Block::Hash, items: Vec>, child_trie: Option, ) -> ResponsePayload<'static, MethodResponse> { + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return ResponsePayload::success(MethodResponse::LimitReached); + } + // Gain control over parameter parsing and returned error. let items = match items .into_iter() @@ -357,25 +395,26 @@ where let mut items = items; items.truncate(num_operations); - let (rp, rp_is_success) = method_started_response(operation_id, Some(discarded)); - + let (rp, rp_fut) = 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() { + // This leaves on a seperate task, because the `rp` needs to be acknowledged by the + // server. Events should only by generated if the response was successfully + // propagated. + if rp_fut.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()); rp } - fn chain_head_unstable_call( + async fn chain_head_unstable_call( &self, + connection_id: ConnectionId, follow_subscription: String, hash: Block::Hash, function: String, @@ -386,6 +425,10 @@ where Err(err) => return ResponsePayload::error(err), }; + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return ResponsePayload::success(MethodResponse::LimitReached); + } + let mut block_guard = match self.subscriptions.lock_block(&follow_subscription, hash, 1) { Ok(block) => block, Err(SubscriptionManagementError::SubscriptionAbsent) | @@ -408,44 +451,50 @@ where } let operation_id = block_guard.operation().operation_id(); - let event = self - .client - .executor() - .call(hash, &function, &call_parameters, CallContext::Offchain) - .map(|result| { - FollowEvent::::OperationCallDone(OperationCallDone { - operation_id: operation_id.clone(), - output: hex_string(&result), - }) - }) - .unwrap_or_else(|error| { - FollowEvent::::OperationError(OperationError { - operation_id: operation_id.clone(), - error: error.to_string(), - }) - }); - - let (rp, rp_fut) = method_started_response(operation_id, None); + let client = self.client.clone(); + let (rp, rp_fut) = method_started_response(operation_id.clone(), None); let fut = async move { - // Events should only by generated - // if the response was successfully propagated. + let event = client + .executor() + .call(hash, &function, &call_parameters, CallContext::Offchain) + .map(|result| { + FollowEvent::::OperationCallDone(OperationCallDone { + operation_id: operation_id.clone(), + output: hex_string(&result), + }) + }) + .unwrap_or_else(|error| { + FollowEvent::::OperationError(OperationError { + operation_id: operation_id.clone(), + error: error.to_string(), + }) + }); + + // This leaves on a seperate task, because the `rp` needs to be acknowledged by the + // server. Events should only by generated if the response was successfully + // propagated. if rp_fut.await.is_err() { - return; + return } let _ = block_guard.response_sender().unbounded_send(event); }; - - self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + self.executor + .spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); rp } - fn chain_head_unstable_unpin( + async fn chain_head_unstable_unpin( &self, + connection_id: ConnectionId, follow_subscription: String, hash_or_hashes: ListOrValue, ) -> Result<(), ChainHeadRpcError> { + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return Ok(()); + } + let result = match hash_or_hashes { ListOrValue::Value(hash) => self.subscriptions.unpin_blocks(&follow_subscription, [hash]), @@ -469,11 +518,16 @@ where } } - fn chain_head_unstable_continue( + async fn chain_head_unstable_continue( &self, + connection_id: ConnectionId, follow_subscription: String, operation_id: String, ) -> Result<(), ChainHeadRpcError> { + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return Ok(()) + } + let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) else { return Ok(()) @@ -487,11 +541,16 @@ where } } - fn chain_head_unstable_stop_operation( + async fn chain_head_unstable_stop_operation( &self, + connection_id: ConnectionId, follow_subscription: String, operation_id: String, ) -> Result<(), ChainHeadRpcError> { + if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + return Ok(()) + } + let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) else { return Ok(()) @@ -510,3 +569,26 @@ fn method_started_response( let rp = MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items }); ResponsePayload::success(rp).notify_on_completion() } + +/// Spawn a blocking future on the provided executor and return the result on a oneshot channel. +/// +/// This is a wrapper to extract the result of a `executor.spawn_blocking` future. +fn spawn_blocking( + executor: &SubscriptionTaskExecutor, + fut: impl std::future::Future + Send + 'static, +) -> oneshot::Receiver +where + R: Send + 'static, +{ + let (tx, rx) = oneshot::channel(); + + let blocking_fut = async move { + let result = fut.await; + // Send the result back on the channel. + let _ = tx.send(result); + }; + + executor.spawn_blocking("substrate-rpc-subscription", Some("rpc"), blocking_fut.boxed()); + + rx +} From a38df0895a5082525f19ba8520c78323e18263ec Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 14:29:21 +0200 Subject: [PATCH 05/36] chainHead/tests: Check multiple connection calls Signed-off-by: Alexandru Vasile --- Cargo.lock | 81 ++++++++++++++ substrate/client/rpc-spec-v2/Cargo.toml | 1 + .../rpc-spec-v2/src/chain_head/tests.rs | 103 +++++++++++++++++- 3 files changed, 184 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 052e9ff2f5fe..42ba5233c148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5992,6 +5992,10 @@ name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] [[package]] name = "futures-util" @@ -6142,6 +6146,52 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "glutton-westend-runtime" version = "3.0.0" @@ -6828,11 +6878,13 @@ name = "jsonrpsee" version = "0.22.1" source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ + "jsonrpsee-client-transport", "jsonrpsee-core 0.22.1", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types 0.22.1", + "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", "tracing", @@ -6843,7 +6895,9 @@ name = "jsonrpsee-client-transport" version = "0.22.1" source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" dependencies = [ + "futures-channel", "futures-util", + "gloo-net", "http", "jsonrpsee-core 0.22.1", "pin-project", @@ -6856,6 +6910,7 @@ dependencies = [ "tokio-util", "tracing", "url", + "webpki-roots 0.26.1", ] [[package]] @@ -6897,6 +6952,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "wasm-bindgen-futures", ] [[package]] @@ -6978,6 +7034,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.22.1" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core 0.22.1", + "jsonrpsee-types 0.22.1", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.22.1" @@ -17266,6 +17332,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "separator" version = "0.4.1" @@ -21758,6 +21830,15 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "westend-emulated-chain" version = "0.0.0" diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 2e1defd7eb7f..4b720bb8ad21 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -44,6 +44,7 @@ futures-util = { version = "0.3.30", default-features = false } rand = "0.8.5" [dev-dependencies] +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "client", "server"] } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } 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 9544736d84c8..1bc20003ef34 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,14 @@ use assert_matches::assert_matches; use codec::{Decode, Encode}; use futures::Future; use jsonrpsee::{ - core::server::Subscription as RpcSubscription, rpc_params, MethodsError as Error, RpcModule, + core::{ + client::{ClientT, Subscription as RpcClientSubscription, SubscriptionClientT}, + server::Subscription as RpcSubscription, + }, + rpc_params, MethodsError as Error, RpcModule, }; +use serde_json::Value as JsonValue; + use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; use sc_service::client::new_in_mem; @@ -66,6 +72,34 @@ const CHILD_STORAGE_KEY: &[u8] = b"child"; const CHILD_VALUE: &[u8] = b"child value"; const DOES_NOT_PRODUCE_EVENTS_SECONDS: u64 = 10; +/// Start an RPC server with the chainHead module. +pub async fn run_server() -> std::net::SocketAddr { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let api = ChainHead::new( + client, + backend, + Arc::new(TaskExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + operation_max_storage_items: MAX_PAGINATION_LIMIT, + }, + ) + .into_rpc(); + + let server = jsonrpsee::server::ServerBuilder::default().build("127.0.0.1:0").await.unwrap(); + + let addr = server.local_addr().unwrap(); + let handle = server.start(api); + + tokio::spawn(handle.stopped()); + addr +} + async fn get_next_event(sub: &mut RpcSubscription) -> T { let (event, _sub_id) = tokio::time::timeout(std::time::Duration::from_secs(60), sub.next()) .await @@ -3289,3 +3323,70 @@ async fn storage_closest_merkle_value() { merkle_values_rhs.get(&hex_string(b":AAAA")).unwrap() ); } + +#[tokio::test] +async fn chain_head_single_connection_context() { + let server_addr = run_server().await; + let server_url = format!("ws://{}", server_addr); + let client = jsonrpsee::ws_client::WsClientBuilder::default() + .build(&server_url) + .await + .unwrap(); + + let sub: RpcClientSubscription = client + .subscribe("chainHead_unstable_follow", rpc_params![false], "chainHead_unstable_unfollow") + .await + .unwrap(); + + let first_sub_id = match sub.kind() { + jsonrpsee::core::client::SubscriptionKind::Subscription(id) => match id { + jsonrpsee::types::SubscriptionId::Num(num) => num.to_string(), + jsonrpsee::types::SubscriptionId::Str(s) => s.to_string(), + }, + _ => panic!("Unexpected subscription ID"), + }; + + // Calls cannot be made from a different connection context. + let second_client = jsonrpsee::ws_client::WsClientBuilder::default() + .build(&server_url) + .await + .unwrap(); + let invalid_hash = hex_string(&INVALID_HASH); + let response: MethodResponse = second_client + .request("chainHead_unstable_body", rpc_params![&first_sub_id, &invalid_hash]) + .await + .unwrap(); + assert_matches!(response, MethodResponse::LimitReached); + + let response: Option = second_client + .request("chainHead_unstable_header", rpc_params![&first_sub_id, &invalid_hash]) + .await + .unwrap(); + assert!(response.is_none()); + + let key = hex_string(&KEY); + let response: MethodResponse = second_client + .request( + "chainHead_unstable_storage", + rpc_params![ + &first_sub_id, + &invalid_hash, + vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }] + ], + ) + .await + .unwrap(); + assert_matches!(response, MethodResponse::LimitReached); + + let alice_id = AccountKeyring::Alice.to_account_id(); + // Hex encoded scale encoded bytes representing the call parameters. + let call_parameters = hex_string(&alice_id.encode()); + let response: MethodResponse = second_client + .request( + "chainHead_unstable_call", + [&first_sub_id, &invalid_hash, "AccountNonceApi_account_nonce", &call_parameters], + ) + .await + .unwrap(); + assert_matches!(response, MethodResponse::LimitReached); +} From ec9dd79102d4bb8f8147a320f3b126fdb6fa206d Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 15:18:36 +0200 Subject: [PATCH 06/36] chainHead/tests: Ensure single connection works Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/tests.rs | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) 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 1bc20003ef34..00fd1c107f25 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -33,7 +33,6 @@ use jsonrpsee::{ }, rpc_params, MethodsError as Error, RpcModule, }; -use serde_json::Value as JsonValue; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; @@ -3332,11 +3331,26 @@ async fn chain_head_single_connection_context() { .build(&server_url) .await .unwrap(); + // Calls cannot be made from a different connection context. + let second_client = jsonrpsee::ws_client::WsClientBuilder::default() + .build(&server_url) + .await + .unwrap(); + + let mut sub: RpcClientSubscription> = client + .subscribe("chainHead_unstable_follow", rpc_params![true], "chainHead_unstable_unfollow") + .await + .unwrap(); - let sub: RpcClientSubscription = client - .subscribe("chainHead_unstable_follow", rpc_params![false], "chainHead_unstable_unfollow") + let event = tokio::time::timeout(std::time::Duration::from_secs(60), sub.next()) .await + .unwrap() + .unwrap() .unwrap(); + let finalized_hash = match event { + FollowEvent::Initialized(init) => init.finalized_block_hash, + _ => panic!("Expected FollowEvent::Initialized"), + }; let first_sub_id = match sub.kind() { jsonrpsee::core::client::SubscriptionKind::Subscription(id) => match id { @@ -3346,31 +3360,50 @@ async fn chain_head_single_connection_context() { _ => panic!("Unexpected subscription ID"), }; - // Calls cannot be made from a different connection context. - let second_client = jsonrpsee::ws_client::WsClientBuilder::default() - .build(&server_url) + let response: MethodResponse = client + .request("chainHead_unstable_body", rpc_params![&first_sub_id, &finalized_hash]) .await .unwrap(); - let invalid_hash = hex_string(&INVALID_HASH); + assert_matches!(response, MethodResponse::Started(_started)); + // Cannot make a call from a different connection context. let response: MethodResponse = second_client - .request("chainHead_unstable_body", rpc_params![&first_sub_id, &invalid_hash]) + .request("chainHead_unstable_body", rpc_params![&first_sub_id, &finalized_hash]) .await .unwrap(); assert_matches!(response, MethodResponse::LimitReached); + let response: Option = client + .request("chainHead_unstable_header", rpc_params![&first_sub_id, &finalized_hash]) + .await + .unwrap(); + assert!(response.is_some()); + // Cannot make a call from a different connection context. let response: Option = second_client - .request("chainHead_unstable_header", rpc_params![&first_sub_id, &invalid_hash]) + .request("chainHead_unstable_header", rpc_params![&first_sub_id, &finalized_hash]) .await .unwrap(); assert!(response.is_none()); let key = hex_string(&KEY); + let response: MethodResponse = client + .request( + "chainHead_unstable_storage", + rpc_params![ + &first_sub_id, + &finalized_hash, + vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }] + ], + ) + .await + .unwrap(); + assert_matches!(response, MethodResponse::Started(_started)); + // Cannot make a call from a different connection context. let response: MethodResponse = second_client .request( "chainHead_unstable_storage", rpc_params![ &first_sub_id, - &invalid_hash, + &finalized_hash, vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }] ], ) @@ -3381,10 +3414,19 @@ async fn chain_head_single_connection_context() { let alice_id = AccountKeyring::Alice.to_account_id(); // Hex encoded scale encoded bytes representing the call parameters. let call_parameters = hex_string(&alice_id.encode()); + let response: MethodResponse = client + .request( + "chainHead_unstable_call", + [&first_sub_id, &finalized_hash, "AccountNonceApi_account_nonce", &call_parameters], + ) + .await + .unwrap(); + assert_matches!(response, MethodResponse::Started(_started)); + // Cannot make a call from a different connection context. let response: MethodResponse = second_client .request( "chainHead_unstable_call", - [&first_sub_id, &invalid_hash, "AccountNonceApi_account_nonce", &call_parameters], + [&first_sub_id, &finalized_hash, "AccountNonceApi_account_nonce", &call_parameters], ) .await .unwrap(); From e47477a3e8b6979cf22f588e0c0127cbfae408b3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 26 Feb 2024 15:21:20 +0200 Subject: [PATCH 07/36] chainHead/tests: Ensure unpin does not work from different connections Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/tests.rs | 7 +++++++ 1 file changed, 7 insertions(+) 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 00fd1c107f25..31a16238e2bd 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -3360,6 +3360,13 @@ async fn chain_head_single_connection_context() { _ => panic!("Unexpected subscription ID"), }; + // Cannot make a call from a different connection context. + let _response: () = second_client + .request("chainHead_unstable_unpin", [&first_sub_id, &finalized_hash]) + .await + .unwrap(); + + // Body can still be fetched from the first subscription. let response: MethodResponse = client .request("chainHead_unstable_body", rpc_params![&first_sub_id, &finalized_hash]) .await From 64ee5213efe795458383002301cf55b939399b75 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 5 Mar 2024 18:25:05 +0200 Subject: [PATCH 08/36] Cargo: Update jsonrpsee to 0.22.2 unreleased Signed-off-by: Alexandru Vasile --- Cargo.lock | 62 +++++++++---------- .../relay-chain-rpc-interface/Cargo.toml | 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 +- substrate/bin/node/rpc/Cargo.toml | 2 +- .../client/consensus/babe/rpc/Cargo.toml | 2 +- .../client/consensus/beefy/rpc/Cargo.toml | 2 +- .../client/consensus/grandpa/rpc/Cargo.toml | 2 +- .../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 | 2 +- substrate/client/rpc-spec-v2/Cargo.toml | 4 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- 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 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/rpc/support/Cargo.toml | 4 +- substrate/utils/frame/rpc/system/Cargo.toml | 2 +- 27 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42ba5233c148..e7617d0e00a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6875,15 +6875,15 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" [[package]] name = "jsonrpsee" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.1", + "jsonrpsee-core 0.22.2", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types 0.22.1", + "jsonrpsee-types 0.22.2", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", @@ -6892,14 +6892,14 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "futures-channel", "futures-util", "gloo-net", "http", - "jsonrpsee-core 0.22.1", + "jsonrpsee-core 0.22.2", "pin-project", "rustls-native-certs 0.7.0", "rustls-pki-types", @@ -6931,8 +6931,8 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "anyhow", "async-lock 3.3.0", @@ -6941,7 +6941,7 @@ dependencies = [ "futures-timer", "futures-util", "hyper", - "jsonrpsee-types 0.22.1", + "jsonrpsee-types 0.22.2", "parking_lot 0.12.1", "pin-project", "rand", @@ -6957,14 +6957,14 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core 0.22.1", - "jsonrpsee-types 0.22.1", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", "serde", "serde_json", "thiserror", @@ -6976,26 +6976,26 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "heck", "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.50", ] [[package]] name = "jsonrpsee-server" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core 0.22.1", - "jsonrpsee-types 0.22.1", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", "pin-project", "route-recognizer", "serde", @@ -7024,8 +7024,8 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "anyhow", "beef", @@ -7036,23 +7036,23 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.1", - "jsonrpsee-types 0.22.1", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.22.1" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/low-level-context-api-v2#15659276f1381748694fdb9c0cbaeee67ffec138" +version = "0.22.2" +source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.1", - "jsonrpsee-types 0.22.1", + "jsonrpsee-core 0.22.2", + "jsonrpsee-types 0.22.2", "url", ] diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index a8200d67030f..8026445802df 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["ws-client"] } tracing = "0.1.37" async-trait = "0.1.74" url = "2.4.0" diff --git a/cumulus/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index 8a672a376dce..9c0ea1982a1b 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.5.1", features = ["derive"] } log = { workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } futures = "0.3.28" serde_json = { workspace = true, default-features = true } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 8e146dd3c807..8eddebd1ad5f 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 f08f48447063..adbc7aaddc24 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.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } rand = "0.8.5" serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml index 613d641583e1..1941e50fdfe6 100644 --- a/polkadot/rpc/Cargo.toml +++ b/polkadot/rpc/Cargo.toml @@ -10,7 +10,7 @@ description = "Polkadot specific RPC functionality." workspace = true [dependencies] -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 09005a54f1b9..49a904f84a76 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.5.1", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } serde_json = { workspace = true, default-features = true } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index 13ced0fd08bb..daa6a1ae924a 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 9958fdac806b..fb318ea9d598 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.5.1", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } futures = "0.3.21" log = { workspace = true, default-features = true } rand = "0.8" diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index f306e98381e7..5f796f72663b 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index fe5f026933e0..d120e3918d30 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } futures = "0.3.21" serde = { features = ["derive"], workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 1a9607c1c477..f9a879bc0d9e 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index 59623cddfaaa..b7c99fcf9c5a 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", features = ["derive"] } serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index b07d3c77c859..86038a36b155 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 678a0084650c..8b2041d16482 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } 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 28697a45507a..e27ec26df2f3 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index df9af63ac291..43ce878e232c 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } log = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 4b720bb8ad21..693b63f0b070 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } # Internal chain structures for "chain_spec". sc-chain-spec = { path = "../chain-spec" } # Pool for submitting extrinsics required by "transaction" @@ -44,7 +44,7 @@ futures-util = { version = "0.3.30", default-features = false } rand = "0.8.5" [dev-dependencies] -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "client", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "client", "server"] } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index d09502f7c0e1..917190fb1958 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde_json = { workspace = true, default-features = true } diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 3722b9ad4cb8..30745bfce96f 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -28,7 +28,7 @@ runtime-benchmarks = [ ] [dependencies] -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } thiserror = { workspace = true } futures = "0.3.21" rand = "0.8.5" diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 06cafbeca487..4e348fe49afd 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index 81a46b6f53aa..631aa70beba6 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 5c5a2fe9291c..f4300f422b03 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["http-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.6.1" } log = { workspace = true, default-features = true } serde = { workspace = true, default-features = true } diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index 06932221af49..d75de186a5bd 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["ws-client"] } sc-rpc-api = { path = "../../../../client/rpc-api" } async-trait = "0.1.74" serde = { workspace = true, default-features = true } 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 518938351103..09e5e4490c77 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -24,7 +24,7 @@ sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } trie-db = "0.28.0" -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 a736b62e7da8..b58ca97451e1 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["jsonrpsee-types"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["jsonrpsee-types"] } serde = { workspace = true, default-features = true } 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["jsonrpsee-types", "ws-client"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index ac98b6a61a67..dd3416772c30 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/low-level-context-api-v2", features = ["client-core", "macros", "server"] } +jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } futures = "0.3.21" log = { workspace = true, default-features = true } frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" } From 683ecca752b0e6dd3d21f19305603d2d516f92bd Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 5 Mar 2024 18:29:53 +0200 Subject: [PATCH 09/36] chainHead: Use connection details instead of connection id Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) 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 ff3133e765d8..0bb190885766 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,7 @@ use crate::{ use codec::Encode; use futures::{channel::oneshot, future::FutureExt}; use jsonrpsee::{ - core::async_trait, server::ResponsePayload, types::SubscriptionId, ConnectionId, + core::async_trait, server::ResponsePayload, types::SubscriptionId, ConnectionDetails, MethodResponseFuture, PendingSubscriptionSink, SubscriptionSink, }; use log::debug; @@ -224,11 +224,14 @@ where async fn chain_head_unstable_body( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, hash: Block::Hash, ) -> ResponsePayload<'static, MethodResponse> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -304,11 +307,14 @@ where async fn chain_head_unstable_header( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, hash: Block::Hash, ) -> Result, ChainHeadRpcError> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return Ok(None); } @@ -337,13 +343,16 @@ where async fn chain_head_unstable_storage( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, hash: Block::Hash, items: Vec>, child_trie: Option, ) -> ResponsePayload<'static, MethodResponse> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -414,7 +423,7 @@ where async fn chain_head_unstable_call( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, hash: Block::Hash, function: String, @@ -425,7 +434,10 @@ where Err(err) => return ResponsePayload::error(err), }; - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -487,11 +499,14 @@ where async fn chain_head_unstable_unpin( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, hash_or_hashes: ListOrValue, ) -> Result<(), ChainHeadRpcError> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return Ok(()); } @@ -520,11 +535,14 @@ where async fn chain_head_unstable_continue( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, operation_id: String, ) -> Result<(), ChainHeadRpcError> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return Ok(()) } @@ -543,11 +561,14 @@ where async fn chain_head_unstable_stop_operation( &self, - connection_id: ConnectionId, + connection_details: ConnectionDetails, follow_subscription: String, operation_id: String, ) -> Result<(), ChainHeadRpcError> { - if !self.rpc_connections.contains_token(connection_id, &follow_subscription) { + if !self + .rpc_connections + .contains_token(connection_details.id(), &follow_subscription) + { return Ok(()) } From e6547d621d30e9ae44c97f3e4ba844a9a7b4c7a0 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 5 Mar 2024 18:43:26 +0200 Subject: [PATCH 10/36] cargo: Use jsonrpsee from crates.io Signed-off-by: Alexandru Vasile --- Cargo.lock | 82 +++++++------------ .../relay-chain-rpc-interface/Cargo.toml | 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 +- substrate/bin/node/rpc/Cargo.toml | 2 +- .../client/consensus/babe/rpc/Cargo.toml | 2 +- .../client/consensus/beefy/rpc/Cargo.toml | 2 +- .../client/consensus/grandpa/rpc/Cargo.toml | 2 +- .../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 | 2 +- substrate/client/rpc-spec-v2/Cargo.toml | 4 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- 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 +- .../rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/rpc/support/Cargo.toml | 4 +- substrate/utils/frame/rpc/system/Cargo.toml | 2 +- 27 files changed, 59 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7617d0e00a8..db67a6074452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4114,7 +4114,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core 0.22.0", + "jsonrpsee-core", "parity-scale-codec", "polkadot-overseer", "sc-client-api", @@ -6876,14 +6876,15 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" [[package]] name = "jsonrpsee" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3ae45a64cfc0882934f963be9431b2a165d667f53140358181f262aca0702" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.2", + "jsonrpsee-core", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types 0.22.2", + "jsonrpsee-types", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", @@ -6893,13 +6894,14 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455fc882e56f58228df2aee36b88a1340eafd707c76af2fa68cf94b37d461131" dependencies = [ "futures-channel", "futures-util", "gloo-net", "http", - "jsonrpsee-core 0.22.2", + "jsonrpsee-core", "pin-project", "rustls-native-certs 0.7.0", "rustls-pki-types", @@ -6913,26 +6915,11 @@ dependencies = [ "webpki-roots 0.26.1", ] -[[package]] -name = "jsonrpsee-core" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82030d038658974732103e623ba2e0abec03bbbe175b39c0a2fafbada60c5868" -dependencies = [ - "anyhow", - "async-trait", - "beef", - "jsonrpsee-types 0.22.0", - "serde", - "serde_json", - "thiserror", - "tracing", -] - [[package]] name = "jsonrpsee-core" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75568f4f9696e3a47426e1985b548e1a9fcb13372a5e320372acaf04aca30d1" dependencies = [ "anyhow", "async-lock 3.3.0", @@ -6941,7 +6928,7 @@ dependencies = [ "futures-timer", "futures-util", "hyper", - "jsonrpsee-types 0.22.2", + "jsonrpsee-types", "parking_lot 0.12.1", "pin-project", "rand", @@ -6958,13 +6945,14 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7a95e346f55df84fb167b7e06470e196e7d5b9488a21d69c5d9732043ba7ba" dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core 0.22.2", - "jsonrpsee-types 0.22.2", + "jsonrpsee-core", + "jsonrpsee-types", "serde", "serde_json", "thiserror", @@ -6977,7 +6965,8 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca066e73dd70294aebc5c2675d8ffae43be944af027c857ce0d4c51785f014" dependencies = [ "heck", "proc-macro-crate 3.0.0", @@ -6989,13 +6978,14 @@ dependencies = [ [[package]] name = "jsonrpsee-server" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e29c1bd1f9bba83c864977c73404e505f74f730fa0db89dd490ec174e36d7f0" dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core 0.22.2", - "jsonrpsee-types 0.22.2", + "jsonrpsee-core", + "jsonrpsee-types", "pin-project", "route-recognizer", "serde", @@ -7009,23 +6999,11 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-types" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a48fdc1202eafc51c63e00406575e59493284ace8b8b61aa16f3a6db5d64f1a" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "jsonrpsee-types" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467fd35feeee179f71ab294516bdf3a81139e7aeebdd860e46897c12e1a3368" dependencies = [ "anyhow", "beef", @@ -7037,22 +7015,24 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee61985930ef5b67bfcd0f21a881bae25e15fc982dfc943d73de39381d5e46bf" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.2", - "jsonrpsee-types 0.22.2", + "jsonrpsee-core", + "jsonrpsee-types", ] [[package]] name = "jsonrpsee-ws-client" version = "0.22.2" -source = "git+https://github.com/paritytech/jsonrpsee.git?branch=lexnv/release-0.22.2#ee5de5478a3386f4a83127bcfc3516057aff51a3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca71e74983f624c0cb67828e480a981586074da8ad3a2f214c6a3f884edab9" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core 0.22.2", - "jsonrpsee-types 0.22.2", + "jsonrpsee-core", + "jsonrpsee-types", "url", ] diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 8026445802df..801712b1ad15 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index 9c0ea1982a1b..0ef678c4cbae 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.5.1", features = ["derive"] } log = { workspace = true, default-features = true } codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.28" serde_json = { workspace = true, default-features = true } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 8eddebd1ad5f..84a232e954fc 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 adbc7aaddc24..b26f0b9967cf 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.5.1", features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.0.0" } criterion = { version = "0.5.1", features = ["async_tokio"] } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } rand = "0.8.5" serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } diff --git a/polkadot/rpc/Cargo.toml b/polkadot/rpc/Cargo.toml index 1941e50fdfe6..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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 49a904f84a76..65644861c9ac 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.5.1", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } serde_json = { workspace = true, default-features = true } sc-cli = { path = "../../../client/cli" } diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index daa6a1ae924a..9cba2b5a3660 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 fb318ea9d598..abe284c41da1 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.5.1", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1" } serde = { features = ["derive"], workspace = true, default-features = true } -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } futures = "0.3.21" log = { workspace = true, default-features = true } rand = "0.8" diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml index 5f796f72663b..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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index d120e3918d30..043b566673e7 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" serde = { features = ["derive"], workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index f9a879bc0d9e..bb2ae4a08966 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/grandpa/rpc/Cargo.toml b/substrate/client/consensus/grandpa/rpc/Cargo.toml index b7c99fcf9c5a..f7e87415448e 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } log = { workspace = true, default-features = true } parity-scale-codec = { version = "3.6.1", features = ["derive"] } serde = { features = ["derive"], workspace = true, default-features = true } diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml index 86038a36b155..ac32fed72289 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 8b2041d16482..9b391b76ea00 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } 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 e27ec26df2f3..1b7af6a4a52f 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 43ce878e232c..7f7a86799e0c 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } log = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 693b63f0b070..ad6e15a31cd1 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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" @@ -44,7 +44,7 @@ futures-util = { version = "0.3.30", default-features = false } rand = "0.8.5" [dev-dependencies] -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "client", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "client", "server"] } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index 917190fb1958..f65e6c9a59ec 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } log = { workspace = true, default-features = true } parking_lot = "0.12.1" serde_json = { workspace = true, default-features = true } diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 30745bfce96f..73edceb2ef36 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -28,7 +28,7 @@ runtime-benchmarks = [ ] [dependencies] -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["server"] } +jsonrpsee = { version = "0.22", features = ["server"] } thiserror = { workspace = true } futures = "0.3.21" rand = "0.8.5" diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index 4e348fe49afd..09dc611caa04 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } thiserror = { workspace = true } diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml index 631aa70beba6..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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 f4300f422b03..61e0a861ee0e 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["http-client"] } +jsonrpsee = { version = "0.22", features = ["http-client"] } codec = { package = "parity-scale-codec", version = "3.6.1" } log = { workspace = true, default-features = true } serde = { workspace = true, default-features = true } diff --git a/substrate/utils/frame/rpc/client/Cargo.toml b/substrate/utils/frame/rpc/client/Cargo.toml index d75de186a5bd..b51e3f44f4e6 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["ws-client"] } +jsonrpsee = { version = "0.22", features = ["ws-client"] } sc-rpc-api = { path = "../../../../client/rpc-api" } async-trait = "0.1.74" serde = { workspace = true, default-features = true } 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 09e5e4490c77..f9a45e21ce13 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -24,7 +24,7 @@ sp-state-machine = { path = "../../../../primitives/state-machine" } sp-trie = { path = "../../../../primitives/trie" } trie-db = "0.28.0" -jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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 b58ca97451e1..2e4bb6a10578 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["jsonrpsee-types"] } +jsonrpsee = { version = "0.22", features = ["jsonrpsee-types"] } serde = { workspace = true, default-features = true } 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", 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/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml index dd3416772c30..f9a84a01af82 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 = { git = "https://github.com/paritytech/jsonrpsee.git", branch = "lexnv/release-0.22.2", features = ["client-core", "macros", "server"] } +jsonrpsee = { version = "0.22", features = ["client-core", "macros", "server"] } futures = "0.3.21" log = { workspace = true, default-features = true } frame-system-rpc-runtime-api = { path = "../../../../frame/system/rpc/runtime-api" } From 1a503d2d680aa3e33a808eed87aa8b4891df46d2 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 15 Mar 2024 15:57:06 +0200 Subject: [PATCH 11/36] chainHead/tests: Fix merge conflict with master Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 60d5ace50d0d..ad5cb8bee3ca 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -3356,7 +3356,7 @@ async fn chain_head_single_connection_context() { .unwrap() .unwrap(); let finalized_hash = match event { - FollowEvent::Initialized(init) => init.finalized_block_hash, + FollowEvent::Initialized(init) => init.finalized_block_hashes.into_iter().last().unwrap(), _ => panic!("Expected FollowEvent::Initialized"), }; From ad9838363155a5ad01f8425f93faf363f32ab28f Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 12:30:01 +0200 Subject: [PATCH 12/36] rpc-v2/common: Limit the number of subscriptions for a connection ID Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/common/connections.rs | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 81086438b66d..2a8898361f07 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -26,19 +26,30 @@ use std::{ /// Limit the RPC functionality to a single connection. #[derive(Default, Clone)] pub struct RpcConnections { + /// The number of tokens that can be registered for each connection. + capacity: usize, + /// Map the connecton ID to a set of tokens. data: Arc>>>, } impl RpcConnections { /// Constructs a new instance of [`RpcConnections`]. - pub fn new() -> Self { - RpcConnections { data: Default::default() } + pub fn new(capacity: usize) -> Self { + RpcConnections { capacity, data: Default::default() } } /// Register a token for the given connection. - pub fn register_token(&self, connection_id: ConnectionId, token: String) { + /// + /// Returns true if the token can be registered, false otherwise. + pub fn register_token(&self, connection_id: ConnectionId, token: String) -> bool { let mut data = self.data.lock(); - data.entry(connection_id).or_insert_with(HashSet::new).insert(token); + + let mut entry = data.entry(connection_id).or_insert_with(HashSet::new); + if entry.len() >= self.capacity { + return false; + } + + entry.insert(token) } /// Unregister a token for the given connection. @@ -58,3 +69,28 @@ impl RpcConnections { data.get(&connection_id).map(|tokens| tokens.contains(token)).unwrap_or(false) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn register_token() { + let rpc_connections = RpcConnections::new(2); + assert!(rpc_connections.register_token(1, "token1".to_string())); + assert!(rpc_connections.register_token(1, "token2".to_string())); + // Cannot be registered due to exceeding limits. + assert!(!rpc_connections.register_token(1, "token3".to_string())); + } + + #[test] + fn unregister_token() { + let rpc_connections = RpcConnections::new(2); + rpc_connections.register_token(1, "token1".to_string()); + rpc_connections.register_token(1, "token2".to_string()); + + rpc_connections.unregister_token(1, "token1"); + assert!(!rpc_connections.contains_token(1, "token1")); + assert!(rpc_connections.contains_token(1, "token2")); + } +} From 07a18a63de47ada17ddf8a94f6c5464e971fe655 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 12:32:27 +0200 Subject: [PATCH 13/36] chainHead/config: Add max follow subscriptions per connection Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs | 6 ++++++ 1 file changed, 6 insertions(+) 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 0bb190885766..52cae7a6e193 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 @@ -65,6 +65,8 @@ pub struct ChainHeadConfig { /// The maximum number of items reported by the `chainHead_storage` before /// pagination is required. pub operation_max_storage_items: usize, + /// The maximum number of `chainHead_follow` subscriptions per connection. + pub max_follow_subscriptions_per_connection: usize, } /// Maximum pinned blocks across all connections. @@ -86,6 +88,9 @@ const MAX_ONGOING_OPERATIONS: usize = 16; /// before paginations is required. const MAX_STORAGE_ITER_ITEMS: usize = 5; +/// The maximum number of `chainHead_follow` subscriptions per connection. +const MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION: usize = 4; + impl Default for ChainHeadConfig { fn default() -> Self { ChainHeadConfig { @@ -93,6 +98,7 @@ impl Default for ChainHeadConfig { subscription_max_pinned_duration: MAX_PINNED_DURATION, subscription_max_ongoing_operations: MAX_ONGOING_OPERATIONS, operation_max_storage_items: MAX_STORAGE_ITER_ITEMS, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, } } } From d3fc06a20550445e839ab7a1f43b2d7a368ca6e8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 14:31:53 +0200 Subject: [PATCH 14/36] chainHead: Limit number of chainHead_follow subscriptions Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 19 ++++-- .../rpc-spec-v2/src/chain_head/error.rs | 7 ++ .../rpc-spec-v2/src/common/connections.rs | 67 ++++++++++++++++--- 3 files changed, 78 insertions(+), 15 deletions(-) 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 52cae7a6e193..82e2eab90012 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 @@ -141,7 +141,7 @@ impl, Block: BlockT, Client> ChainHead { backend, )), operation_max_storage_items: config.operation_max_storage_items, - rpc_connections: RpcConnections::new(), + rpc_connections: RpcConnections::new(config.max_follow_subscriptions_per_connection), _phantom: PhantomData, } } @@ -192,10 +192,20 @@ where let rpc_connections = self.rpc_connections.clone(); let fut = async move { - let Ok(sink) = pending.accept().await else { return }; + // Ensure the current connection ID has enough space to accept a new subscription. + let connection_id = pending.connection_id(); + if !rpc_connections.reserve_token(connection_id) { + pending.reject(ChainHeadRpcError::ReachedLimits).await; + return + } - let sub_id = read_subscription_id_as_string(&sink); + let Ok(sink) = pending.accept().await else { + rpc_connections.unreserve_token(connection_id); + return + }; + let connection_id = sink.connection_id(); + let sub_id = read_subscription_id_as_string(&sink); // Keep track of the subscription. let Some(sub_data) = subscriptions.insert_subscription(sub_id.clone(), with_runtime) else { @@ -204,10 +214,11 @@ where debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); let msg = to_sub_message(&sink, &FollowEvent::::Stop); let _ = sink.send(msg).await; + rpc_connections.unreserve_token(connection_id); return }; debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); - let connection_id = sink.connection_id(); + rpc_connections.register_token(connection_id, sub_id.clone()); let mut chain_head_follow = ChainHeadFollower::new( diff --git a/substrate/client/rpc-spec-v2/src/chain_head/error.rs b/substrate/client/rpc-spec-v2/src/chain_head/error.rs index 8c50e445aa0c..35604db06600 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/error.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/error.rs @@ -23,6 +23,9 @@ use jsonrpsee::types::error::ErrorObject; /// ChainHead RPC errors. #[derive(Debug, thiserror::Error)] pub enum Error { + /// Maximum number of chainHead_follow has been reached. + #[error("Maximum number of chainHead_follow has been reached")] + ReachedLimits, /// The provided block hash is invalid. #[error("Invalid block hash")] InvalidBlock, @@ -46,6 +49,8 @@ pub enum Error { /// Errors for `chainHead` RPC module, as defined in /// . pub mod rpc_spec_v2 { + /// Maximum number of chainHead_follow has been reached. + pub const REACHED_LIMITS: i32 = -32800; /// The provided block hash is invalid. pub const INVALID_BLOCK_ERROR: i32 = -32801; /// The follow subscription was started with `withRuntime` set to `false`. @@ -70,6 +75,8 @@ impl From for ErrorObject<'static> { let msg = e.to_string(); match e { + Error::ReachedLimits => + ErrorObject::owned(rpc_spec_v2::REACHED_LIMITS, msg, None::<()>), Error::InvalidBlock => ErrorObject::owned(rpc_spec_v2::INVALID_BLOCK_ERROR, msg, None::<()>), Error::InvalidRuntimeCall(_) => diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 2a8898361f07..c6f4e96dc4b9 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -29,7 +29,22 @@ pub struct RpcConnections { /// The number of tokens that can be registered for each connection. capacity: usize, /// Map the connecton ID to a set of tokens. - data: Arc>>>, + data: Arc>>, +} + +#[derive(Default)] +struct ConnectionData { + /// The total number of tokens. + /// + /// # Note + /// + /// Because a pending subscription sink does not expose the future subscription ID, + /// we cannot register a token before the pending subscription is accepted. + /// This variable ensures that we have enough capacity to register a token, after + /// the subscription is accepted. Otherwise, a jsonrpc error object should be returned. + num_tokens: usize, + /// Active registered tokens. + tokens: HashSet, } impl RpcConnections { @@ -38,26 +53,54 @@ impl RpcConnections { RpcConnections { capacity, data: Default::default() } } - /// Register a token for the given connection. + /// Reserve space for a token. /// - /// Returns true if the token can be registered, false otherwise. - pub fn register_token(&self, connection_id: ConnectionId, token: String) -> bool { + /// This ensures that [`Self::register_token`] has enough capacity. + /// Returns false if the token cannot be reserved. + pub fn reserve_token(&self, connection_id: ConnectionId) -> bool { let mut data = self.data.lock(); - let mut entry = data.entry(connection_id).or_insert_with(HashSet::new); - if entry.len() >= self.capacity { + let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); + if entry.num_tokens >= self.capacity { return false; } + entry.num_tokens += 1; - entry.insert(token) + true + } + + /// Gives back the reserved space before the token is registered. + /// + /// # Note + /// + /// This may happen if the pending subscription cannot be accepted (unlikely). + pub fn unreserve_token(&self, connection_id: ConnectionId) { + let mut data = self.data.lock(); + + let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); + if entry.num_tokens > 0 { + entry.num_tokens -= 1; + } + } + + /// Register a token for the given connection. + /// + /// Users should call [`Self::reserve_token`] before calling this method. + pub fn register_token(&self, connection_id: ConnectionId, token: String) { + let mut data = self.data.lock(); + + let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); + entry.tokens.insert(token); } /// Unregister a token for the given connection. pub fn unregister_token(&self, connection_id: ConnectionId, token: &str) { let mut data = self.data.lock(); - if let Some(tokens) = data.get_mut(&connection_id) { - tokens.remove(token); - if tokens.is_empty() { + if let Some(connection_data) = data.get_mut(&connection_id) { + connection_data.tokens.remove(token); + connection_data.num_tokens -= 1; + + if connection_data.num_tokens == 0 { data.remove(&connection_id); } } @@ -66,7 +109,9 @@ impl RpcConnections { /// Check if the given connection contains the given token. pub fn contains_token(&self, connection_id: ConnectionId, token: &str) -> bool { let data = self.data.lock(); - data.get(&connection_id).map(|tokens| tokens.contains(token)).unwrap_or(false) + data.get(&connection_id) + .map(|connection_data| connection_data.tokens.contains(token)) + .unwrap_or(false) } } From 493f9339e01f2250fe847ed1497031b7a64a858e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 14:53:07 +0200 Subject: [PATCH 15/36] chainHead: Add RAII wrappers for managing registering tokens Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 15 ++--- .../rpc-spec-v2/src/common/connections.rs | 56 ++++++++++++++++--- 2 files changed, 52 insertions(+), 19 deletions(-) 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 82e2eab90012..de2786245a37 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 @@ -194,17 +194,14 @@ where let fut = async move { // Ensure the current connection ID has enough space to accept a new subscription. let connection_id = pending.connection_id(); - if !rpc_connections.reserve_token(connection_id) { - pending.reject(ChainHeadRpcError::ReachedLimits).await; - return - } - let Ok(sink) = pending.accept().await else { - rpc_connections.unreserve_token(connection_id); + let Some(reserved_token) = rpc_connections.reserve_token(connection_id) else { + pending.reject(ChainHeadRpcError::ReachedLimits).await; return }; - let connection_id = sink.connection_id(); + let Ok(sink) = pending.accept().await else { return }; + let sub_id = read_subscription_id_as_string(&sink); // Keep track of the subscription. let Some(sub_data) = subscriptions.insert_subscription(sub_id.clone(), with_runtime) @@ -214,12 +211,11 @@ where debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription already accepted", sub_id); let msg = to_sub_message(&sink, &FollowEvent::::Stop); let _ = sink.send(msg).await; - rpc_connections.unreserve_token(connection_id); return }; debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); - rpc_connections.register_token(connection_id, sub_id.clone()); + let _registered_token = reserved_token.register(sub_id.clone()); let mut chain_head_follow = ChainHeadFollower::new( client, @@ -232,7 +228,6 @@ where chain_head_follow.generate_events(sink, sub_data).await; subscriptions.remove_subscription(&sub_id); - rpc_connections.unregister_token(connection_id, &sub_id); debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription removed", sub_id); }; diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index c6f4e96dc4b9..128761bb22d4 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -54,19 +54,16 @@ impl RpcConnections { } /// Reserve space for a token. - /// - /// This ensures that [`Self::register_token`] has enough capacity. - /// Returns false if the token cannot be reserved. - pub fn reserve_token(&self, connection_id: ConnectionId) -> bool { + pub fn reserve_token(&self, connection_id: ConnectionId) -> Option { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); if entry.num_tokens >= self.capacity { - return false; + return None; } entry.num_tokens += 1; - true + Some(ReservedConnectionToken { connection_id, rpc_connections: Some(self.clone()) }) } /// Gives back the reserved space before the token is registered. @@ -74,7 +71,7 @@ impl RpcConnections { /// # Note /// /// This may happen if the pending subscription cannot be accepted (unlikely). - pub fn unreserve_token(&self, connection_id: ConnectionId) { + fn unreserve_token(&self, connection_id: ConnectionId) { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); @@ -86,7 +83,7 @@ impl RpcConnections { /// Register a token for the given connection. /// /// Users should call [`Self::reserve_token`] before calling this method. - pub fn register_token(&self, connection_id: ConnectionId, token: String) { + fn register_token(&self, connection_id: ConnectionId, token: String) { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); @@ -94,7 +91,7 @@ impl RpcConnections { } /// Unregister a token for the given connection. - pub fn unregister_token(&self, connection_id: ConnectionId, token: &str) { + fn unregister_token(&self, connection_id: ConnectionId, token: &str) { let mut data = self.data.lock(); if let Some(connection_data) = data.get_mut(&connection_id) { connection_data.tokens.remove(token); @@ -115,6 +112,47 @@ impl RpcConnections { } } +/// RAII wrapper that ensures the reserved space is given back if the object is +/// dropped before the token is registered. +pub struct ReservedConnectionToken { + connection_id: ConnectionId, + rpc_connections: Option, +} + +impl ReservedConnectionToken { + /// Register the token for the given connection. + pub fn register(mut self, token: String) -> RegisteredConnectionToken { + let rpc_connections = self + .rpc_connections + .take() + .expect("Always constructed with rpc connections; qed"); + + rpc_connections.register_token(self.connection_id, token.clone()); + RegisteredConnectionToken { connection_id: self.connection_id, token, rpc_connections } + } +} + +impl Drop for ReservedConnectionToken { + fn drop(&mut self) { + if let Some(rpc_connections) = self.rpc_connections.take() { + rpc_connections.unreserve_token(self.connection_id); + } + } +} + +/// RAII wrapper that ensures the token is unregistered if the object is dropped. +pub struct RegisteredConnectionToken { + connection_id: ConnectionId, + token: String, + rpc_connections: RpcConnections, +} + +impl Drop for RegisteredConnectionToken { + fn drop(&mut self) { + self.rpc_connections.unregister_token(self.connection_id, &self.token); + } +} + #[cfg(test)] mod tests { use super::*; From 4b34a3baeeb8c7187381272b29396b54a7230374 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 14:57:20 +0200 Subject: [PATCH 16/36] chainHead/tests: Adjust testing Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/tests.rs | 21 +++++ .../rpc-spec-v2/src/common/connections.rs | 79 +++++++++++++++---- 2 files changed, 85 insertions(+), 15 deletions(-) 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 ad5cb8bee3ca..bb438c858faf 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -64,6 +64,8 @@ const MAX_PINNED_BLOCKS: usize = 32; const MAX_PINNED_SECS: u64 = 60; const MAX_OPERATIONS: usize = 16; const MAX_PAGINATION_LIMIT: usize = 5; +const MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION: usize = 4; + const INVALID_HASH: [u8; 32] = [1; 32]; const KEY: &[u8] = b":mock"; const VALUE: &[u8] = b"hello world"; @@ -86,6 +88,7 @@ pub async fn run_server() -> std::net::SocketAddr { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -146,6 +149,7 @@ async fn setup_api() -> ( subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -196,6 +200,7 @@ async fn follow_subscription_produces_blocks() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -264,6 +269,7 @@ async fn follow_with_runtime() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -576,6 +582,7 @@ async fn call_runtime_without_flag() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1234,6 +1241,7 @@ async fn separate_operation_ids_for_subscriptions() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1322,6 +1330,7 @@ async fn follow_generates_initial_blocks() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1477,6 +1486,7 @@ async fn follow_exceeding_pinned_blocks() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1553,6 +1563,7 @@ async fn follow_with_unpin() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1664,6 +1675,7 @@ async fn unpin_duplicate_hashes() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1766,6 +1778,7 @@ async fn follow_with_multiple_unpin_hashes() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -1919,6 +1932,7 @@ async fn follow_prune_best_block() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2104,6 +2118,7 @@ async fn follow_forks_pruned_block() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2263,6 +2278,7 @@ async fn follow_report_multiple_pruned_block() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2508,6 +2524,7 @@ async fn pin_block_references() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2645,6 +2662,7 @@ async fn follow_finalized_before_new_block() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2759,6 +2777,7 @@ async fn ensure_operation_limits_works() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: 1, operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -2863,6 +2882,7 @@ async fn check_continue_operation() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: 1, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); @@ -3045,6 +3065,7 @@ async fn stop_storage_operation() { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: 1, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, ) .into_rpc(); diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 128761bb22d4..23f334207d6c 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -61,7 +61,7 @@ impl RpcConnections { if entry.num_tokens >= self.capacity { return None; } - entry.num_tokens += 1; + entry.num_tokens = entry.num_tokens.saturating_add(1); Some(ReservedConnectionToken { connection_id, rpc_connections: Some(self.clone()) }) } @@ -75,9 +75,7 @@ impl RpcConnections { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); - if entry.num_tokens > 0 { - entry.num_tokens -= 1; - } + entry.num_tokens = entry.num_tokens.saturating_sub(1); } /// Register a token for the given connection. @@ -87,6 +85,11 @@ impl RpcConnections { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); + // Should be already checked `Self::reserve_token`. + if entry.tokens.len() >= self.capacity { + return; + } + entry.tokens.insert(token); } @@ -95,7 +98,7 @@ impl RpcConnections { let mut data = self.data.lock(); if let Some(connection_data) = data.get_mut(&connection_id) { connection_data.tokens.remove(token); - connection_data.num_tokens -= 1; + connection_data.num_tokens = connection_data.num_tokens.saturating_sub(1); if connection_data.num_tokens == 0 { data.remove(&connection_id); @@ -158,22 +161,68 @@ mod tests { use super::*; #[test] - fn register_token() { + fn reserve_token() { let rpc_connections = RpcConnections::new(2); - assert!(rpc_connections.register_token(1, "token1".to_string())); - assert!(rpc_connections.register_token(1, "token2".to_string())); - // Cannot be registered due to exceeding limits. - assert!(!rpc_connections.register_token(1, "token3".to_string())); + let reserved = rpc_connections.reserve_token(1); + assert!(reserved.is_some()); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + let reserved = reserved.unwrap(); + let registered = reserved.register("token1".to_string()); + assert!(rpc_connections.contains_token(1, "token1")); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + drop(registered); + + // Data is dropped. + assert!(rpc_connections.data.lock().get(&1).is_none()); + // Checks can still happen. + assert!(!rpc_connections.contains_token(1, "token1")); } #[test] - fn unregister_token() { + fn reserve_token_capacity_reached() { let rpc_connections = RpcConnections::new(2); - rpc_connections.register_token(1, "token1".to_string()); - rpc_connections.register_token(1, "token2".to_string()); - rpc_connections.unregister_token(1, "token1"); - assert!(!rpc_connections.contains_token(1, "token1")); + // Reserve token for connection 1. + let reserved = rpc_connections.reserve_token(1); + assert!(reserved.is_some()); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + // Add token for connection 1. + let reserved = reserved.unwrap(); + let registered = reserved.register("token1".to_string()); + assert!(rpc_connections.contains_token(1, "token1")); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + // Reserve token for connection 1 again. + let reserved = rpc_connections.reserve_token(1); + assert!(reserved.is_some()); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + // Add token for connection 1 again. + let reserved = reserved.unwrap(); + let registered_second = reserved.register("token2".to_string()); assert!(rpc_connections.contains_token(1, "token2")); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + // Cannot reserve more tokens. + let reserved = rpc_connections.reserve_token(1); + assert!(reserved.is_none()); + + // Drop the first token. + drop(registered); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert!(rpc_connections.contains_token(1, "token2")); + assert!(!rpc_connections.contains_token(1, "token1")); + + // Can reserve again after clearing the space. + let reserved = rpc_connections.reserve_token(1); + assert!(reserved.is_some()); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + + // Ensure data is cleared. + drop(reserved); + drop(registered_second); + assert!(rpc_connections.data.lock().get(&1).is_none()); } } From e726a51ab266bf9efa8028e82318330c19816855 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 15:57:48 +0200 Subject: [PATCH 17/36] chainHead/follow: Early detection when chainHead client is disconnected Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head_follow.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index afa99f3aa164..63f189e3779d 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -546,7 +546,12 @@ where EventStream: Stream> + Unpin, { let mut stream_item = stream.next(); - let mut stop_event = rx_stop; + + // The stop event can be triggered by the chainHead logic when the pinned + // block guarantee cannot be hold. Or when the client is disconnected. + let connection_closed = sink.closed(); + tokio::pin!(connection_closed); + let mut stop_event = futures_util::future::select(rx_stop, connection_closed); while let Either::Left((Some(event), next_stop_event)) = futures_util::future::select(stream_item, stop_event).await @@ -594,8 +599,10 @@ where stop_event = next_stop_event; } - // If we got here either the substrate streams have closed - // or the `Stop` receiver was triggered. + // If we got here either: + // - the substrate streams have closed + // - the `Stop` receiver was triggered internally. + // - the client disconnected. let msg = to_sub_message(&sink, &FollowEvent::::Stop); let _ = sink.send(msg).await; } From 539291d315935ceda48f54a3208677ea12cbf035 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 15:59:24 +0200 Subject: [PATCH 18/36] chainHead/tests: Check connection limits Signed-off-by: Alexandru Vasile --- .../src/chain_head/chain_head_follow.rs | 2 +- .../rpc-spec-v2/src/chain_head/tests.rs | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 63f189e3779d..83663c9c9780 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -601,7 +601,7 @@ where // If we got here either: // - the substrate streams have closed - // - the `Stop` receiver was triggered internally. + // - the `Stop` receiver was triggered internally (cannot hold the pinned block guarantee) // - the client disconnected. let msg = to_sub_message(&sink, &FollowEvent::::Stop); let _ = sink.send(msg).await; 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 bb438c858faf..f613cc90a42c 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -88,7 +88,7 @@ pub async fn run_server() -> std::net::SocketAddr { subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + max_follow_subscriptions_per_connection: 1, }, ) .into_rpc(); @@ -3468,3 +3468,43 @@ async fn chain_head_single_connection_context() { .unwrap(); assert_matches!(response, MethodResponse::LimitReached); } + +#[tokio::test] +async fn chain_head_limit_reached() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + // Maximum of 1 chainHead_follow subscription. + let api = ChainHead::new( + client.clone(), + backend, + Arc::new(TaskExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + operation_max_storage_items: MAX_PAGINATION_LIMIT, + max_follow_subscriptions_per_connection: 1, + }, + ) + .into_rpc(); + + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); + // Initialized must always be reported first. + let _event: FollowEvent = get_next_event(&mut sub).await; + + let error = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap_err(); + assert!(error + .to_string() + .contains("Maximum number of chainHead_follow has been reached")); + + // After dropping the subscription, other subscriptions are allowed to be created. + drop(sub); + // Ensure the `chainHead_unfollow` is propagated to the server. + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap(); + // Initialized must always be reported first. + let _event: FollowEvent = get_next_event(&mut sub).await; +} From d5ce140a3f044165a35b408d856e4811e446de89 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:05:58 +0200 Subject: [PATCH 19/36] Update substrate/client/rpc-spec-v2/Cargo.toml Co-authored-by: Niklas Adolfsson --- substrate/client/rpc-spec-v2/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index ad6e15a31cd1..808c7e083d4d 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -44,7 +44,7 @@ futures-util = { version = "0.3.30", default-features = false } rand = "0.8.5" [dev-dependencies] -jsonrpsee = { version = "0.22", features = ["client-core", "macros", "client", "server"] } +jsonrpsee = { version = "0.22", features = ["ws-client", "server"] } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } From 378d9b9487e689d3d28f3879791dcf3f06a735f0 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 16:09:00 +0200 Subject: [PATCH 20/36] Update cargo lock Signed-off-by: Alexandru Vasile --- Cargo.lock | 82 ------------------------------------------------------ 1 file changed, 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21ca2edf2a47..461980ae8a9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6019,10 +6019,6 @@ name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" -dependencies = [ - "gloo-timers", - "send_wrapper", -] [[package]] name = "futures-util" @@ -6173,52 +6169,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "glutton-westend-runtime" version = "3.0.0" @@ -6902,13 +6852,11 @@ version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3ae45a64cfc0882934f963be9431b2a165d667f53140358181f262aca0702" dependencies = [ - "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", - "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", "tracing", @@ -6920,9 +6868,7 @@ version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455fc882e56f58228df2aee36b88a1340eafd707c76af2fa68cf94b37d461131" dependencies = [ - "futures-channel", "futures-util", - "gloo-net", "http", "jsonrpsee-core", "pin-project", @@ -6935,7 +6881,6 @@ dependencies = [ "tokio-util", "tracing", "url", - "webpki-roots 0.26.1", ] [[package]] @@ -6962,7 +6907,6 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "wasm-bindgen-futures", ] [[package]] @@ -7035,17 +6979,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "jsonrpsee-wasm-client" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee61985930ef5b67bfcd0f21a881bae25e15fc982dfc943d73de39381d5e46bf" -dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", -] - [[package]] name = "jsonrpsee-ws-client" version = "0.22.2" @@ -17395,12 +17328,6 @@ dependencies = [ "pest", ] -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - [[package]] name = "separator" version = "0.4.1" @@ -21974,15 +21901,6 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" -[[package]] -name = "webpki-roots" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "westend-emulated-chain" version = "0.0.0" From 36e986f151236084bb92ee92ec21cc5814ef1e16 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 16:09:08 +0200 Subject: [PATCH 21/36] chainHead: Clarify connection context for methods Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 de2786245a37..1d69ff0afa16 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 @@ -116,7 +116,10 @@ pub struct ChainHead, Block: BlockT, Client> { /// The maximum number of items reported by the `chainHead_storage` before /// pagination is required. operation_max_storage_items: usize, - /// Limit the RPC functionality to a single connection. + /// Ensures that chainHead methods can be called from a single connection context. + /// + /// For example, `chainHead_storage` cannot be called with a subscription ID that + /// was obtained from a different connection. rpc_connections: RpcConnections, /// Phantom member to pin the block type. _phantom: PhantomData, From 9405b8a44d87747f0caece207b3c6ef6248e229e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 16:10:02 +0200 Subject: [PATCH 22/36] chainHead: Adjust comment wrt jsonrpc future Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) 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 1d69ff0afa16..d19b0c37f161 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 @@ -301,9 +301,8 @@ where let (rp, rp_fut) = method_started_response(operation_id, None); let fut = async move { - // This leaves on a seperate task, because the `rp` needs to be acknowledged by the - // server. Events should only by generated if the response was successfully - // propagated. + // Wait for the server to send out the response and if it produces an error no event + // should be generated. if rp_fut.await.is_err() { return; } @@ -421,9 +420,8 @@ where let (rp, rp_fut) = method_started_response(operation_id, Some(discarded)); let fut = async move { - // This leaves on a seperate task, because the `rp` needs to be acknowledged by the - // server. Events should only by generated if the response was successfully - // propagated. + // Wait for the server to send out the response and if it produces an error no event + // should be generated. if rp_fut.await.is_err() { return; } @@ -498,9 +496,8 @@ where }) }); - // This leaves on a seperate task, because the `rp` needs to be acknowledged by the - // server. Events should only by generated if the response was successfully - // propagated. + // Wait for the server to send out the response and if it produces an error no event + // should be generated. if rp_fut.await.is_err() { return } From e506c6a548767e9aaba7dbe27c0058043cd8f7f8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 16:10:25 +0200 Subject: [PATCH 23/36] chainHead: Generate runtime call event after the future is propagated Signed-off-by: Alexandru Vasile --- .../client/rpc-spec-v2/src/chain_head/chain_head.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 d19b0c37f161..b928a7aa2d92 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 @@ -480,6 +480,12 @@ where let (rp, rp_fut) = method_started_response(operation_id.clone(), None); let fut = async move { + // Wait for the server to send out the response and if it produces an error no event + // should be generated. + if rp_fut.await.is_err() { + return + } + let event = client .executor() .call(hash, &function, &call_parameters, CallContext::Offchain) @@ -496,11 +502,6 @@ where }) }); - // Wait for the server to send out the response and if it produces an error no event - // should be generated. - if rp_fut.await.is_err() { - return - } let _ = block_guard.response_sender().unbounded_send(event); }; self.executor From fe460dbd4446660200cd2c1809fef1fa88140278 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 16:32:08 +0200 Subject: [PATCH 24/36] chainHead/tests: Adjust testing to use jsonrpsee macro interface Signed-off-by: Alexandru Vasile --- .../client/rpc-spec-v2/src/chain_head/api.rs | 2 +- .../rpc-spec-v2/src/chain_head/tests.rs | 141 ++++++++++-------- 2 files changed, 79 insertions(+), 64 deletions(-) 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 cd30fb78d8d8..3851adac2644 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/api.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/api.rs @@ -27,7 +27,7 @@ use crate::{ common::events::StorageQuery, }; use jsonrpsee::{proc_macros::rpc, server::ResponsePayload}; -use sp_rpc::list::ListOrValue; +pub use sp_rpc::list::ListOrValue; #[rpc(client, server)] pub trait ChainHeadApi { 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 f613cc90a42c..9027142a312c 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use crate::{ - chain_head::{event::MethodResponse, test_utils::ChainHeadMockClient}, + chain_head::{api::ChainHeadApiClient, event::MethodResponse, test_utils::ChainHeadMockClient}, common::events::{StorageQuery, StorageQueryType, StorageResultType}, hex_string, }; @@ -28,8 +28,7 @@ use codec::{Decode, Encode}; use futures::Future; use jsonrpsee::{ core::{ - client::{ClientT, Subscription as RpcClientSubscription, SubscriptionClientT}, - server::Subscription as RpcSubscription, + client::Subscription as RpcClientSubscription, server::Subscription as RpcSubscription, }, rpc_params, MethodsError as Error, RpcModule, }; @@ -3366,10 +3365,10 @@ async fn chain_head_single_connection_context() { .await .unwrap(); - let mut sub: RpcClientSubscription> = client - .subscribe("chainHead_unstable_follow", rpc_params![true], "chainHead_unstable_unfollow") - .await - .unwrap(); + let mut sub: RpcClientSubscription> = + ChainHeadApiClient::::chain_head_unstable_follow(&client, true) + .await + .unwrap(); let event = tokio::time::timeout(std::time::Duration::from_secs(60), sub.next()) .await @@ -3390,82 +3389,98 @@ async fn chain_head_single_connection_context() { }; // Cannot make a call from a different connection context. - let _response: () = second_client - .request("chainHead_unstable_unpin", [&first_sub_id, &finalized_hash]) - .await - .unwrap(); + let _response = ChainHeadApiClient::::chain_head_unstable_unpin( + &second_client, + first_sub_id.clone(), + crate::chain_head::api::ListOrValue::Value(finalized_hash.clone()), + ) + .await + .unwrap(); // Body can still be fetched from the first subscription. - let response: MethodResponse = client - .request("chainHead_unstable_body", rpc_params![&first_sub_id, &finalized_hash]) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_body( + &client, + first_sub_id.clone(), + finalized_hash.clone(), + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::Started(_started)); + // Cannot make a call from a different connection context. - let response: MethodResponse = second_client - .request("chainHead_unstable_body", rpc_params![&first_sub_id, &finalized_hash]) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_body( + &second_client, + first_sub_id.clone(), + finalized_hash.clone(), + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::LimitReached); - let response: Option = client - .request("chainHead_unstable_header", rpc_params![&first_sub_id, &finalized_hash]) - .await - .unwrap(); + let response: Option = ChainHeadApiClient::::chain_head_unstable_header( + &client, + first_sub_id.clone(), + finalized_hash.clone(), + ) + .await + .unwrap(); assert!(response.is_some()); // Cannot make a call from a different connection context. - let response: Option = second_client - .request("chainHead_unstable_header", rpc_params![&first_sub_id, &finalized_hash]) - .await - .unwrap(); + let response: Option = ChainHeadApiClient::::chain_head_unstable_header( + &second_client, + first_sub_id.clone(), + finalized_hash.clone(), + ) + .await + .unwrap(); assert!(response.is_none()); let key = hex_string(&KEY); - let response: MethodResponse = client - .request( - "chainHead_unstable_storage", - rpc_params![ - &first_sub_id, - &finalized_hash, - vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }] - ], - ) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_storage( + &client, + first_sub_id.clone(), + finalized_hash.clone(), + vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }], + None, + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::Started(_started)); // Cannot make a call from a different connection context. - let response: MethodResponse = second_client - .request( - "chainHead_unstable_storage", - rpc_params![ - &first_sub_id, - &finalized_hash, - vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }] - ], - ) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_storage( + &second_client, + first_sub_id.clone(), + finalized_hash.clone(), + vec![StorageQuery { key: key.clone(), query_type: StorageQueryType::Hash }], + None, + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::LimitReached); let alice_id = AccountKeyring::Alice.to_account_id(); // Hex encoded scale encoded bytes representing the call parameters. let call_parameters = hex_string(&alice_id.encode()); - let response: MethodResponse = client - .request( - "chainHead_unstable_call", - [&first_sub_id, &finalized_hash, "AccountNonceApi_account_nonce", &call_parameters], - ) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_call( + &client, + first_sub_id.clone(), + finalized_hash.clone(), + "AccountNonceApi_account_nonce".into(), + call_parameters.clone(), + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::Started(_started)); // Cannot make a call from a different connection context. - let response: MethodResponse = second_client - .request( - "chainHead_unstable_call", - [&first_sub_id, &finalized_hash, "AccountNonceApi_account_nonce", &call_parameters], - ) - .await - .unwrap(); + let response: MethodResponse = ChainHeadApiClient::::chain_head_unstable_call( + &second_client, + first_sub_id.clone(), + finalized_hash.clone(), + "AccountNonceApi_account_nonce".into(), + call_parameters.clone(), + ) + .await + .unwrap(); assert_matches!(response, MethodResponse::LimitReached); } From 512d8164c35260686f4eccdeb6e9d19ecf90a2ec Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 19 Mar 2024 17:33:43 +0200 Subject: [PATCH 25/36] Update cargo.lock Signed-off-by: Alexandru Vasile --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 10f6b847be02..d1c6e1471b16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6945,7 +6945,7 @@ dependencies = [ "proc-macro-crate 3.0.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] From 109d2b754d84f3d8148f22df84fa38b05b2ec641 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 20 Mar 2024 17:55:16 +0200 Subject: [PATCH 26/36] rpc-v2/connections: Check empty connection cleans the connection ID Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/common/connections.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 23f334207d6c..58b68cf2d60c 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -166,6 +166,7 @@ mod tests { let reserved = rpc_connections.reserve_token(1); assert!(reserved.is_some()); assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert_eq!(rpc_connections.data.lock().len(), 1); let reserved = reserved.unwrap(); let registered = reserved.register("token1".to_string()); @@ -175,6 +176,7 @@ mod tests { // Data is dropped. assert!(rpc_connections.data.lock().get(&1).is_none()); + assert!(rpc_connections.data.lock().is_empty()); // Checks can still happen. assert!(!rpc_connections.contains_token(1, "token1")); } From 71cbfeb63d9028f53ed78f72edfaad5ff0aed7df Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 20 Mar 2024 18:03:04 +0200 Subject: [PATCH 27/36] chainHead: Encapsulate rpc connections with subscription management Signed-off-by: Alexandru Vasile --- .../rpc-spec-v2/src/chain_head/chain_head.rs | 59 +++++---- .../src/chain_head/chain_head_follow.rs | 4 +- .../src/chain_head/subscription/mod.rs | 112 ++++++++++++++++-- 3 files changed, 128 insertions(+), 47 deletions(-) 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 b928a7aa2d92..b2334ba01f9d 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 @@ -30,7 +30,7 @@ use crate::{ event::{FollowEvent, MethodResponse, OperationError}, subscription::{SubscriptionManagement, SubscriptionManagementError}, }, - common::{connections::RpcConnections, events::StorageQuery}, + common::events::StorageQuery, hex_string, SubscriptionTaskExecutor, }; use codec::Encode; @@ -112,15 +112,10 @@ pub struct ChainHead, Block: BlockT, Client> { /// Executor to spawn subscriptions. executor: SubscriptionTaskExecutor, /// Keep track of the pinned blocks for each subscription. - subscriptions: Arc>, + subscriptions: SubscriptionManagement, /// The maximum number of items reported by the `chainHead_storage` before /// pagination is required. operation_max_storage_items: usize, - /// Ensures that chainHead methods can be called from a single connection context. - /// - /// For example, `chainHead_storage` cannot be called with a subscription ID that - /// was obtained from a different connection. - rpc_connections: RpcConnections, /// Phantom member to pin the block type. _phantom: PhantomData, } @@ -137,14 +132,14 @@ impl, Block: BlockT, Client> ChainHead { client, backend: backend.clone(), executor, - subscriptions: Arc::new(SubscriptionManagement::new( + subscriptions: SubscriptionManagement::new( config.global_max_pinned_blocks, config.subscription_max_pinned_duration, config.subscription_max_ongoing_operations, + config.max_follow_subscriptions_per_connection, backend, - )), + ), operation_max_storage_items: config.operation_max_storage_items, - rpc_connections: RpcConnections::new(config.max_follow_subscriptions_per_connection), _phantom: PhantomData, } } @@ -192,13 +187,15 @@ where let subscriptions = self.subscriptions.clone(); let backend = self.backend.clone(); let client = self.client.clone(); - let rpc_connections = self.rpc_connections.clone(); let fut = async move { // Ensure the current connection ID has enough space to accept a new subscription. let connection_id = pending.connection_id(); - - let Some(reserved_token) = rpc_connections.reserve_token(connection_id) else { + // The RAII `reserved_subscription` will clean up resources on drop: + // - free the reserved subscription for the connection ID. + // - remove the subscription ID from the subscription management. + let Some(mut reserved_subscription) = subscriptions.reserve_subscription(connection_id) + else { pending.reject(ChainHeadRpcError::ReachedLimits).await; return }; @@ -207,7 +204,8 @@ where let sub_id = read_subscription_id_as_string(&sink); // Keep track of the subscription. - let Some(sub_data) = subscriptions.insert_subscription(sub_id.clone(), with_runtime) + let Some(sub_data) = + reserved_subscription.insert_subscription(sub_id.clone(), with_runtime) else { // Inserting the subscription can only fail if the JsonRPSee // generated a duplicate subscription ID. @@ -218,19 +216,16 @@ where }; debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription accepted", sub_id); - let _registered_token = reserved_token.register(sub_id.clone()); - let mut chain_head_follow = ChainHeadFollower::new( client, backend, - subscriptions.clone(), + subscriptions, with_runtime, sub_id.clone(), ); chain_head_follow.generate_events(sink, sub_data).await; - subscriptions.remove_subscription(&sub_id); debug!(target: LOG_TARGET, "[follow][id={:?}] Subscription removed", sub_id); }; @@ -244,8 +239,8 @@ where hash: Block::Hash, ) -> ResponsePayload<'static, MethodResponse> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -326,8 +321,8 @@ where hash: Block::Hash, ) -> Result, ChainHeadRpcError> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return Ok(None); } @@ -364,8 +359,8 @@ where child_trie: Option, ) -> ResponsePayload<'static, MethodResponse> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -448,8 +443,8 @@ where }; if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return ResponsePayload::success(MethodResponse::LimitReached); } @@ -517,8 +512,8 @@ where hash_or_hashes: ListOrValue, ) -> Result<(), ChainHeadRpcError> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return Ok(()); } @@ -553,8 +548,8 @@ where operation_id: String, ) -> Result<(), ChainHeadRpcError> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return Ok(()) } @@ -579,8 +574,8 @@ where operation_id: String, ) -> Result<(), ChainHeadRpcError> { if !self - .rpc_connections - .contains_token(connection_details.id(), &follow_subscription) + .subscriptions + .contains_subscription(connection_details.id(), &follow_subscription) { return Ok(()) } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index 83663c9c9780..90cc62a36fa9 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -60,7 +60,7 @@ pub struct ChainHeadFollower, Block: BlockT, Client> { /// Backend of the chain. backend: Arc, /// Subscriptions handle. - sub_handle: Arc>, + sub_handle: SubscriptionManagement, /// Subscription was started with the runtime updates flag. with_runtime: bool, /// Subscription ID. @@ -74,7 +74,7 @@ impl, Block: BlockT, Client> ChainHeadFollower, backend: Arc, - sub_handle: Arc>, + sub_handle: SubscriptionManagement, with_runtime: bool, sub_id: String, ) -> Self { diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index c830e662da2e..ced61eb80752 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use jsonrpsee::ConnectionId; use parking_lot::RwLock; use sc_client_api::Backend; use sp_runtime::traits::Block as BlockT; @@ -24,6 +25,10 @@ use std::{sync::Arc, time::Duration}; mod error; mod inner; +use crate::common::connections::{ + RegisteredConnectionToken, ReservedConnectionToken, RpcConnections, +}; + use self::inner::SubscriptionsInner; pub use self::inner::OperationState; @@ -34,7 +39,22 @@ pub use inner::{BlockGuard, InsertedSubscriptionData}; pub struct SubscriptionManagement> { /// Manage subscription by mapping the subscription ID /// to a set of block hashes. - inner: RwLock>, + inner: Arc>>, + + /// Ensures that chainHead methods can be called from a single connection context. + /// + /// For example, `chainHead_storage` cannot be called with a subscription ID that + /// was obtained from a different connection. + rpc_connections: RpcConnections, +} + +impl> Clone for SubscriptionManagement { + fn clone(&self) -> Self { + SubscriptionManagement { + inner: self.inner.clone(), + rpc_connections: self.rpc_connections.clone(), + } + } } impl> SubscriptionManagement { @@ -43,30 +63,42 @@ impl> SubscriptionManagement { global_max_pinned_blocks: usize, local_max_pin_duration: Duration, max_ongoing_operations: usize, + max_follow_subscriptions_per_connection: usize, backend: Arc, ) -> Self { SubscriptionManagement { - inner: RwLock::new(SubscriptionsInner::new( + inner: Arc::new(RwLock::new(SubscriptionsInner::new( global_max_pinned_blocks, local_max_pin_duration, max_ongoing_operations, backend, - )), + ))), + rpc_connections: RpcConnections::new(max_follow_subscriptions_per_connection), } } - /// Insert a new subscription ID. + /// Reserve space for a subscriptions. /// - /// If the subscription was not previously inserted, returns the receiver that is - /// triggered upon the "Stop" event. Otherwise, if the subscription ID was already - /// inserted returns none. - pub fn insert_subscription( + /// Fails if the connection ID is has reached the maximum number of active subscriptions. + pub fn reserve_subscription( &self, - sub_id: String, - runtime_updates: bool, - ) -> Option> { - let mut inner = self.inner.write(); - inner.insert_subscription(sub_id, runtime_updates) + connection_id: ConnectionId, + ) -> Option> { + let reserved_token = self.rpc_connections.reserve_token(connection_id)?; + + Some(ReservedSubscription { + state: ConnectionState::Reserved(reserved_token), + inner: self.inner.clone(), + }) + } + + /// Check if the given connection contains the given subscription. + pub fn contains_subscription( + &self, + connection_id: ConnectionId, + subscription_id: &str, + ) -> bool { + self.rpc_connections.contains_token(connection_id, subscription_id) } /// Remove the subscription ID with associated pinned blocks. @@ -136,3 +168,57 @@ impl> SubscriptionManagement { inner.get_operation(sub_id, operation_id) } } + +/// The state of the connection. +/// +/// The state starts in a [`ConnectionState::Reserved`] state and then transitions to +/// [`ConnectionState::Registered`] when the subscription is inserted. +enum ConnectionState { + Reserved(ReservedConnectionToken), + Registered { _unregister_on_drop: RegisteredConnectionToken, sub_id: String }, + Empty, +} + +/// RAII wrapper that removes the subscription from internal mappings and +/// gives back the reserved space for the connection. +pub struct ReservedSubscription> { + state: ConnectionState, + inner: Arc>>, +} + +impl> ReservedSubscription { + /// Insert a new subscription ID. + /// + /// If the subscription was not previously inserted, returns the receiver that is + /// triggered upon the "Stop" event. Otherwise, if the subscription ID was already + /// inserted returns none. + pub fn insert_subscription( + &mut self, + sub_id: String, + runtime_updates: bool, + ) -> Option> { + match std::mem::replace(&mut self.state, ConnectionState::Empty) { + ConnectionState::Reserved(reserved) => { + let registered_token = reserved.register(sub_id.clone()); + self.state = ConnectionState::Registered { + _unregister_on_drop: registered_token, + sub_id: sub_id.clone(), + }; + + let mut inner = self.inner.write(); + inner.insert_subscription(sub_id, runtime_updates) + }, + // Cannot insert multiple subscriptions into one single reserved space. + ConnectionState::Registered { .. } => None, + ConnectionState::Empty => None, + } + } +} + +impl> Drop for ReservedSubscription { + fn drop(&mut self) { + if let ConnectionState::Registered { sub_id, .. } = &self.state { + self.inner.write().remove_subscription(sub_id); + } + } +} From 0e015ab2e2cbb82e92d84397e5ca764059466cdc Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 20 Mar 2024 18:21:53 +0200 Subject: [PATCH 28/36] subscription/tests: Check resource cleanup on drop Signed-off-by: Alexandru Vasile --- .../src/chain_head/subscription/inner.rs | 53 +++++++++++++++++++ .../src/chain_head/subscription/mod.rs | 13 +++++ 2 files changed, 66 insertions(+) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs index d2879679501f..1ebee3c80fc8 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs @@ -1455,4 +1455,57 @@ mod tests { let permit_three = ops.reserve_at_most(1).unwrap(); assert_eq!(permit_three.num_ops, 1); } + + #[test] + fn reserved_subscription_cleans_resources() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let subs = Arc::new(parking_lot::RwLock::new(SubscriptionsInner::new( + 10, + Duration::from_secs(10), + MAX_OPERATIONS_PER_SUB, + backend, + ))); + + // Maximum 2 subscriptions per connection. + let rpc_connections = crate::common::connections::RpcConnections::new(2); + + let subscription_management = + crate::chain_head::subscription::SubscriptionManagement::_from_inner( + subs.clone(), + rpc_connections.clone(), + ); + + let reserved_sub_first = subscription_management.reserve_subscription(1).unwrap(); + let mut reserved_sub_second = subscription_management.reserve_subscription(1).unwrap(); + // Subscriptions reserved but not yet populated. + assert_eq!(subs.read().subs.len(), 0); + + // Cannot reserve anymore. + assert!(subscription_management.reserve_subscription(1).is_none()); + // Drop the first subscription. + drop(reserved_sub_first); + // Space is freed-up for the rpc connections. + let mut reserved_sub_first = subscription_management.reserve_subscription(1).unwrap(); + + // Insert subscriptions. + let _sub_data_first = + reserved_sub_first.insert_subscription("sub1".to_string(), true).unwrap(); + let _sub_data_second = + reserved_sub_second.insert_subscription("sub2".to_string(), true).unwrap(); + // Check we have 2 subscriptions under management. + assert_eq!(subs.read().subs.len(), 2); + + // Drop first reserved subscription. + drop(reserved_sub_first); + // Check that the subscription is removed. + assert_eq!(subs.read().subs.len(), 1); + // Space is freed-up for the rpc connections. + let reserved_sub_first = subscription_management.reserve_subscription(1).unwrap(); + + // Drop all subscriptions. + drop(reserved_sub_first); + drop(reserved_sub_second); + assert_eq!(subs.read().subs.len(), 0); + } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index ced61eb80752..80b7048dcf40 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -77,6 +77,19 @@ impl> SubscriptionManagement { } } + /// Create a new instance from the inner state. + /// + /// # Note + /// + /// Used for testing. + #[doc(hidden)] + pub(crate) fn _from_inner( + inner: Arc>>, + rpc_connections: RpcConnections, + ) -> Self { + SubscriptionManagement { inner, rpc_connections } + } + /// Reserve space for a subscriptions. /// /// Fails if the connection ID is has reached the maximum number of active subscriptions. From 65e26e48804c9cbdc3e0871e54fa042b5bfb63c6 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 20 Mar 2024 18:23:25 +0200 Subject: [PATCH 29/36] Update substrate/client/rpc-spec-v2/src/common/connections.rs Co-authored-by: Niklas Adolfsson --- substrate/client/rpc-spec-v2/src/common/connections.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 58b68cf2d60c..38ccb2ad3e79 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -23,7 +23,8 @@ use std::{ sync::Arc, }; -/// Limit the RPC functionality to a single connection. +/// Connection state which keeps track whether a connection exist and +/// the number of concurrent operations. #[derive(Default, Clone)] pub struct RpcConnections { /// The number of tokens that can be registered for each connection. From aae5a11053ed1d019b2ba9f1881c9b262ff707ce Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 20 Mar 2024 18:29:27 +0200 Subject: [PATCH 30/36] chainHead/connections: Detect duplicate tokens Signed-off-by: Alexandru Vasile --- .../src/chain_head/subscription/mod.rs | 17 ++++++--- .../rpc-spec-v2/src/common/connections.rs | 35 +++++++++++-------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index 80b7048dcf40..91a280a7ef44 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -25,8 +25,9 @@ use std::{sync::Arc, time::Duration}; mod error; mod inner; -use crate::common::connections::{ - RegisteredConnectionToken, ReservedConnectionToken, RpcConnections, +use crate::{ + chain_head::chain_head::LOG_TARGET, + common::connections::{RegisteredConnectionToken, ReservedConnectionToken, RpcConnections}, }; use self::inner::SubscriptionsInner; @@ -205,6 +206,10 @@ impl> ReservedSubscription { /// If the subscription was not previously inserted, returns the receiver that is /// triggered upon the "Stop" event. Otherwise, if the subscription ID was already /// inserted returns none. + /// + /// # Note + /// + /// This method should be called only once. pub fn insert_subscription( &mut self, sub_id: String, @@ -212,7 +217,7 @@ impl> ReservedSubscription { ) -> Option> { match std::mem::replace(&mut self.state, ConnectionState::Empty) { ConnectionState::Reserved(reserved) => { - let registered_token = reserved.register(sub_id.clone()); + let registered_token = reserved.register(sub_id.clone())?; self.state = ConnectionState::Registered { _unregister_on_drop: registered_token, sub_id: sub_id.clone(), @@ -222,8 +227,10 @@ impl> ReservedSubscription { inner.insert_subscription(sub_id, runtime_updates) }, // Cannot insert multiple subscriptions into one single reserved space. - ConnectionState::Registered { .. } => None, - ConnectionState::Empty => None, + ConnectionState::Registered { .. } | ConnectionState::Empty => { + log::error!(target: LOG_TARGET, "Called insert_subscription on a connection that is not reserved"); + None + }, } } } diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 38ccb2ad3e79..0e4338e766c9 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -82,16 +82,19 @@ impl RpcConnections { /// Register a token for the given connection. /// /// Users should call [`Self::reserve_token`] before calling this method. - fn register_token(&self, connection_id: ConnectionId, token: String) { + /// + /// Returns true if the token was inserted successfully, false if the token was already + /// inserted or reached capacity. + fn register_token(&self, connection_id: ConnectionId, token: String) -> bool { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); // Should be already checked `Self::reserve_token`. if entry.tokens.len() >= self.capacity { - return; + return false; } - entry.tokens.insert(token); + entry.tokens.insert(token) } /// Unregister a token for the given connection. @@ -125,14 +128,18 @@ pub struct ReservedConnectionToken { impl ReservedConnectionToken { /// Register the token for the given connection. - pub fn register(mut self, token: String) -> RegisteredConnectionToken { - let rpc_connections = self - .rpc_connections - .take() - .expect("Always constructed with rpc connections; qed"); - - rpc_connections.register_token(self.connection_id, token.clone()); - RegisteredConnectionToken { connection_id: self.connection_id, token, rpc_connections } + pub fn register(mut self, token: String) -> Option { + let rpc_connections = self.rpc_connections.take()?; + + if rpc_connections.register_token(self.connection_id, token.clone()) { + Some(RegisteredConnectionToken { + connection_id: self.connection_id, + token, + rpc_connections, + }) + } else { + None + } } } @@ -170,7 +177,7 @@ mod tests { assert_eq!(rpc_connections.data.lock().len(), 1); let reserved = reserved.unwrap(); - let registered = reserved.register("token1".to_string()); + let registered = reserved.register("token1".to_string()).unwrap(); assert!(rpc_connections.contains_token(1, "token1")); assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); drop(registered); @@ -193,7 +200,7 @@ mod tests { // Add token for connection 1. let reserved = reserved.unwrap(); - let registered = reserved.register("token1".to_string()); + let registered = reserved.register("token1".to_string()).unwrap(); assert!(rpc_connections.contains_token(1, "token1")); assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); @@ -204,7 +211,7 @@ mod tests { // Add token for connection 1 again. let reserved = reserved.unwrap(); - let registered_second = reserved.register("token2".to_string()); + let registered_second = reserved.register("token2".to_string()).unwrap(); assert!(rpc_connections.contains_token(1, "token2")); assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); From f06138b98b929a2d72cb35f8c2e483418524bfc5 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 20 Mar 2024 18:36:01 +0200 Subject: [PATCH 31/36] chainHead/connections: Change connection names Signed-off-by: Alexandru Vasile --- .../src/chain_head/subscription/mod.rs | 10 +- .../rpc-spec-v2/src/common/connections.rs | 174 ++++++++++-------- 2 files changed, 102 insertions(+), 82 deletions(-) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index 91a280a7ef44..d23d1398c90c 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -27,7 +27,7 @@ mod inner; use crate::{ chain_head::chain_head::LOG_TARGET, - common::connections::{RegisteredConnectionToken, ReservedConnectionToken, RpcConnections}, + common::connections::{RegisteredConnection, ReservedConnection, RpcConnections}, }; use self::inner::SubscriptionsInner; @@ -98,7 +98,7 @@ impl> SubscriptionManagement { &self, connection_id: ConnectionId, ) -> Option> { - let reserved_token = self.rpc_connections.reserve_token(connection_id)?; + let reserved_token = self.rpc_connections.reserve_space(connection_id)?; Some(ReservedSubscription { state: ConnectionState::Reserved(reserved_token), @@ -112,7 +112,7 @@ impl> SubscriptionManagement { connection_id: ConnectionId, subscription_id: &str, ) -> bool { - self.rpc_connections.contains_token(connection_id, subscription_id) + self.rpc_connections.contains_identifier(connection_id, subscription_id) } /// Remove the subscription ID with associated pinned blocks. @@ -188,8 +188,8 @@ impl> SubscriptionManagement { /// The state starts in a [`ConnectionState::Reserved`] state and then transitions to /// [`ConnectionState::Registered`] when the subscription is inserted. enum ConnectionState { - Reserved(ReservedConnectionToken), - Registered { _unregister_on_drop: RegisteredConnectionToken, sub_id: String }, + Reserved(ReservedConnection), + Registered { _unregister_on_drop: RegisteredConnection, sub_id: String }, Empty, } diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 0e4338e766c9..87eac4df9e3e 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -27,25 +27,41 @@ use std::{ /// the number of concurrent operations. #[derive(Default, Clone)] pub struct RpcConnections { - /// The number of tokens that can be registered for each connection. + /// The number of identifiers that can be registered for each connection. + /// + /// # Example + /// + /// This is used to limit how many `chainHead_follow` subscriptions are active at one time. capacity: usize, - /// Map the connecton ID to a set of tokens. + /// Map the connecton ID to a set of identifiers. data: Arc>>, } #[derive(Default)] struct ConnectionData { - /// The total number of tokens. + /// The total number of identifiers for the given connection. + /// + /// An identifier for a connection might be: + /// - the subscription ID for chainHead_follow + /// - the operation ID for the transactionBroadcast API + /// - or simply how many times the transaction API has been called. /// /// # Note /// /// Because a pending subscription sink does not expose the future subscription ID, - /// we cannot register a token before the pending subscription is accepted. - /// This variable ensures that we have enough capacity to register a token, after + /// we cannot register a subscription ID before the pending subscription is accepted. + /// This variable ensures that we have enough capacity to register an identifier, after /// the subscription is accepted. Otherwise, a jsonrpc error object should be returned. - num_tokens: usize, - /// Active registered tokens. - tokens: HashSet, + num_identifiers: usize, + /// Active registered identifiers for the given connection. + /// + /// # Note + /// + /// For chainHead, this represents the subscription ID. + /// For transactionBroadcast, this represents the operation ID. + /// For transaction, this is empty and the number of active calls is tracked by + /// [`Self::num_identifiers`]. + identifiers: HashSet, } impl RpcConnections { @@ -54,87 +70,91 @@ impl RpcConnections { RpcConnections { capacity, data: Default::default() } } - /// Reserve space for a token. - pub fn reserve_token(&self, connection_id: ConnectionId) -> Option { + /// Reserve space for a new connection identifier. + /// + /// If the number of active identifiers for the given connection exceeds the capacity, + /// returns None. + pub fn reserve_space(&self, connection_id: ConnectionId) -> Option { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); - if entry.num_tokens >= self.capacity { + if entry.num_identifiers >= self.capacity { return None; } - entry.num_tokens = entry.num_tokens.saturating_add(1); + entry.num_identifiers = entry.num_identifiers.saturating_add(1); - Some(ReservedConnectionToken { connection_id, rpc_connections: Some(self.clone()) }) + Some(ReservedConnection { connection_id, rpc_connections: Some(self.clone()) }) } - /// Gives back the reserved space before the token is registered. + /// Gives back the reserved space before the connection identifier is registered. /// /// # Note /// /// This may happen if the pending subscription cannot be accepted (unlikely). - fn unreserve_token(&self, connection_id: ConnectionId) { + fn unreserve_space(&self, connection_id: ConnectionId) { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); - entry.num_tokens = entry.num_tokens.saturating_sub(1); + entry.num_identifiers = entry.num_identifiers.saturating_sub(1); } - /// Register a token for the given connection. + /// Register an identifier for the given connection. /// - /// Users should call [`Self::reserve_token`] before calling this method. + /// Users must call [`Self::reserve_space`] before calling this method to ensure enough + /// space is available. /// - /// Returns true if the token was inserted successfully, false if the token was already - /// inserted or reached capacity. - fn register_token(&self, connection_id: ConnectionId, token: String) -> bool { + /// Returns true if the identifier was inserted successfully, false if the identifier was + /// already inserted or reached capacity. + fn register_identifier(&self, connection_id: ConnectionId, identifier: String) -> bool { let mut data = self.data.lock(); let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); - // Should be already checked `Self::reserve_token`. - if entry.tokens.len() >= self.capacity { + // Should be already checked `Self::reserve_space`. + if entry.identifiers.len() >= self.capacity { return false; } - entry.tokens.insert(token) + entry.identifiers.insert(identifier) } - /// Unregister a token for the given connection. - fn unregister_token(&self, connection_id: ConnectionId, token: &str) { + /// Unregister an identifier for the given connection. + fn unregister_identifier(&self, connection_id: ConnectionId, identifier: &str) { let mut data = self.data.lock(); if let Some(connection_data) = data.get_mut(&connection_id) { - connection_data.tokens.remove(token); - connection_data.num_tokens = connection_data.num_tokens.saturating_sub(1); + connection_data.identifiers.remove(identifier); + connection_data.num_identifiers = connection_data.num_identifiers.saturating_sub(1); - if connection_data.num_tokens == 0 { + if connection_data.num_identifiers == 0 { data.remove(&connection_id); } } } - /// Check if the given connection contains the given token. - pub fn contains_token(&self, connection_id: ConnectionId, token: &str) -> bool { + /// Check if the given connection contains the given identifier. + pub fn contains_identifier(&self, connection_id: ConnectionId, identifier: &str) -> bool { let data = self.data.lock(); data.get(&connection_id) - .map(|connection_data| connection_data.tokens.contains(token)) + .map(|connection_data| connection_data.identifiers.contains(identifier)) .unwrap_or(false) } } /// RAII wrapper that ensures the reserved space is given back if the object is -/// dropped before the token is registered. -pub struct ReservedConnectionToken { +/// dropped before the identifier is registered. +pub struct ReservedConnection { connection_id: ConnectionId, rpc_connections: Option, } -impl ReservedConnectionToken { - /// Register the token for the given connection. - pub fn register(mut self, token: String) -> Option { +impl ReservedConnection { + /// Register the identifier for the given connection. + pub fn register(mut self, identifier: String) -> Option { let rpc_connections = self.rpc_connections.take()?; - if rpc_connections.register_token(self.connection_id, token.clone()) { - Some(RegisteredConnectionToken { + if rpc_connections.register_identifier(self.connection_id, identifier.clone()) { + Some(RegisteredConnection { connection_id: self.connection_id, - token, + identifier, rpc_connections, }) } else { @@ -143,24 +163,24 @@ impl ReservedConnectionToken { } } -impl Drop for ReservedConnectionToken { +impl Drop for ReservedConnection { fn drop(&mut self) { if let Some(rpc_connections) = self.rpc_connections.take() { - rpc_connections.unreserve_token(self.connection_id); + rpc_connections.unreserve_space(self.connection_id); } } } -/// RAII wrapper that ensures the token is unregistered if the object is dropped. -pub struct RegisteredConnectionToken { +/// RAII wrapper that ensures the identifier is unregistered if the object is dropped. +pub struct RegisteredConnection { connection_id: ConnectionId, - token: String, + identifier: String, rpc_connections: RpcConnections, } -impl Drop for RegisteredConnectionToken { +impl Drop for RegisteredConnection { fn drop(&mut self) { - self.rpc_connections.unregister_token(self.connection_id, &self.token); + self.rpc_connections.unregister_identifier(self.connection_id, &self.identifier); } } @@ -169,66 +189,66 @@ mod tests { use super::*; #[test] - fn reserve_token() { + fn reserve_space() { let rpc_connections = RpcConnections::new(2); - let reserved = rpc_connections.reserve_token(1); + let reserved = rpc_connections.reserve_space(1); assert!(reserved.is_some()); - assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); assert_eq!(rpc_connections.data.lock().len(), 1); let reserved = reserved.unwrap(); - let registered = reserved.register("token1".to_string()).unwrap(); - assert!(rpc_connections.contains_token(1, "token1")); - assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + let registered = reserved.register("identifier1".to_string()).unwrap(); + assert!(rpc_connections.contains_identifier(1, "identifier1")); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); drop(registered); // Data is dropped. assert!(rpc_connections.data.lock().get(&1).is_none()); assert!(rpc_connections.data.lock().is_empty()); // Checks can still happen. - assert!(!rpc_connections.contains_token(1, "token1")); + assert!(!rpc_connections.contains_identifier(1, "identifier1")); } #[test] - fn reserve_token_capacity_reached() { + fn reserve_space_capacity_reached() { let rpc_connections = RpcConnections::new(2); - // Reserve token for connection 1. - let reserved = rpc_connections.reserve_token(1); + // Reserve identifier for connection 1. + let reserved = rpc_connections.reserve_space(1); assert!(reserved.is_some()); - assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); - // Add token for connection 1. + // Add identifier for connection 1. let reserved = reserved.unwrap(); - let registered = reserved.register("token1".to_string()).unwrap(); - assert!(rpc_connections.contains_token(1, "token1")); - assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + let registered = reserved.register("identifier1".to_string()).unwrap(); + assert!(rpc_connections.contains_identifier(1, "identifier1")); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); - // Reserve token for connection 1 again. - let reserved = rpc_connections.reserve_token(1); + // Reserve identifier for connection 1 again. + let reserved = rpc_connections.reserve_space(1); assert!(reserved.is_some()); - assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); - // Add token for connection 1 again. + // Add identifier for connection 1 again. let reserved = reserved.unwrap(); - let registered_second = reserved.register("token2".to_string()).unwrap(); - assert!(rpc_connections.contains_token(1, "token2")); - assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + let registered_second = reserved.register("identifier2".to_string()).unwrap(); + assert!(rpc_connections.contains_identifier(1, "identifier2")); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); - // Cannot reserve more tokens. - let reserved = rpc_connections.reserve_token(1); + // Cannot reserve more identifiers. + let reserved = rpc_connections.reserve_space(1); assert!(reserved.is_none()); - // Drop the first token. + // Drop the first identifier. drop(registered); - assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_tokens); - assert!(rpc_connections.contains_token(1, "token2")); - assert!(!rpc_connections.contains_token(1, "token1")); + assert_eq!(1, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); + assert!(rpc_connections.contains_identifier(1, "identifier2")); + assert!(!rpc_connections.contains_identifier(1, "identifier1")); // Can reserve again after clearing the space. - let reserved = rpc_connections.reserve_token(1); + let reserved = rpc_connections.reserve_space(1); assert!(reserved.is_some()); - assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_tokens); + assert_eq!(2, rpc_connections.data.lock().get(&1).unwrap().num_identifiers); // Ensure data is cleared. drop(reserved); From f78d74be602aa7ae61d4c176c7dbdc249205fe78 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 26 Mar 2024 18:16:59 +0200 Subject: [PATCH 32/36] chainHead: Add comment for limit reached on invalid subscription ID Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs | 6 ++++++ 1 file changed, 6 insertions(+) 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 b2334ba01f9d..975abbca4b68 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 @@ -242,6 +242,8 @@ where .subscriptions .contains_subscription(connection_details.id(), &follow_subscription) { + // The spec says to return `LimitReached` if the follow subscription is invalid or + // stale. return ResponsePayload::success(MethodResponse::LimitReached); } @@ -362,6 +364,8 @@ where .subscriptions .contains_subscription(connection_details.id(), &follow_subscription) { + // The spec says to return `LimitReached` if the follow subscription is invalid or + // stale. return ResponsePayload::success(MethodResponse::LimitReached); } @@ -446,6 +450,8 @@ where .subscriptions .contains_subscription(connection_details.id(), &follow_subscription) { + // The spec says to return `LimitReached` if the follow subscription is invalid or + // stale. return ResponsePayload::success(MethodResponse::LimitReached); } From 9b9d196559466339987bd293e48d0ecb76b9471d Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 27 Mar 2024 16:59:10 +0200 Subject: [PATCH 33/36] chainHead: Guard fn with cfg test Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index d23d1398c90c..5b016af1aa49 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -83,7 +83,7 @@ impl> SubscriptionManagement { /// # Note /// /// Used for testing. - #[doc(hidden)] + #[cfg(test)] pub(crate) fn _from_inner( inner: Arc>>, rpc_connections: RpcConnections, From 9e7803eccc6bb46559b7fd3af8a558041015b622 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 27 Mar 2024 17:00:41 +0200 Subject: [PATCH 34/36] chainHead/tests: Adjust comment Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/chain_head/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9027142a312c..a66fa58cab92 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -3388,7 +3388,7 @@ async fn chain_head_single_connection_context() { _ => panic!("Unexpected subscription ID"), }; - // Cannot make a call from a different connection context. + // Trying to unpin from a different connection will have no effect. let _response = ChainHeadApiClient::::chain_head_unstable_unpin( &second_client, first_sub_id.clone(), From f56316af35f37441adcced7ee246ac2cea0b06da Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 27 Mar 2024 17:04:23 +0200 Subject: [PATCH 35/36] rpc-v2/connections: Remove data on empty identifiers as well Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/src/common/connections.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/substrate/client/rpc-spec-v2/src/common/connections.rs b/substrate/client/rpc-spec-v2/src/common/connections.rs index 87eac4df9e3e..c16a80bf49db 100644 --- a/substrate/client/rpc-spec-v2/src/common/connections.rs +++ b/substrate/client/rpc-spec-v2/src/common/connections.rs @@ -96,6 +96,10 @@ impl RpcConnections { let entry = data.entry(connection_id).or_insert_with(ConnectionData::default); entry.num_identifiers = entry.num_identifiers.saturating_sub(1); + + if entry.num_identifiers == 0 { + data.remove(&connection_id); + } } /// Register an identifier for the given connection. From 5195aa90f08bf028743a8d79f6afbab6a4338c38 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 2 Apr 2024 15:48:09 +0300 Subject: [PATCH 36/36] rpc-v2/cargo: Fix taplo check Signed-off-by: Alexandru Vasile --- substrate/client/rpc-spec-v2/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 808c7e083d4d..937e5c6b626a 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -44,7 +44,7 @@ futures-util = { version = "0.3.30", default-features = false } rand = "0.8.5" [dev-dependencies] -jsonrpsee = { version = "0.22", features = ["ws-client", "server"] } +jsonrpsee = { version = "0.22", features = ["server", "ws-client"] } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" }