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
+52 -50
View File
@@ -11,6 +11,7 @@ use crate::{
error::{
DispatchError,
Error,
RpcError,
TransactionError,
},
events::{
@@ -21,7 +22,10 @@ use crate::{
Phase,
StaticEvent,
},
rpc::SubstrateTxStatus,
rpc::{
Subscription,
SubstrateTxStatus,
},
Config,
};
use derivative::Derivative;
@@ -29,10 +33,6 @@ use futures::{
Stream,
StreamExt,
};
use jsonrpsee::core::{
client::Subscription as RpcSubscription,
Error as RpcError,
};
use sp_runtime::traits::Hash;
pub use sp_runtime::traits::SignedExtension;
@@ -41,7 +41,7 @@ pub use sp_runtime::traits::SignedExtension;
#[derive(Derivative)]
#[derivative(Debug(bound = "C: std::fmt::Debug"))]
pub struct TxProgress<T: Config, C> {
sub: Option<RpcSubscription<SubstrateTxStatus<T::Hash, T::Hash>>>,
sub: Option<Subscription<SubstrateTxStatus<T::Hash, T::Hash>>>,
ext_hash: T::Hash,
client: C,
}
@@ -54,7 +54,7 @@ impl<T: Config, C> Unpin for TxProgress<T, C> {}
impl<T: Config, C> TxProgress<T, C> {
/// Instantiate a new [`TxProgress`] from a custom subscription.
pub fn new(
sub: RpcSubscription<SubstrateTxStatus<T::Hash, T::Hash>>,
sub: Subscription<SubstrateTxStatus<T::Hash, T::Hash>>,
client: C,
ext_hash: T::Hash,
) -> Self {
@@ -71,7 +71,11 @@ impl<T: Config, C> TxProgress<T, C> {
}
}
impl<T: Config, C: OnlineClientT<T>> TxProgress<T, C> {
impl<T, C> TxProgress<T, C>
where
T: Config,
C: OnlineClientT<T>,
{
/// Return the next transaction status when it's emitted. This just delegates to the
/// [`futures::Stream`] implementation for [`TxProgress`], but allows you to
/// avoid importing that trait if you don't otherwise need it.
@@ -103,7 +107,7 @@ impl<T: Config, C: OnlineClientT<T>> TxProgress<T, C> {
_ => continue,
}
}
Err(RpcError::Custom("RPC subscription dropped".into()).into())
Err(RpcError("RPC subscription dropped".to_string()).into())
}
/// Wait for the transaction to be finalized, and return a [`TxInBlock`]
@@ -129,7 +133,7 @@ impl<T: Config, C: OnlineClientT<T>> TxProgress<T, C> {
_ => continue,
}
}
Err(RpcError::Custom("RPC subscription dropped".into()).into())
Err(RpcError("RPC subscription dropped".to_string()).into())
}
/// Wait for the transaction to be finalized, and for the transaction events to indicate
@@ -161,47 +165,45 @@ impl<T: Config, C: OnlineClientT<T>> Stream for TxProgress<T, C> {
None => return Poll::Ready(None),
};
sub.poll_next_unpin(cx)
.map_err(|e| e.into())
.map_ok(|status| {
match status {
SubstrateTxStatus::Future => TxStatus::Future,
SubstrateTxStatus::Ready => TxStatus::Ready,
SubstrateTxStatus::Broadcast(peers) => TxStatus::Broadcast(peers),
SubstrateTxStatus::InBlock(hash) => {
TxStatus::InBlock(TxInBlock::new(
hash,
self.ext_hash,
self.client.clone(),
))
}
SubstrateTxStatus::Retracted(hash) => TxStatus::Retracted(hash),
SubstrateTxStatus::Usurped(hash) => TxStatus::Usurped(hash),
SubstrateTxStatus::Dropped => TxStatus::Dropped,
SubstrateTxStatus::Invalid => TxStatus::Invalid,
// Only the following statuses are actually considered "final" (see the substrate
// docs on `TxStatus`). Basically, either the transaction makes it into a
// block, or we eventually give up on waiting for it to make it into a block.
// Even `Dropped`/`Invalid`/`Usurped` transactions might make it into a block eventually.
//
// As an example, a transaction that is `Invalid` on one node due to having the wrong
// nonce might still be valid on some fork on another node which ends up being finalized.
// Equally, a transaction `Dropped` from one node may still be in the transaction pool,
// and make it into a block, on another node. Likewise with `Usurped`.
SubstrateTxStatus::FinalityTimeout(hash) => {
self.sub = None;
TxStatus::FinalityTimeout(hash)
}
SubstrateTxStatus::Finalized(hash) => {
self.sub = None;
TxStatus::Finalized(TxInBlock::new(
hash,
self.ext_hash,
self.client.clone(),
))
}
sub.poll_next_unpin(cx).map_ok(|status| {
match status {
SubstrateTxStatus::Future => TxStatus::Future,
SubstrateTxStatus::Ready => TxStatus::Ready,
SubstrateTxStatus::Broadcast(peers) => TxStatus::Broadcast(peers),
SubstrateTxStatus::InBlock(hash) => {
TxStatus::InBlock(TxInBlock::new(
hash,
self.ext_hash,
self.client.clone(),
))
}
})
SubstrateTxStatus::Retracted(hash) => TxStatus::Retracted(hash),
SubstrateTxStatus::Usurped(hash) => TxStatus::Usurped(hash),
SubstrateTxStatus::Dropped => TxStatus::Dropped,
SubstrateTxStatus::Invalid => TxStatus::Invalid,
// Only the following statuses are actually considered "final" (see the substrate
// docs on `TxStatus`). Basically, either the transaction makes it into a
// block, or we eventually give up on waiting for it to make it into a block.
// Even `Dropped`/`Invalid`/`Usurped` transactions might make it into a block eventually.
//
// As an example, a transaction that is `Invalid` on one node due to having the wrong
// nonce might still be valid on some fork on another node which ends up being finalized.
// Equally, a transaction `Dropped` from one node may still be in the transaction pool,
// and make it into a block, on another node. Likewise with `Usurped`.
SubstrateTxStatus::FinalityTimeout(hash) => {
self.sub = None;
TxStatus::FinalityTimeout(hash)
}
SubstrateTxStatus::Finalized(hash) => {
self.sub = None;
TxStatus::Finalized(TxInBlock::new(
hash,
self.ext_hash,
self.client.clone(),
))
}
}
})
}
}