mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 04:41:04 +00:00
Split RPCs into a separate crate (#1910)
* WIP extract RPCs into separate crate * fmt * Fix test * Remove unused deps * fix import * WIP: Fix up errors and most tests. Start extracintg some tests/code to rpc crate * MockRpcClient sync or async * MockRpcClient only async but better type inference * WIP MockRpcClient FnMuts and some test updates to use it * Get all but one test working with new MockRpcClient * WIP trying to debug failure * WIP, Tests mostly fixed, need to add back oen more * Get mock RPC tests working * fmt * fmt * Clippy and comment tweak * update CI to explicitly check subxt-rpc features * clippy * small tweaks after pass over * feature flag rename * update some docs * Fix some examples * fmt * Fix features flags to work with web/wasm32 * Fix unused dep warning * explicit targets in wasm CI * Add better crate level docs * fmt * Address review comments * Comment out flaky test for now and make more obvious how similar POlkadot and Substrate configs are * Not a doc comment * Remove unused imports
This commit is contained in:
@@ -2,13 +2,13 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::rpc_methods::{ChainHeadRpcMethods, FollowEvent};
|
||||
use crate::config::Config;
|
||||
use crate::error::Error;
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use futures::{FutureExt, Stream, StreamExt, TryStreamExt};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use subxt_rpcs::methods::chain_head::{ChainHeadRpcMethods, FollowEvent};
|
||||
|
||||
/// A `Stream` whose goal is to remain subscribed to `chainHead_follow`. It will re-subscribe if the subscription
|
||||
/// is ended for any reason, and it will return the current `subscription_id` as an event, along with the other
|
||||
@@ -113,8 +113,10 @@ impl<Hash> FollowStream<Hash> {
|
||||
.to_owned(),
|
||||
));
|
||||
};
|
||||
// Return both:
|
||||
// Map stream errors into the higher level subxt one:
|
||||
let stream = stream.map_err(|e| e.into());
|
||||
let stream: FollowEventStream<T::Hash> = Box::pin(stream);
|
||||
// Return both:
|
||||
Ok((stream, sub_id))
|
||||
})
|
||||
}),
|
||||
@@ -215,12 +217,10 @@ impl<Hash> Stream for FollowStream<Hash> {
|
||||
#[cfg(test)]
|
||||
pub(super) mod test_utils {
|
||||
use super::*;
|
||||
use crate::backend::chain_head::rpc_methods::{
|
||||
BestBlockChanged, Finalized, Initialized, NewBlock,
|
||||
};
|
||||
use crate::config::substrate::H256;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use subxt_rpcs::methods::chain_head::{BestBlockChanged, Finalized, Initialized, NewBlock};
|
||||
|
||||
/// Given some events, returns a follow stream getter that we can use in
|
||||
/// place of the usual RPC method.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::follow_stream_unpin::{BlockRef, FollowStreamMsg, FollowStreamUnpin};
|
||||
use crate::backend::chain_head::rpc_methods::{FollowEvent, Initialized, RuntimeEvent};
|
||||
use crate::config::BlockHash;
|
||||
use crate::error::{Error, RpcError};
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
@@ -12,6 +11,7 @@ use std::ops::DerefMut;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use subxt_rpcs::methods::chain_head::{FollowEvent, Initialized, RuntimeEvent};
|
||||
|
||||
/// A `Stream` which builds on `FollowStreamDriver`, and allows multiple subscribers to obtain events
|
||||
/// from the single underlying subscription (each being provided an `Initialized` message and all new
|
||||
@@ -454,8 +454,11 @@ where
|
||||
.iter()
|
||||
.position(|b| b.hash() == p.hash())
|
||||
else {
|
||||
return Poll::Ready(Some(Err(RpcError::DisconnectedWillReconnect(
|
||||
"Missed at least one block when the connection was lost".to_owned(),
|
||||
return Poll::Ready(Some(Err(RpcError::ClientError(
|
||||
subxt_rpcs::Error::DisconnectedWillReconnect(
|
||||
"Missed at least one block when the connection was lost"
|
||||
.to_owned(),
|
||||
),
|
||||
)
|
||||
.into())));
|
||||
};
|
||||
@@ -739,7 +742,7 @@ mod test {
|
||||
)
|
||||
);
|
||||
assert!(
|
||||
matches!(&evs[1], Err(Error::Rpc(RpcError::DisconnectedWillReconnect(e))) if e.contains("Missed at least one block when the connection was lost"))
|
||||
matches!(&evs[1], Err(Error::Rpc(RpcError::ClientError(subxt_rpcs::Error::DisconnectedWillReconnect(e)))) if e.contains("Missed at least one block when the connection was lost"))
|
||||
);
|
||||
assert_eq!(
|
||||
evs[2].as_ref().unwrap(),
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
use super::follow_stream::FollowStream;
|
||||
use super::ChainHeadRpcMethods;
|
||||
use crate::backend::chain_head::rpc_methods::{
|
||||
BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock,
|
||||
};
|
||||
use crate::config::{BlockHash, Config};
|
||||
use crate::error::Error;
|
||||
use futures::stream::{FuturesUnordered, Stream, StreamExt};
|
||||
use subxt_rpcs::methods::chain_head::{
|
||||
BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock,
|
||||
};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
|
||||
@@ -16,15 +16,10 @@ mod follow_stream_driver;
|
||||
mod follow_stream_unpin;
|
||||
mod storage_items;
|
||||
|
||||
pub mod rpc_methods;
|
||||
|
||||
use self::follow_stream_driver::FollowStreamFinalizedHeads;
|
||||
use self::rpc_methods::{
|
||||
FollowEvent, MethodResponse, RuntimeEvent, StorageQuery, StorageQueryType, StorageResultType,
|
||||
};
|
||||
use crate::backend::{
|
||||
rpc::RpcClient, utils::retry, Backend, BlockRef, BlockRefT, RuntimeVersion, StorageResponse,
|
||||
StreamOf, StreamOfResults, TransactionStatus,
|
||||
utils::retry, Backend, BlockRef, BlockRefT, RuntimeVersion, StorageResponse, StreamOf,
|
||||
StreamOfResults, TransactionStatus,
|
||||
};
|
||||
use crate::config::BlockHash;
|
||||
use crate::error::{Error, RpcError};
|
||||
@@ -36,9 +31,18 @@ use futures::{Stream, StreamExt};
|
||||
use std::collections::HashMap;
|
||||
use std::task::Poll;
|
||||
use storage_items::StorageItems;
|
||||
use subxt_rpcs::methods::chain_head::{
|
||||
FollowEvent, MethodResponse, RuntimeEvent, StorageQuery, StorageQueryType, StorageResultType,
|
||||
};
|
||||
use subxt_rpcs::RpcClient;
|
||||
|
||||
/// Re-export RPC types and methods from [`subxt_rpcs::methods::chain_head`].
|
||||
pub mod rpc_methods {
|
||||
pub use subxt_rpcs::methods::legacy::*;
|
||||
}
|
||||
|
||||
// Expose the RPC methods.
|
||||
pub use rpc_methods::ChainHeadRpcMethods;
|
||||
pub use subxt_rpcs::methods::chain_head::ChainHeadRpcMethods;
|
||||
|
||||
/// Configure and build an [`ChainHeadBackend`].
|
||||
pub struct ChainHeadBackendBuilder<T> {
|
||||
@@ -213,7 +217,7 @@ impl<T: Config> ChainHeadBackend<T> {
|
||||
|
||||
let header = match res {
|
||||
Ok(header) => header,
|
||||
Err(e) => return Some(Err(e)),
|
||||
Err(e) => return Some(Err(e.into())),
|
||||
};
|
||||
|
||||
Some(Ok((header, block_ref.into())))
|
||||
@@ -338,13 +342,18 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
}
|
||||
|
||||
async fn genesis_hash(&self) -> Result<T::Hash, Error> {
|
||||
retry(|| self.methods.chainspec_v1_genesis_hash()).await
|
||||
retry(|| async {
|
||||
let genesis_hash = self.methods.chainspec_v1_genesis_hash().await?;
|
||||
Ok(genesis_hash)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn block_header(&self, at: T::Hash) -> Result<Option<T::Header>, Error> {
|
||||
retry(|| async {
|
||||
let sub_id = get_subscription_id(&self.follow_handle).await?;
|
||||
self.methods.chainhead_v1_header(&sub_id, at).await
|
||||
let header = self.methods.chainhead_v1_header(&sub_id, at).await?;
|
||||
Ok(header)
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -357,9 +366,7 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
let follow_events = self.follow_handle.subscribe().events();
|
||||
let status = self.methods.chainhead_v1_body(&sub_id, at).await?;
|
||||
let operation_id = match status {
|
||||
MethodResponse::LimitReached => {
|
||||
return Err(RpcError::request_rejected("limit reached").into())
|
||||
}
|
||||
MethodResponse::LimitReached => return Err(RpcError::LimitReached.into()),
|
||||
MethodResponse::Started(s) => s.operation_id,
|
||||
};
|
||||
|
||||
@@ -653,22 +660,21 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
let tx_progress_ev = match tx_progress.poll_next_unpin(cx) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(None) => return Poll::Ready(err_other("No more transaction progress events, but we haven't seen a Finalized one yet")),
|
||||
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
|
||||
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e.into()))),
|
||||
Poll::Ready(Some(Ok(ev))) => ev,
|
||||
};
|
||||
|
||||
// When we get one, map it to the correct format (or for finalized ev, wait for the pinned block):
|
||||
use subxt_rpcs::methods::chain_head::TransactionStatus as RpcTransactionStatus;
|
||||
let tx_progress_ev = match tx_progress_ev {
|
||||
rpc_methods::TransactionStatus::Finalized { block } => {
|
||||
RpcTransactionStatus::Finalized { block } => {
|
||||
// We'll wait until we have seen this hash, to try to guarantee
|
||||
// that when we return this event, the corresponding block is
|
||||
// pinned and accessible.
|
||||
finalized_hash = Some(block.hash);
|
||||
continue;
|
||||
}
|
||||
rpc_methods::TransactionStatus::BestChainBlockIncluded {
|
||||
block: Some(block),
|
||||
} => {
|
||||
RpcTransactionStatus::BestChainBlockIncluded { block: Some(block) } => {
|
||||
// Look up a pinned block ref if we can, else return a non-pinned
|
||||
// block that likely isn't accessible. We have no guarantee that a best
|
||||
// block on the node a tx was sent to will ever be known about on the
|
||||
@@ -679,20 +685,20 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
};
|
||||
TransactionStatus::InBestBlock { hash: block_ref }
|
||||
}
|
||||
rpc_methods::TransactionStatus::BestChainBlockIncluded { block: None } => {
|
||||
RpcTransactionStatus::BestChainBlockIncluded { block: None } => {
|
||||
TransactionStatus::NoLongerInBestBlock
|
||||
}
|
||||
rpc_methods::TransactionStatus::Broadcasted => TransactionStatus::Broadcasted,
|
||||
rpc_methods::TransactionStatus::Dropped { error, .. } => {
|
||||
RpcTransactionStatus::Broadcasted => TransactionStatus::Broadcasted,
|
||||
RpcTransactionStatus::Dropped { error, .. } => {
|
||||
TransactionStatus::Dropped { message: error }
|
||||
}
|
||||
rpc_methods::TransactionStatus::Error { error } => {
|
||||
RpcTransactionStatus::Error { error } => {
|
||||
TransactionStatus::Error { message: error }
|
||||
}
|
||||
rpc_methods::TransactionStatus::Invalid { error } => {
|
||||
RpcTransactionStatus::Invalid { error } => {
|
||||
TransactionStatus::Invalid { message: error }
|
||||
}
|
||||
rpc_methods::TransactionStatus::Validated => TransactionStatus::Validated,
|
||||
RpcTransactionStatus::Validated => TransactionStatus::Validated,
|
||||
};
|
||||
return Poll::Ready(Some(Ok(tx_progress_ev)));
|
||||
}
|
||||
@@ -718,9 +724,7 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for ChainHeadBackend<T> {
|
||||
.chainhead_v1_call(&sub_id, at, method, call_parameters)
|
||||
.await?;
|
||||
let operation_id = match status {
|
||||
MethodResponse::LimitReached => {
|
||||
return Err(RpcError::request_rejected("limit reached").into())
|
||||
}
|
||||
MethodResponse::LimitReached => return Err(RpcError::LimitReached.into()),
|
||||
MethodResponse::Started(s) => s.operation_id,
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,6 @@
|
||||
|
||||
use super::follow_stream_driver::FollowStreamDriverHandle;
|
||||
use super::follow_stream_unpin::BlockRef;
|
||||
use super::rpc_methods::{
|
||||
ChainHeadRpcMethods, FollowEvent, MethodResponse, StorageQuery, StorageResult,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::{Error, RpcError};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
@@ -15,6 +12,9 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use subxt_rpcs::methods::chain_head::{
|
||||
ChainHeadRpcMethods, FollowEvent, MethodResponse, StorageQuery, StorageResult,
|
||||
};
|
||||
|
||||
/// Obtain a stream of storage items given some query. this handles continuing
|
||||
/// and stopping under the hood, and returns a stream of `StorageResult`s.
|
||||
@@ -45,9 +45,7 @@ impl<T: Config> StorageItems<T> {
|
||||
.chainhead_v1_storage(&sub_id, at, queries, None)
|
||||
.await?;
|
||||
let operation_id: Arc<str> = match status {
|
||||
MethodResponse::LimitReached => {
|
||||
return Err(RpcError::request_rejected("limit reached").into())
|
||||
}
|
||||
MethodResponse::LimitReached => return Err(RpcError::LimitReached.into()),
|
||||
MethodResponse::Started(s) => s.operation_id.into(),
|
||||
};
|
||||
|
||||
@@ -59,7 +57,12 @@ impl<T: Config> StorageItems<T> {
|
||||
let operation_id = operation_id.clone();
|
||||
let methods = methods.clone();
|
||||
|
||||
Box::pin(async move { methods.chainhead_v1_continue(&sub_id, &operation_id).await })
|
||||
Box::pin(async move {
|
||||
methods
|
||||
.chainhead_v1_continue(&sub_id, &operation_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user