Allow generalising over RPC implementation (#634)

* WIP generalising RPC client

* WIP: non-object-safe RpcClientT.. aah generics everywhere

* WIP object-safe RpcClientT trait and no more extra generics

* Get core things compiling again with object-safe RpcClientT trait

* Make jsonrpsee optional and get test-runtime working again

* cargo fmt

* add RpcParams object to enforce correct formatting of rps params

* Wee tweaks

* clippy fixes

* cargo fmt

* TWeak a few types

* make sure we get jsonrpsee-types, too

* Add examples for rpc_params/RpcParams

* more doc tweaks

* remove a now unneeded dev note

* Option<Box<RawValue>> instead to avoid allocations in some cases

* update docs

* tweak RpcClientT trait docs

* Tweak docs around RpcClient and RpcClientT. Don't expose RpcClientT directly

* more doc tweaking about RpcParams and undo decision not to expose RpcParamsT

* Doc tweak

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>

* more doc tweaks

* Fix doc thing

* Add an example of injecting a custom RPC client

* Fix a typo

* Address clippy things in example

* Fix a silly typo

* another clippy fix

* rpc_params to panic instead of returning a result, like serde_json::json, and deref on Rpc<T>

* fix docs

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-08-31 10:00:49 +01:00
committed by GitHub
parent 5ff849318b
commit 599107b432
17 changed files with 737 additions and 143 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This example should compile but should aos fail to work, since we've modified the
//! This example should compile but should fail to work, since we've modified the
//! config to not align with a Polkadot node.
use sp_keyring::AccountKeyring;
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::{
fmt::Write,
pin::Pin,
sync::{
Arc,
Mutex,
},
};
use subxt::{
rpc::{
RawValue,
RpcClientT,
RpcFuture,
RpcSubscription,
},
OnlineClient,
PolkadotConfig,
};
// A dummy RPC client that doesn't actually handle requests properly
// at all, but instead just logs what requests to it were made.
struct MyLoggingClient {
log: Arc<Mutex<String>>,
}
// We have to implement this fairly low level trait to turn [`MyLoggingClient`]
// into an RPC client that we can make use of in Subxt. Here we just log the requests
// made but don't forward them to any real node, and instead just return nonsense.
impl RpcClientT for MyLoggingClient {
fn request_raw<'a>(
&'a self,
method: &'a str,
params: Option<Box<RawValue>>,
) -> RpcFuture<'a, Box<RawValue>> {
writeln!(
self.log.lock().unwrap(),
"{method}({})",
params.as_ref().map(|p| p.get()).unwrap_or("[]")
)
.unwrap();
// We've logged the request; just return garbage. Because a boxed future is returned,
// you're able to run whatever async code you'd need to actually talk to a node.
let res = RawValue::from_string("[]".to_string()).unwrap();
Box::pin(std::future::ready(Ok(res)))
}
fn subscribe_raw<'a>(
&'a self,
sub: &'a str,
params: Option<Box<RawValue>>,
unsub: &'a str,
) -> RpcFuture<'a, RpcSubscription> {
writeln!(
self.log.lock().unwrap(),
"{sub}({}) (unsub: {unsub})",
params.as_ref().map(|p| p.get()).unwrap_or("[]")
)
.unwrap();
// We've logged the request; just return garbage. Because a boxed future is returned,
// and that will return a boxed Stream impl, you have a bunch of flexibility to build
// and return whatever type of Stream you see fit.
let res = RawValue::from_string("[]".to_string()).unwrap();
let stream = futures::stream::once(async move { Ok(res) });
let stream: Pin<Box<dyn futures::Stream<Item = _> + Send>> = Box::pin(stream);
Box::pin(std::future::ready(Ok(stream)))
}
}
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Instantiate our replacement RPC client.
let log = Arc::default();
let rpc_client = MyLoggingClient {
log: Arc::clone(&log),
};
// Pass this into our OnlineClient to instantiate it. This will lead to some
// RPC calls being made to fetch chain details/metadata, which will immediately
// fail..
let _ = OnlineClient::<PolkadotConfig>::from_rpc_client(rpc_client).await;
// But, we can see that the calls were made via our custom RPC client:
println!("Log of calls made:\n\n{}", log.lock().unwrap().as_str());
Ok(())
}