From b44ac6bbbed7ac323dc6715a62bf55d3e8a3b94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bigna=20H=C3=A4rdi?= Date: Tue, 27 Feb 2024 10:32:16 +0100 Subject: [PATCH] Add Jsonrpsee::new_with_client (#735) * add access function and generated with new * add wrapper and test * add test * udpate jsonrspee library --- Cargo.lock | 154 ++++++++++++++++++++-- Cargo.toml | 2 +- src/rpc/jsonrpsee_client/mod.rs | 39 +++++- testing/async/Cargo.toml | 2 + testing/async/examples/jsonrpsee_tests.rs | 55 ++++++++ 5 files changed, 242 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41afa87d7..b307668b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,7 @@ name = "ac-testing-async" version = "0.4.0" dependencies = [ "frame-support", + "jsonrpsee", "kitchensink-runtime", "pallet-balances", "pallet-staking", @@ -2362,6 +2363,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes 1.5.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.2.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hash-db" version = "0.16.0" @@ -2490,18 +2510,58 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes 1.5.0", + "http 0.2.11", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes 1.5.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.11", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -2700,20 +2760,22 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9579d0ca9fb30da026bac2f0f7d9576ec93489aeb7cd4971dd5b4617d82c79b2" +checksum = "16fcc9dd231e72d22993f1643d5f7f0db785737dbe3c3d7ca222916ab4280795" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", + "jsonrpsee-server", "jsonrpsee-types", + "tokio", ] [[package]] name = "jsonrpsee-client-transport" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9f9ed46590a8d5681975f126e22531698211b926129a40a2db47cbca429220" +checksum = "0476c96eb741b40d39dcb39d0124e3b9be9840ec77653c42a0996563ae2a53f7" dependencies = [ "futures-util", "http 0.2.11", @@ -2732,9 +2794,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776d009e2f591b78c038e0d053a796f94575d66ca4e77dd84bfc5e81419e436c" +checksum = "b974d8f6139efbe8425f32cb33302aba6d5e049556b5bfc067874e7a0da54a2e" dependencies = [ "anyhow", "async-lock", @@ -2742,8 +2804,11 @@ dependencies = [ "beef", "futures-timer", "futures-util", + "hyper", "jsonrpsee-types", + "parking_lot", "pin-project", + "rand 0.8.5", "rustc-hash", "serde", "serde_json", @@ -2753,11 +2818,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-server" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "344440ccd8492c1ca65f1391c5aa03f91189db38d602d189b9266a1a5c6a4d22" +dependencies = [ + "futures-util", + "http 0.2.11", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + [[package]] name = "jsonrpsee-types" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3266dfb045c9174b24c77c2dfe0084914bb23a6b2597d70c9dc6018392e1cd1b" +checksum = "b13dac43c1a9fc2648b37f306b0a5b0e29b2a6e1c36a33b95c1948da2494e9c5" dependencies = [ "anyhow", "beef", @@ -5551,6 +5640,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -6202,6 +6297,7 @@ dependencies = [ "base64 0.13.1", "bytes 1.5.0", "futures", + "http 0.2.11", "httparse", "log", "rand 0.8.5", @@ -7422,6 +7518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", + "bytes 1.5.0", "libc", "mio 0.8.10", "num_cpus", @@ -7462,6 +7559,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -7476,6 +7574,7 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -7545,12 +7644,36 @@ dependencies = [ "winnow 0.6.0", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -7642,6 +7765,12 @@ dependencies = [ "hash-db", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tt-call" version = "1.0.9" @@ -7845,6 +7974,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 734e93f24..2e1a5bdd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ serde_json = { version = "1.0.79", default-features = false } url = { version = "2.0.0", optional = true } # websocket dependent features -jsonrpsee = { version = "0.21", optional = true, features = ["async-client", "client-ws-transport-native-tls", "jsonrpsee-types"] } +jsonrpsee = { version = "0.22", optional = true, features = ["async-client", "client-ws-transport-native-tls", "jsonrpsee-types"] } tungstenite = { version = "0.21", optional = true, features = ["native-tls"] } ws = { version = "0.9.2", optional = true, features = ["ssl"] } diff --git a/src/rpc/jsonrpsee_client/mod.rs b/src/rpc/jsonrpsee_client/mod.rs index bdb822cf3..30b6f1018 100644 --- a/src/rpc/jsonrpsee_client/mod.rs +++ b/src/rpc/jsonrpsee_client/mod.rs @@ -15,7 +15,7 @@ use crate::rpc::{Error, Request, Result, RpcParams, Subscribe}; use jsonrpsee::{ client_transport::ws::{Url, WsTransportClientBuilder}, core::{ - client::{Client, ClientBuilder, ClientT, SubscriptionClientT}, + client::{Client, ClientBuilder, ClientT, Error as JsonrpseeError, SubscriptionClientT}, traits::ToRpcParams, }, }; @@ -33,6 +33,7 @@ pub struct JsonrpseeClient { } impl JsonrpseeClient { + /// Create a new client to a local Substrate node with default port. pub async fn with_default_url() -> Result { Self::new("ws://127.0.0.1:9944").await } @@ -59,6 +60,42 @@ impl JsonrpseeClient { let url = format!("{address}:{port:?}"); Self::new(&url).await } + + /// Create a new client with a user-generated Jsonrpsee Client. + pub fn new_with_client(client: Client) -> Self { + let inner = Arc::new(client); + Self { inner } + } +} + +impl JsonrpseeClient { + /// Checks if the client is connected to the target. + pub fn is_connected(&self) -> bool { + self.inner.is_connected() + } + + /// This is similar to [`Client::on_disconnect`] but it can be used to get + /// the reason why the client was disconnected but it's not cancel-safe. + /// + /// The typical use-case is that this method will be called after + /// [`Client::on_disconnect`] has returned in a "select loop". + /// + /// # Cancel-safety + /// + /// This method is not cancel-safe + pub async fn disconnect_reason(&self) -> JsonrpseeError { + self.inner.disconnect_reason().await + } + + /// Completes when the client is disconnected or the client's background task encountered an error. + /// If the client is already disconnected, the future produced by this method will complete immediately. + /// + /// # Cancel safety + /// + /// This method is cancel safe. + pub async fn on_disconnect(&self) { + self.inner.on_disconnect().await; + } } #[maybe_async::async_impl(?Send)] diff --git a/testing/async/Cargo.toml b/testing/async/Cargo.toml index fba3ca587..58820e8f3 100644 --- a/testing/async/Cargo.toml +++ b/testing/async/Cargo.toml @@ -8,6 +8,8 @@ edition = "2021" [dev-dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ['derive'] } tokio = { version = "1.24", features = ["rt-multi-thread", "macros", "time"] } +jsonrpsee = { version = "0.22", features = ["async-client", "client-ws-transport-native-tls", "jsonrpsee-types", "server"] } + # Substrate dependencies frame-support = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" } diff --git a/testing/async/examples/jsonrpsee_tests.rs b/testing/async/examples/jsonrpsee_tests.rs index b2c66de68..87c25a7ad 100644 --- a/testing/async/examples/jsonrpsee_tests.rs +++ b/testing/async/examples/jsonrpsee_tests.rs @@ -15,7 +15,15 @@ //! Tests for the Jsonrpseeclient Wrapper. Should happen during runtime, otherwise no connection can be etablished. +use core::panic; + +use jsonrpsee::{ + client_transport::ws::{Url, WsTransportClientBuilder}, + core::client::{Client, ClientBuilder}, + server::{RpcModule, Server}, +}; use substrate_api_client::rpc::JsonrpseeClient; +use tokio::{sync::oneshot, task, time, time::Duration}; #[tokio::main] async fn main() { @@ -30,4 +38,51 @@ async fn main() { assert!(client2.is_ok()); // No node running at this port - creation should fail. assert!(client3.is_err()); + + // Test new_with_client and extra functionality of inner Jsonrpseee client. + let (tx, mut rx) = oneshot::channel(); + let finish_message = "Finishing task"; + + // Start server. + let server = Server::builder().build("127.0.0.1:0").await.unwrap(); + let addr = server.local_addr().unwrap(); + let server_handle = server.start(RpcModule::new(())); + + // Create client and connect. + let uri = Url::parse(&format!("ws://{}", addr)).unwrap(); + let (tx1, rx1) = WsTransportClientBuilder::default().build(uri).await.unwrap(); + let client: Client = ClientBuilder::default().build_with_tokio(tx1, rx1); + let api_rpsee_client = JsonrpseeClient::new_with_client(client); + assert!(api_rpsee_client.is_connected()); + + let client_handle = task::spawn(async move { + println!("Waiting for client disconnect"); + api_rpsee_client.on_disconnect().await; + time::sleep(Duration::from_secs(2)).await; + println!("Disconnected due to: {:?}", api_rpsee_client.disconnect_reason().await); + tx.send(finish_message).unwrap(); + }); + + // Drop server such that client gets a disconnect. + drop(server_handle); + + // Wait for the disconnect message. + let timeout = 5; + let mut ctr = 0; + loop { + if let Ok(message) = rx.try_recv() { + assert_eq!(finish_message, message); + println!("{message}"); + break; + } else { + ctr += 1; + if ctr == timeout { + panic!("Timeout"); + } + time::sleep(Duration::from_secs(1)).await; + println!("sleeping.."); + } + } + + client_handle.await.unwrap(); }