mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 00:31:07 +00:00
Merge v0.50.x to master (#2127)
* v0.50.0: Integrate frame-decode, redo storage APIs and break up Error. (#2100) * WIP integrating new frame-decode and working out new storage APIS * WIP: first pass adding new storage things to subxt-core * Second pass over Address type and start impl in Subxt * WIP new storage APIs * WIP New storage APIs roughly completed, lots of errors still * Remove PlainorMap enum; plain and map values now use same struct to simplify usage * Begin 'fixing' errors * WIP splitting errors and tidying payload/address traits * Get subxt-core compiling * Small fixes in subxt-core and remove metadata mod * subxt-core: cargo check --all-targets passes * Fix test * WIP starting to update subxt from subxt-core changes * WIP splitting up subxt errors into smaller variants * WIP errors: add DispatchError errors * Port new Storage APIs to subxt-core * cargo check -p subxt passes * Quick-fix errors in subxt-cli (explore subcommand) * fmt * Finish fixing codegen up and start fixing examples * get Subxt examples compiling and bytes_at for constants * Add some arcs to limit lifetimes in subxt/subxt-core storage APIs * A little Arcing to allow more method chaining in Storage APIs, aligning with Subxt * Update codegen test * cargo check --all-targets passing * cargo check --features 'unstable-light-client' passing * clippy * Remove unused dep in subxt * use published frame-decode * fix wasm-example * Add new tx extension to fix daily tests * Remove unused subxt_core::dynamic::DecodedValue type * Update book to match changes * Update docs to fix more broken bits * Add missing docs * fmt * allow larger result errs for now * Add missing alloc imports in subxt-core * Fix doc tests and fix bug getting constant info * Fix V14 -> Metadata transform for storage & constants * Fix parachain example * Fix FFI example * BlockLength decodes t ostruct, not u128 * use fetch/iter shorthands rather than entry in most storage tests * Fix some integration tests * Fix Runtime codegen tests * Expose the dynamic custom_value selecter and use in a UI test * Update codegen metadata * Tidy CLI storage query and support (str,str) as a storage address * Add (str,str) as valid constant address too * Show string tuple in constants example * Via the magic of traits, avoid needing any clones of queries/addresses and accept references to them * clippy * [v0.50] update scale-info-legacy and frame-decode to latest (#2119) * bump scale-info-legacy and frame-decode to latest * Remove something we don't need in this PR * Fully remove unused for now dep * [v0.50] Convert historic metadata to subxt::Metadata (#2120) * First pass converting historic metadatas to our subxt::Metadata type * use published frame-decode * fmt and rename legacy metadata macro * Enable legacy feature where needed in subxt_metadata so it compiles on its own * Use cargo hack more in CI and fix subxt-metadata features * Add tests for metadata conversion (need to optimise; some too expensive right now * Address performance and equality issues in metadata conversion testing * fmt * fmt all * clippy * Fix a doc link * Test codegen and fixes to make it work * Remove local frame-decode patch * bump frame-decode to latest * [v0.50.0] Allow visiting extrinsic fields in subxt_historic (#2124) * Allow visiting extrinsic fields * fmt * Don't use local scale-decode dep * Clippy and tidy * Extend 'subxt codegen' CLI to work with legacy metadatas * Simplify historic extrinsics example now that AccountId32s have paths/names * clippy * clippy * clippy.. * Allow visiting storage values, too, and clean up extrinsic visiting a little by narrowing lifetime * Try to fix flaky test * Add custom value decode to extrinsics example * Remove useless else branch ra thought I needed * Simplify examples * Prep to release v0.0.5 (#2126)
This commit is contained in:
+104
-52
@@ -6,13 +6,13 @@ use crate::{
|
||||
backend::{BackendExt, BlockRef, TransactionStatus},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, ExtrinsicParams, HashFor, Header},
|
||||
error::{BlockError, Error},
|
||||
error::{ExtrinsicError, TransactionStatusError},
|
||||
tx::{Payload, Signer as SignerT, TxProgress},
|
||||
utils::PhantomDataSendSync,
|
||||
};
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use derive_where::derive_where;
|
||||
use futures::future::try_join;
|
||||
use futures::future::{TryFutureExt, try_join};
|
||||
use subxt_core::tx::TransactionVersion;
|
||||
|
||||
/// A client for working with transactions.
|
||||
@@ -37,7 +37,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
/// if the call is valid (or if it's not possible to check since the call has no validation hash).
|
||||
/// Return an error if the call was not valid or something went wrong trying to validate it (ie
|
||||
/// the pallet or call in question do not exist at all).
|
||||
pub fn validate<Call>(&self, call: &Call) -> Result<(), Error>
|
||||
pub fn validate<Call>(&self, call: &Call) -> Result<(), ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -45,7 +45,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
}
|
||||
|
||||
/// Return the SCALE encoded bytes representing the call data of the transaction.
|
||||
pub fn call_data<Call>(&self, call: &Call) -> Result<Vec<u8>, Error>
|
||||
pub fn call_data<Call>(&self, call: &Call) -> Result<Vec<u8>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -55,7 +55,10 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
/// Creates an unsigned transaction without submitting it. Depending on the metadata, we might end
|
||||
/// up constructing either a v4 or v5 transaction. See [`Self::create_v4_unsigned`] or
|
||||
/// [`Self::create_v5_bare`] if you'd like to explicitly create an unsigned transaction of a certain version.
|
||||
pub fn create_unsigned<Call>(&self, call: &Call) -> Result<SubmittableTransaction<T, C>, Error>
|
||||
pub fn create_unsigned<Call>(
|
||||
&self,
|
||||
call: &Call,
|
||||
) -> Result<SubmittableTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -78,7 +81,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
pub fn create_v4_unsigned<Call>(
|
||||
&self,
|
||||
call: &Call,
|
||||
) -> Result<SubmittableTransaction<T, C>, Error>
|
||||
) -> Result<SubmittableTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -95,7 +98,10 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
///
|
||||
/// Prefer [`Self::create_unsigned()`] if you don't know which version to create; this will pick the
|
||||
/// most suitable one for the given chain.
|
||||
pub fn create_v5_bare<Call>(&self, call: &Call) -> Result<SubmittableTransaction<T, C>, Error>
|
||||
pub fn create_v5_bare<Call>(
|
||||
&self,
|
||||
call: &Call,
|
||||
) -> Result<SubmittableTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -117,7 +123,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
&self,
|
||||
call: &Call,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -148,7 +154,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
&self,
|
||||
call: &Call,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -175,7 +181,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
&self,
|
||||
call: &Call,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -198,9 +204,21 @@ where
|
||||
C: OnlineClientT<T>,
|
||||
{
|
||||
/// Get the account nonce for a given account ID.
|
||||
pub async fn account_nonce(&self, account_id: &T::AccountId) -> Result<u64, Error> {
|
||||
let block_ref = self.client.backend().latest_finalized_block_ref().await?;
|
||||
crate::blocks::get_account_nonce(&self.client, account_id, block_ref.hash()).await
|
||||
pub async fn account_nonce(&self, account_id: &T::AccountId) -> Result<u64, ExtrinsicError> {
|
||||
let block_ref = self
|
||||
.client
|
||||
.backend()
|
||||
.latest_finalized_block_ref()
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetLatestFinalizedBlock)?;
|
||||
|
||||
crate::blocks::get_account_nonce(&self.client, account_id, block_ref.hash())
|
||||
.await
|
||||
.map_err(|e| ExtrinsicError::AccountNonceError {
|
||||
block_hash: block_ref.hash().into(),
|
||||
account_id: account_id.encode().into(),
|
||||
reason: e,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a partial transaction, without submitting it. This can then be signed and submitted.
|
||||
@@ -209,7 +227,7 @@ where
|
||||
call: &Call,
|
||||
account_id: &T::AccountId,
|
||||
mut params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -226,7 +244,7 @@ where
|
||||
call: &Call,
|
||||
account_id: &T::AccountId,
|
||||
mut params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -243,7 +261,7 @@ where
|
||||
call: &Call,
|
||||
account_id: &T::AccountId,
|
||||
mut params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialTransaction<T, C>, Error>
|
||||
) -> Result<PartialTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
{
|
||||
@@ -257,7 +275,7 @@ where
|
||||
call: &Call,
|
||||
signer: &Signer,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<SubmittableTransaction<T, C>, Error>
|
||||
) -> Result<SubmittableTransaction<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
Signer: SignerT<T>,
|
||||
@@ -278,7 +296,7 @@ where
|
||||
&mut self,
|
||||
call: &Call,
|
||||
signer: &Signer,
|
||||
) -> Result<TxProgress<T, C>, Error>
|
||||
) -> Result<TxProgress<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
Signer: SignerT<T>,
|
||||
@@ -297,7 +315,7 @@ where
|
||||
call: &Call,
|
||||
signer: &Signer,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<TxProgress<T, C>, Error>
|
||||
) -> Result<TxProgress<T, C>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
Signer: SignerT<T>,
|
||||
@@ -322,7 +340,7 @@ where
|
||||
&mut self,
|
||||
call: &Call,
|
||||
signer: &Signer,
|
||||
) -> Result<HashFor<T>, Error>
|
||||
) -> Result<HashFor<T>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
Signer: SignerT<T>,
|
||||
@@ -345,7 +363,7 @@ where
|
||||
call: &Call,
|
||||
signer: &Signer,
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<HashFor<T>, Error>
|
||||
) -> Result<HashFor<T>, ExtrinsicError>
|
||||
where
|
||||
Call: Payload,
|
||||
Signer: SignerT<T>,
|
||||
@@ -485,7 +503,7 @@ where
|
||||
///
|
||||
/// Returns a [`TxProgress`], which can be used to track the status of the transaction
|
||||
/// and obtain details about it, once it has made it into a block.
|
||||
pub async fn submit_and_watch(&self) -> Result<TxProgress<T, C>, Error> {
|
||||
pub async fn submit_and_watch(&self) -> Result<TxProgress<T, C>, ExtrinsicError> {
|
||||
// Get a hash of the transaction (we'll need this later).
|
||||
let ext_hash = self.hash();
|
||||
|
||||
@@ -494,7 +512,8 @@ where
|
||||
.client
|
||||
.backend()
|
||||
.submit_transaction(self.encoded())
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ExtrinsicError::ErrorSubmittingTransaction)?;
|
||||
|
||||
Ok(TxProgress::new(sub, self.client.clone(), ext_hash))
|
||||
}
|
||||
@@ -504,13 +523,14 @@ where
|
||||
/// It's usually better to call `submit_and_watch` to get an idea of the progress of the
|
||||
/// submission and whether it's eventually successful or not. This call does not guarantee
|
||||
/// success, and is just sending the transaction to the chain.
|
||||
pub async fn submit(&self) -> Result<HashFor<T>, Error> {
|
||||
pub async fn submit(&self) -> Result<HashFor<T>, ExtrinsicError> {
|
||||
let ext_hash = self.hash();
|
||||
let mut sub = self
|
||||
.client
|
||||
.backend()
|
||||
.submit_transaction(self.encoded())
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ExtrinsicError::ErrorSubmittingTransaction)?;
|
||||
|
||||
// If we get a bad status or error back straight away then error, else return the hash.
|
||||
match sub.next().await {
|
||||
@@ -520,20 +540,22 @@ where
|
||||
| TransactionStatus::InBestBlock { .. }
|
||||
| TransactionStatus::NoLongerInBestBlock
|
||||
| TransactionStatus::InFinalizedBlock { .. } => Ok(ext_hash),
|
||||
TransactionStatus::Error { message } => {
|
||||
Err(Error::Other(format!("Transaction error: {message}")))
|
||||
}
|
||||
TransactionStatus::Error { message } => Err(
|
||||
ExtrinsicError::TransactionStatusError(TransactionStatusError::Error(message)),
|
||||
),
|
||||
TransactionStatus::Invalid { message } => {
|
||||
Err(Error::Other(format!("Transaction invalid: {message}")))
|
||||
Err(ExtrinsicError::TransactionStatusError(
|
||||
TransactionStatusError::Invalid(message),
|
||||
))
|
||||
}
|
||||
TransactionStatus::Dropped { message } => {
|
||||
Err(Error::Other(format!("Transaction dropped: {message}")))
|
||||
Err(ExtrinsicError::TransactionStatusError(
|
||||
TransactionStatusError::Dropped(message),
|
||||
))
|
||||
}
|
||||
},
|
||||
Some(Err(e)) => Err(e),
|
||||
None => Err(Error::Other(
|
||||
"Transaction broadcast was unsuccessful; stream terminated early".into(),
|
||||
)),
|
||||
Some(Err(e)) => Err(ExtrinsicError::TransactionStatusStreamError(e)),
|
||||
None => Err(ExtrinsicError::UnexpectedEndOfTransactionStatusStream),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,8 +563,13 @@ where
|
||||
/// valid can be added to a block, but may still end up in an error state.
|
||||
///
|
||||
/// Returns `Ok` with a [`ValidationResult`], which is the result of attempting to dry run the transaction.
|
||||
pub async fn validate(&self) -> Result<ValidationResult, Error> {
|
||||
let latest_block_ref = self.client.backend().latest_finalized_block_ref().await?;
|
||||
pub async fn validate(&self) -> Result<ValidationResult, ExtrinsicError> {
|
||||
let latest_block_ref = self
|
||||
.client
|
||||
.backend()
|
||||
.latest_finalized_block_ref()
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetLatestFinalizedBlock)?;
|
||||
self.validate_at(latest_block_ref).await
|
||||
}
|
||||
|
||||
@@ -553,7 +580,7 @@ where
|
||||
pub async fn validate_at(
|
||||
&self,
|
||||
at: impl Into<BlockRef<HashFor<T>>>,
|
||||
) -> Result<ValidationResult, Error> {
|
||||
) -> Result<ValidationResult, ExtrinsicError> {
|
||||
let block_hash = at.into().hash();
|
||||
|
||||
// Approach taken from https://github.com/paritytech/json-rpc-interface-spec/issues/55.
|
||||
@@ -570,17 +597,23 @@ where
|
||||
Some(¶ms),
|
||||
block_hash,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetValidationInfo)?;
|
||||
|
||||
ValidationResult::try_from_bytes(res)
|
||||
}
|
||||
|
||||
/// This returns an estimate for what the transaction is expected to cost to execute, less any tips.
|
||||
/// The actual amount paid can vary from block to block based on node traffic and other factors.
|
||||
pub async fn partial_fee_estimate(&self) -> Result<u128, Error> {
|
||||
pub async fn partial_fee_estimate(&self) -> Result<u128, ExtrinsicError> {
|
||||
let mut params = self.encoded().to_vec();
|
||||
(self.encoded().len() as u32).encode_to(&mut params);
|
||||
let latest_block_ref = self.client.backend().latest_finalized_block_ref().await?;
|
||||
let latest_block_ref = self
|
||||
.client
|
||||
.backend()
|
||||
.latest_finalized_block_ref()
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetLatestFinalizedBlock)?;
|
||||
|
||||
// destructuring RuntimeDispatchInfo, see type information <https://paritytech.github.io/substrate/master/pallet_transaction_payment_rpc_runtime_api/struct.RuntimeDispatchInfo.html>
|
||||
// data layout: {weight_ref_time: Compact<u64>, weight_proof_size: Compact<u64>, class: u8, partial_fee: u128}
|
||||
@@ -592,7 +625,9 @@ where
|
||||
Some(¶ms),
|
||||
latest_block_ref.hash(),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetFeeInfo)?;
|
||||
|
||||
Ok(partial_fee)
|
||||
}
|
||||
}
|
||||
@@ -602,19 +637,33 @@ async fn inject_account_nonce_and_block<T: Config, Client: OnlineClientT<T>>(
|
||||
client: &Client,
|
||||
account_id: &T::AccountId,
|
||||
params: &mut <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), ExtrinsicError> {
|
||||
use subxt_core::config::transaction_extensions::Params;
|
||||
|
||||
let block_ref = client.backend().latest_finalized_block_ref().await?;
|
||||
let block_ref = client
|
||||
.backend()
|
||||
.latest_finalized_block_ref()
|
||||
.await
|
||||
.map_err(ExtrinsicError::CannotGetLatestFinalizedBlock)?;
|
||||
|
||||
let (block_header, account_nonce) = try_join(
|
||||
client.backend().block_header(block_ref.hash()),
|
||||
crate::blocks::get_account_nonce(client, account_id, block_ref.hash()),
|
||||
client
|
||||
.backend()
|
||||
.block_header(block_ref.hash())
|
||||
.map_err(ExtrinsicError::CannotGetLatestFinalizedBlock),
|
||||
crate::blocks::get_account_nonce(client, account_id, block_ref.hash()).map_err(|e| {
|
||||
ExtrinsicError::AccountNonceError {
|
||||
block_hash: block_ref.hash().into(),
|
||||
account_id: account_id.encode().into(),
|
||||
reason: e,
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let block_header =
|
||||
block_header.ok_or_else(|| Error::Block(BlockError::not_found(block_ref.hash())))?;
|
||||
let block_header = block_header.ok_or_else(|| ExtrinsicError::CannotFindBlockHeader {
|
||||
block_hash: block_ref.hash().into(),
|
||||
})?;
|
||||
|
||||
params.inject_account_nonce(account_nonce);
|
||||
params.inject_block(block_header.number().into(), block_ref.hash());
|
||||
@@ -624,26 +673,29 @@ async fn inject_account_nonce_and_block<T: Config, Client: OnlineClientT<T>>(
|
||||
|
||||
impl ValidationResult {
|
||||
#[allow(clippy::get_first)]
|
||||
fn try_from_bytes(bytes: Vec<u8>) -> Result<ValidationResult, crate::Error> {
|
||||
fn try_from_bytes(bytes: Vec<u8>) -> Result<ValidationResult, ExtrinsicError> {
|
||||
// TaggedTransactionQueue_validate_transaction returns this:
|
||||
// https://github.com/paritytech/substrate/blob/0cdf7029017b70b7c83c21a4dc0aa1020e7914f6/primitives/runtime/src/transaction_validity.rs#L210
|
||||
// We copy some of the inner types and put the three states (valid, invalid, unknown) into one enum,
|
||||
// because from our perspective, the call was successful regardless.
|
||||
if bytes.get(0) == Some(&0) {
|
||||
// ok: valid. Decode but, for now we discard most of the information
|
||||
let res = TransactionValid::decode(&mut &bytes[1..])?;
|
||||
let res = TransactionValid::decode(&mut &bytes[1..])
|
||||
.map_err(ExtrinsicError::CannotDecodeValidationResult)?;
|
||||
Ok(ValidationResult::Valid(res))
|
||||
} else if bytes.get(0) == Some(&1) && bytes.get(1) == Some(&0) {
|
||||
// error: invalid
|
||||
let res = TransactionInvalid::decode(&mut &bytes[2..])?;
|
||||
let res = TransactionInvalid::decode(&mut &bytes[2..])
|
||||
.map_err(ExtrinsicError::CannotDecodeValidationResult)?;
|
||||
Ok(ValidationResult::Invalid(res))
|
||||
} else if bytes.get(0) == Some(&1) && bytes.get(1) == Some(&1) {
|
||||
// error: unknown
|
||||
let res = TransactionUnknown::decode(&mut &bytes[2..])?;
|
||||
let res = TransactionUnknown::decode(&mut &bytes[2..])
|
||||
.map_err(ExtrinsicError::CannotDecodeValidationResult)?;
|
||||
Ok(ValidationResult::Unknown(res))
|
||||
} else {
|
||||
// unable to decode the bytes; they aren't what we expect.
|
||||
Err(crate::Error::Unknown(bytes))
|
||||
Err(ExtrinsicError::UnexpectedValidationResultBytes(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
-49
@@ -10,7 +10,10 @@ use crate::{
|
||||
backend::{BlockRef, StreamOfResults, TransactionStatus as BackendTxStatus},
|
||||
client::OnlineClientT,
|
||||
config::{Config, HashFor},
|
||||
error::{DispatchError, Error, RpcError, TransactionError},
|
||||
error::{
|
||||
DispatchError, TransactionEventsError, TransactionFinalizedSuccessError,
|
||||
TransactionProgressError, TransactionStatusError,
|
||||
},
|
||||
events::EventsClient,
|
||||
utils::strip_compact_prefix,
|
||||
};
|
||||
@@ -67,7 +70,7 @@ where
|
||||
/// 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.
|
||||
pub async fn next(&mut self) -> Option<Result<TxStatus<T, C>, Error>> {
|
||||
pub async fn next(&mut self) -> Option<Result<TxStatus<T, C>, TransactionProgressError>> {
|
||||
StreamExt::next(self).await
|
||||
}
|
||||
|
||||
@@ -81,24 +84,26 @@ where
|
||||
/// probability that the transaction will not make it into a block but there is no guarantee
|
||||
/// that this is true. In those cases the stream is closed however, so you currently have no way to find
|
||||
/// out if they finally made it into a block or not.
|
||||
pub async fn wait_for_finalized(mut self) -> Result<TxInBlock<T, C>, Error> {
|
||||
pub async fn wait_for_finalized(mut self) -> Result<TxInBlock<T, C>, TransactionProgressError> {
|
||||
while let Some(status) = self.next().await {
|
||||
match status? {
|
||||
// Finalized! Return.
|
||||
TxStatus::InFinalizedBlock(s) => return Ok(s),
|
||||
// Error scenarios; return the error.
|
||||
TxStatus::Error { message } => return Err(TransactionError::Error(message).into()),
|
||||
TxStatus::Error { message } => {
|
||||
return Err(TransactionStatusError::Error(message).into());
|
||||
}
|
||||
TxStatus::Invalid { message } => {
|
||||
return Err(TransactionError::Invalid(message).into());
|
||||
return Err(TransactionStatusError::Invalid(message).into());
|
||||
}
|
||||
TxStatus::Dropped { message } => {
|
||||
return Err(TransactionError::Dropped(message).into());
|
||||
return Err(TransactionStatusError::Dropped(message).into());
|
||||
}
|
||||
// Ignore and wait for next status event:
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(RpcError::SubscriptionDropped.into())
|
||||
Err(TransactionProgressError::UnexpectedEndOfTransactionStatusStream)
|
||||
}
|
||||
|
||||
/// Wait for the transaction to be finalized, and for the transaction events to indicate
|
||||
@@ -114,14 +119,14 @@ where
|
||||
/// out if they finally made it into a block or not.
|
||||
pub async fn wait_for_finalized_success(
|
||||
self,
|
||||
) -> Result<crate::blocks::ExtrinsicEvents<T>, Error> {
|
||||
) -> Result<crate::blocks::ExtrinsicEvents<T>, TransactionFinalizedSuccessError> {
|
||||
let evs = self.wait_for_finalized().await?.wait_for_success().await?;
|
||||
Ok(evs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config, C: Clone> Stream for TxProgress<T, C> {
|
||||
type Item = Result<TxStatus<T, C>, Error>;
|
||||
type Item = Result<TxStatus<T, C>, TransactionProgressError>;
|
||||
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
@@ -132,37 +137,41 @@ impl<T: Config, C: Clone> Stream for TxProgress<T, C> {
|
||||
None => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
sub.poll_next_unpin(cx).map_ok(|status| {
|
||||
match status {
|
||||
BackendTxStatus::Validated => TxStatus::Validated,
|
||||
BackendTxStatus::Broadcasted => TxStatus::Broadcasted,
|
||||
BackendTxStatus::NoLongerInBestBlock => TxStatus::NoLongerInBestBlock,
|
||||
BackendTxStatus::InBestBlock { hash } => {
|
||||
TxStatus::InBestBlock(TxInBlock::new(hash, self.ext_hash, self.client.clone()))
|
||||
}
|
||||
// These stream events mean that nothing further will be sent:
|
||||
BackendTxStatus::InFinalizedBlock { hash } => {
|
||||
self.sub = None;
|
||||
TxStatus::InFinalizedBlock(TxInBlock::new(
|
||||
sub.poll_next_unpin(cx)
|
||||
.map_err(TransactionProgressError::CannotGetNextProgressUpdate)
|
||||
.map_ok(|status| {
|
||||
match status {
|
||||
BackendTxStatus::Validated => TxStatus::Validated,
|
||||
BackendTxStatus::Broadcasted => TxStatus::Broadcasted,
|
||||
BackendTxStatus::NoLongerInBestBlock => TxStatus::NoLongerInBestBlock,
|
||||
BackendTxStatus::InBestBlock { hash } => TxStatus::InBestBlock(TxInBlock::new(
|
||||
hash,
|
||||
self.ext_hash,
|
||||
self.client.clone(),
|
||||
))
|
||||
)),
|
||||
// These stream events mean that nothing further will be sent:
|
||||
BackendTxStatus::InFinalizedBlock { hash } => {
|
||||
self.sub = None;
|
||||
TxStatus::InFinalizedBlock(TxInBlock::new(
|
||||
hash,
|
||||
self.ext_hash,
|
||||
self.client.clone(),
|
||||
))
|
||||
}
|
||||
BackendTxStatus::Error { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Error { message }
|
||||
}
|
||||
BackendTxStatus::Invalid { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Invalid { message }
|
||||
}
|
||||
BackendTxStatus::Dropped { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Dropped { message }
|
||||
}
|
||||
}
|
||||
BackendTxStatus::Error { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Error { message }
|
||||
}
|
||||
BackendTxStatus::Invalid { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Invalid { message }
|
||||
}
|
||||
BackendTxStatus::Dropped { message } => {
|
||||
self.sub = None;
|
||||
TxStatus::Dropped { message }
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,15 +267,27 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
|
||||
///
|
||||
/// **Note:** This has to download block details from the node and decode events
|
||||
/// from them.
|
||||
pub async fn wait_for_success(&self) -> Result<crate::blocks::ExtrinsicEvents<T>, Error> {
|
||||
pub async fn wait_for_success(
|
||||
&self,
|
||||
) -> Result<crate::blocks::ExtrinsicEvents<T>, TransactionEventsError> {
|
||||
let events = self.fetch_events().await?;
|
||||
|
||||
// Try to find any errors; return the first one we encounter.
|
||||
for ev in events.iter() {
|
||||
let ev = ev?;
|
||||
for (ev_idx, ev) in events.iter().enumerate() {
|
||||
let ev = ev.map_err(|e| TransactionEventsError::CannotDecodeEventInBlock {
|
||||
event_index: ev_idx,
|
||||
block_hash: self.block_hash().into(),
|
||||
error: e,
|
||||
})?;
|
||||
|
||||
if ev.pallet_name() == "System" && ev.variant_name() == "ExtrinsicFailed" {
|
||||
let dispatch_error =
|
||||
DispatchError::decode_from(ev.field_bytes(), self.client.metadata())?;
|
||||
DispatchError::decode_from(ev.field_bytes(), self.client.metadata()).map_err(
|
||||
|e| TransactionEventsError::CannotDecodeDispatchError {
|
||||
error: e,
|
||||
bytes: ev.field_bytes().to_vec(),
|
||||
},
|
||||
)?;
|
||||
return Err(dispatch_error.into());
|
||||
}
|
||||
}
|
||||
@@ -280,15 +301,23 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
|
||||
///
|
||||
/// **Note:** This has to download block details from the node and decode events
|
||||
/// from them.
|
||||
pub async fn fetch_events(&self) -> Result<crate::blocks::ExtrinsicEvents<T>, Error> {
|
||||
pub async fn fetch_events(
|
||||
&self,
|
||||
) -> Result<crate::blocks::ExtrinsicEvents<T>, TransactionEventsError> {
|
||||
let hasher = self.client.hasher();
|
||||
|
||||
let block_body = self
|
||||
.client
|
||||
.backend()
|
||||
.block_body(self.block_ref.hash())
|
||||
.await?
|
||||
.ok_or(Error::Transaction(TransactionError::BlockNotFound))?;
|
||||
.await
|
||||
.map_err(|e| TransactionEventsError::CannotFetchBlockBody {
|
||||
block_hash: self.block_hash().into(),
|
||||
error: e,
|
||||
})?
|
||||
.ok_or_else(|| TransactionEventsError::BlockNotFound {
|
||||
block_hash: self.block_hash().into(),
|
||||
})?;
|
||||
|
||||
let extrinsic_idx = block_body
|
||||
.iter()
|
||||
@@ -302,11 +331,21 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
|
||||
})
|
||||
// If we successfully obtain the block hash we think contains our
|
||||
// extrinsic, the extrinsic should be in there somewhere..
|
||||
.ok_or(Error::Transaction(TransactionError::BlockNotFound))?;
|
||||
.ok_or_else(|| TransactionEventsError::CannotFindTransactionInBlock {
|
||||
block_hash: self.block_hash().into(),
|
||||
transaction_hash: self.ext_hash.into(),
|
||||
})?;
|
||||
|
||||
let events = EventsClient::new(self.client.clone())
|
||||
.at(self.block_ref.clone())
|
||||
.await?;
|
||||
.await
|
||||
.map_err(
|
||||
|e| TransactionEventsError::CannotFetchEventsForTransaction {
|
||||
block_hash: self.block_hash().into(),
|
||||
transaction_hash: self.ext_hash.into(),
|
||||
error: e,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(crate::blocks::ExtrinsicEvents::new(
|
||||
self.ext_hash,
|
||||
@@ -318,10 +357,11 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use subxt_core::client::RuntimeVersion;
|
||||
|
||||
use crate::{
|
||||
Error, SubstrateConfig,
|
||||
SubstrateConfig,
|
||||
backend::{StreamOfResults, TransactionStatus},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, HashFor},
|
||||
@@ -375,7 +415,7 @@ mod test {
|
||||
let finalized_result = tx_progress.wait_for_finalized().await;
|
||||
assert!(matches!(
|
||||
finalized_result,
|
||||
Err(Error::Transaction(crate::error::TransactionError::Error(e))) if e == "err"
|
||||
Err(TransactionProgressError::TransactionStatusError(TransactionStatusError::Error(e))) if e == "err"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -390,7 +430,7 @@ mod test {
|
||||
let finalized_result = tx_progress.wait_for_finalized().await;
|
||||
assert!(matches!(
|
||||
finalized_result,
|
||||
Err(Error::Transaction(crate::error::TransactionError::Invalid(e))) if e == "err"
|
||||
Err(TransactionProgressError::TransactionStatusError(TransactionStatusError::Invalid(e))) if e == "err"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -405,7 +445,7 @@ mod test {
|
||||
let finalized_result = tx_progress.wait_for_finalized().await;
|
||||
assert!(matches!(
|
||||
finalized_result,
|
||||
Err(Error::Transaction(crate::error::TransactionError::Dropped(e))) if e == "err"
|
||||
Err(TransactionProgressError::TransactionStatusError(TransactionStatusError::Dropped(e))) if e == "err"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user