mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 09:31:12 +00:00
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-core-client
This commit is contained in:
@@ -52,8 +52,9 @@ jobs:
|
||||
with:
|
||||
# Use this issue template:
|
||||
filename: .github/issue_templates/nightly_run_failed.md
|
||||
# Don't create a new issue; skip updating existing:
|
||||
update_existing: false
|
||||
# Update existing issue if found; hopefully will make it clearer
|
||||
# that it is still an issue:
|
||||
update_existing: true
|
||||
# Look for new *open* issues in this search (we want to
|
||||
# create a new one if we only find closed versions):
|
||||
search_existing: open
|
||||
|
||||
@@ -60,6 +60,14 @@ This can be installed from source via cargo:
|
||||
cargo install --git https://github.com/paritytech/substrate node-cli --tag=polkadot-v0.9.10 --force
|
||||
```
|
||||
|
||||
## Real world usage
|
||||
|
||||
Please add your project to this list via a PR.
|
||||
|
||||
- [cargo-contract](https://github.com/paritytech/cargo-contract/pull/79) CLI for interacting with Wasm smart contracts.
|
||||
- [xcm-cli](https://github.com/ascjones/xcm-cli) CLI for submitting XCM messages.
|
||||
- [phala-pherry](https://github.com/Phala-Network/phala-blockchain/tree/master/standalone/pherry) The relayer between Phala blockchain and the off-chain Secure workers.
|
||||
|
||||
**Alternatives**
|
||||
|
||||
[substrate-api-client](https://github.com/scs/substrate-api-client) provides similar functionality.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
//! polkadot --dev --tmp
|
||||
//! ```
|
||||
|
||||
use futures::StreamExt;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
@@ -61,8 +62,7 @@ async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.unwrap()?;
|
||||
.await?;
|
||||
|
||||
let transfer_event =
|
||||
balance_transfer.find_first_event::<polkadot::balances::events::Transfer>()?;
|
||||
@@ -94,8 +94,7 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.await?
|
||||
.wait_for_finalized()
|
||||
.await
|
||||
.unwrap()?;
|
||||
.await?;
|
||||
|
||||
// Now we know it's been finalized, we can get hold of a couple of
|
||||
// details, including events. Calling `wait_for_finalized_success` is
|
||||
@@ -146,9 +145,9 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.sign_and_submit_then_watch(&signer)
|
||||
.await?;
|
||||
|
||||
loop {
|
||||
while let Some(ev) = balance_transfer_progress.next().await {
|
||||
let ev = ev?;
|
||||
use subxt::TransactionStatus::*;
|
||||
let ev = balance_transfer_progress.next().await.unwrap()?;
|
||||
|
||||
// Made it into a block, but not finalized.
|
||||
if let InBlock(details) = ev {
|
||||
@@ -194,4 +193,6 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Current transaction status: {:?}", ev);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -247,12 +247,12 @@ where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
match sub.next().await {
|
||||
None => None,
|
||||
Some(Ok(next)) => Some(next),
|
||||
Some(Err(e)) => {
|
||||
log::error!("Subscription {} failed: {:?} dropping", sub_name, e);
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+113
-105
@@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::task::Poll;
|
||||
|
||||
use sp_core::storage::StorageKey;
|
||||
use sp_runtime::traits::Hash;
|
||||
pub use sp_runtime::traits::SignedExtension;
|
||||
@@ -30,7 +32,14 @@ use crate::{
|
||||
Config,
|
||||
Phase,
|
||||
};
|
||||
use jsonrpsee::core::client::Subscription as RpcSubscription;
|
||||
use futures::{
|
||||
Stream,
|
||||
StreamExt,
|
||||
};
|
||||
use jsonrpsee::core::{
|
||||
client::Subscription as RpcSubscription,
|
||||
Error as RpcError,
|
||||
};
|
||||
|
||||
/// This struct represents a subscription to the progress of some transaction, and is
|
||||
/// returned from [`crate::SubmittableExtrinsic::sign_and_submit_then_watch()`].
|
||||
@@ -41,6 +50,11 @@ pub struct TransactionProgress<'client, T: Config> {
|
||||
client: &'client Client<T>,
|
||||
}
|
||||
|
||||
// The above type is not `Unpin` by default unless the generic param `T` is,
|
||||
// so we manually make it clear that Unpin is actually fine regardless of `T`
|
||||
// (we don't care if this moves around in memory while it's "pinned").
|
||||
impl<'client, T: Config> Unpin for TransactionProgress<'client, T> {}
|
||||
|
||||
impl<'client, T: Config> TransactionProgress<'client, T> {
|
||||
pub(crate) fn new(
|
||||
sub: RpcSubscription<SubstrateTransactionStatus<T::Hash, T::Hash>>,
|
||||
@@ -54,18 +68,106 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the next transaction status when it's emitted.
|
||||
pub async fn next(&mut self) -> Option<Result<TransactionStatus<'client, T>, Error>> {
|
||||
// Return `None` if the subscription has been dropped:
|
||||
let sub = match &mut self.sub {
|
||||
/// Return the next transaction status when it's emitted. This just delegates to the
|
||||
/// [`futures::Stream`] implementation for [`TransactionProgress`], but allows you to
|
||||
/// avoid importing that trait if you don't otherwise need it.
|
||||
pub async fn next_item(
|
||||
&mut self,
|
||||
) -> Option<Result<TransactionStatus<'client, T>, Error>> {
|
||||
self.next().await
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be in a block (but not necessarily finalized), and return
|
||||
/// an [`TransactionInBlock`] instance when this happens, or an error if there was a problem
|
||||
/// waiting for this to happen.
|
||||
///
|
||||
/// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the
|
||||
/// transaction progresses, use [`TransactionProgress::next_item()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_in_block(
|
||||
mut self,
|
||||
) -> Result<TransactionInBlock<'client, T>, Error> {
|
||||
while let Some(status) = self.next_item().await {
|
||||
match status? {
|
||||
// Finalized or otherwise in a block! Return.
|
||||
TransactionStatus::InBlock(s) | TransactionStatus::Finalized(s) => {
|
||||
return Ok(s)
|
||||
}
|
||||
// Error scenarios; return the error.
|
||||
TransactionStatus::FinalityTimeout(_) => {
|
||||
return Err(TransactionError::FinalitySubscriptionTimeout.into())
|
||||
}
|
||||
// Ignore anything else and wait for next status event:
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(RpcError::Custom("RPC subscription dropped".into()).into())
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be finalized, and return a [`TransactionInBlock`]
|
||||
/// instance when it is, or an error if there was a problem waiting for finalization.
|
||||
///
|
||||
/// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the
|
||||
/// transaction progresses, use [`TransactionProgress::next_item()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_finalized(
|
||||
mut self,
|
||||
) -> Result<TransactionInBlock<'client, T>, Error> {
|
||||
while let Some(status) = self.next_item().await {
|
||||
match status? {
|
||||
// Finalized! Return.
|
||||
TransactionStatus::Finalized(s) => return Ok(s),
|
||||
// Error scenarios; return the error.
|
||||
TransactionStatus::FinalityTimeout(_) => {
|
||||
return Err(TransactionError::FinalitySubscriptionTimeout.into())
|
||||
}
|
||||
// Ignore and wait for next status event:
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(RpcError::Custom("RPC subscription dropped".into()).into())
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be finalized, and for the transaction events to indicate
|
||||
/// that the transaction was successful. Returns the events associated with the transaction,
|
||||
/// as well as a couple of other details (block hash and extrinsic hash).
|
||||
///
|
||||
/// **Note:** consumes self. If you'd like to perform multiple actions as progress is made,
|
||||
/// use [`TransactionProgress::next_item()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_finalized_success(self) -> Result<TransactionEvents<T>, Error> {
|
||||
let evs = self.wait_for_finalized().await?.wait_for_success().await?;
|
||||
Ok(evs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'client, T: Config> Stream for TransactionProgress<'client, T> {
|
||||
type Item = Result<TransactionStatus<'client, T>, Error>;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<Self::Item>> {
|
||||
let sub = match self.sub.as_mut() {
|
||||
Some(sub) => sub,
|
||||
None => return None,
|
||||
None => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
// Return the next item otherwise:
|
||||
let res = sub.next().await?;
|
||||
Some(
|
||||
res.map(|status| {
|
||||
sub.poll_next_unpin(cx)
|
||||
.map_err(|e| e.into())
|
||||
.map_ok(|status| {
|
||||
match status {
|
||||
SubstrateTransactionStatus::Future => TransactionStatus::Future,
|
||||
SubstrateTransactionStatus::Ready => TransactionStatus::Ready,
|
||||
@@ -110,100 +212,6 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(Into::into),
|
||||
)
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be in a block (but not necessarily finalized), and return
|
||||
/// an [`TransactionInBlock`] instance when this happens, or an error if there was a problem
|
||||
/// waiting for this to happen.
|
||||
///
|
||||
/// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the
|
||||
/// transaction progresses, use [`TransactionProgress::next()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_in_block(
|
||||
mut self,
|
||||
) -> Option<Result<TransactionInBlock<'client, T>, Error>> {
|
||||
loop {
|
||||
match self.next().await? {
|
||||
Ok(status) => {
|
||||
match status {
|
||||
// Finalized or otherwise in a block! Return.
|
||||
TransactionStatus::InBlock(s)
|
||||
| TransactionStatus::Finalized(s) => return Some(Ok(s)),
|
||||
// Error scenarios; return the error.
|
||||
TransactionStatus::FinalityTimeout(_) => {
|
||||
return Some(Err(
|
||||
TransactionError::FinalitySubscriptionTimeout.into(),
|
||||
))
|
||||
}
|
||||
// Ignore anything else and wait for next status event:
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(err) => return Some(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be finalized, and return a [`TransactionInBlock`]
|
||||
/// instance when it is, or an error if there was a problem waiting for finalization.
|
||||
///
|
||||
/// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the
|
||||
/// transaction progresses, use [`TransactionProgress::next()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_finalized(
|
||||
mut self,
|
||||
) -> Option<Result<TransactionInBlock<'client, T>, Error>> {
|
||||
loop {
|
||||
match self.next().await? {
|
||||
Ok(status) => {
|
||||
match status {
|
||||
// finalized! return.
|
||||
TransactionStatus::Finalized(s) => return Some(Ok(s)),
|
||||
// error scenarios; return the error.
|
||||
TransactionStatus::FinalityTimeout(_) => {
|
||||
return Some(Err(
|
||||
TransactionError::FinalitySubscriptionTimeout.into(),
|
||||
))
|
||||
}
|
||||
// ignore and wait for next status event:
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(err) => return Some(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be finalized, and for the transaction events to indicate
|
||||
/// that the transaction was successful. Returns the events associated with the transaction,
|
||||
/// as well as a couple of other details (block hash and extrinsic hash).
|
||||
///
|
||||
/// **Note:** consumes self. If you'd like to perform multiple actions as progress is made,
|
||||
/// use [`TransactionProgress::next()`] instead.
|
||||
///
|
||||
/// **Note:** transaction statuses like `Invalid` and `Usurped` are ignored, because while they
|
||||
/// may well indicate with some probability that the transaction will not make it into a block,
|
||||
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
|
||||
/// level [`TransactionProgress::next()`] API if you'd like to handle these statuses yourself.
|
||||
pub async fn wait_for_finalized_success(
|
||||
self,
|
||||
) -> Option<Result<TransactionEvents<T>, Error>> {
|
||||
let finalized = match self.wait_for_finalized().await? {
|
||||
Ok(f) => f,
|
||||
Err(err) => return Some(Err(err)),
|
||||
};
|
||||
|
||||
Some(finalized.wait_for_success().await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +220,7 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
|
||||
//* Note that the number of finality watchers is, at the time of writing, found in the constant
|
||||
//* `MAX_FINALITY_WATCHERS` in the `sc_transaction_pool` crate.
|
||||
//*
|
||||
/// Possible transaction statuses returned from our [`TransactionProgress::next()`] call.
|
||||
/// Possible transaction statuses returned from our [`TransactionProgress::next_item()`] call.
|
||||
///
|
||||
/// These status events can be grouped based on their kinds as:
|
||||
///
|
||||
|
||||
@@ -84,7 +84,7 @@ async fn chain_subscribe_blocks() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let mut blocks = client.rpc().subscribe_blocks().await.unwrap();
|
||||
blocks.next().await.unwrap();
|
||||
blocks.next().await.unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@@ -92,7 +92,7 @@ async fn chain_subscribe_finalized_blocks() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let mut blocks = client.rpc().subscribe_finalized_blocks().await.unwrap();
|
||||
blocks.next().await.unwrap();
|
||||
blocks.next().await.unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
|
||||
Reference in New Issue
Block a user