mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 04:11:07 +00:00
Improve Dispatch Errors (#878)
* better dispatch errors * dry_run to use same DispatchError * fix dry_run_fails; use correct transfer amount * Hide ModuleError impl and avoid pulling details from metadata unless user needs them * fix tests * actually fix the tests (hopefully..) * Add a couple more DispatchError test cases * Add a comment about where the error was copied from * Also expose a way to obtain the raw module error data * Remove redundant variant prefixes * explicit lifetime on From<str> for clarity * fmt
This commit is contained in:
@@ -444,10 +444,10 @@ impl<T: Config> Rpc<T> {
|
||||
&self,
|
||||
encoded_signed: &[u8],
|
||||
at: Option<T::Hash>,
|
||||
) -> Result<types::DryRunResult, Error> {
|
||||
) -> Result<types::DryRunResultBytes, Error> {
|
||||
let params = rpc_params![to_hex(encoded_signed), at];
|
||||
let result_bytes: types::Bytes = self.client.request("system_dryRun", params).await?;
|
||||
Ok(types::decode_dry_run_result(&mut &*result_bytes.0)?)
|
||||
Ok(types::DryRunResultBytes(result_bytes.0))
|
||||
}
|
||||
|
||||
/// Subscribe to `chainHead_unstable_follow` to obtain all reported blocks by the chain.
|
||||
|
||||
+33
-64
@@ -4,7 +4,7 @@
|
||||
|
||||
//! Types sent to/from the Substrate RPC interface.
|
||||
|
||||
use crate::Config;
|
||||
use crate::{metadata::Metadata, Config};
|
||||
use codec::{Decode, Encode};
|
||||
use primitive_types::U256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,40 +13,42 @@ use std::collections::HashMap;
|
||||
// Subscription types are returned from some calls, so expose it with the rest of the returned types.
|
||||
pub use super::rpc_client::Subscription;
|
||||
|
||||
/// Signal what the result of doing a dry run of an extrinsic is.
|
||||
pub type DryRunResult = Result<(), DryRunError>;
|
||||
|
||||
/// An error dry running an extrinsic.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum DryRunError {
|
||||
/// The extrinsic will not be included in the block
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DryRunResult {
|
||||
/// The transaction could be included in the block and executed.
|
||||
Success,
|
||||
/// The transaction could be included in the block, but the call failed to dispatch.
|
||||
DispatchError(crate::error::DispatchError),
|
||||
/// The transaction could not be included in the block.
|
||||
TransactionValidityError,
|
||||
/// The extrinsic will be included in the block, but the call failed to dispatch.
|
||||
DispatchError,
|
||||
}
|
||||
|
||||
/// dryRun returns an ApplyExtrinsicResult, which is basically a
|
||||
/// `Result<Result<(), DispatchError>, TransactionValidityError>`. We want to convert this to
|
||||
/// a [`DryRunResult`].
|
||||
///
|
||||
/// - if `Ok(inner)`, the transaction will be included in the block
|
||||
/// - if `Ok(Ok(()))`, the transaction will be included and the call will be dispatched
|
||||
/// successfully
|
||||
/// - if `Ok(Err(e))`, the transaction will be included but there is some error dispatching
|
||||
/// the call to the module.
|
||||
///
|
||||
/// The errors get a bit involved and have been known to change over time. At the moment
|
||||
/// then, we will keep things simple here and just decode the Result portion (ie the initial bytes)
|
||||
/// and ignore the rest.
|
||||
pub(crate) fn decode_dry_run_result<I: codec::Input>(
|
||||
input: &mut I,
|
||||
) -> Result<DryRunResult, codec::Error> {
|
||||
let res = match <Result<Result<(), ()>, ()>>::decode(input)? {
|
||||
Ok(Ok(())) => Ok(()),
|
||||
Ok(Err(())) => Err(DryRunError::DispatchError),
|
||||
Err(()) => Err(DryRunError::TransactionValidityError),
|
||||
};
|
||||
Ok(res)
|
||||
/// The bytes representing an error dry running an extrinsic.
|
||||
pub struct DryRunResultBytes(pub Vec<u8>);
|
||||
|
||||
impl DryRunResultBytes {
|
||||
/// Attempt to decode the error bytes into a [`DryRunResult`] using the provided [`Metadata`].
|
||||
pub fn into_dry_run_result(self, metadata: &Metadata) -> Result<DryRunResult, crate::Error> {
|
||||
// dryRun returns an ApplyExtrinsicResult, which is basically a
|
||||
// `Result<Result<(), DispatchError>, TransactionValidityError>`.
|
||||
let bytes = self.0;
|
||||
if bytes[0] == 0 && bytes[1] == 0 {
|
||||
// Ok(Ok(())); transaction is valid and executed ok
|
||||
Ok(DryRunResult::Success)
|
||||
} else if bytes[0] == 0 && bytes[1] == 1 {
|
||||
// Ok(Err(dispatch_error)); transaction is valid but execution failed
|
||||
let dispatch_error =
|
||||
crate::error::DispatchError::decode_from(&bytes[2..], metadata.clone())?;
|
||||
Ok(DryRunResult::DispatchError(dispatch_error))
|
||||
} else if bytes[0] == 1 {
|
||||
// Err(transaction_error); some transaction validity error (we ignore the details at the moment)
|
||||
Ok(DryRunResult::TransactionValidityError)
|
||||
} else {
|
||||
// unable to decode the bytes; they aren't what we expect.
|
||||
Err(crate::Error::Unknown(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A number type that can be serialized both as a number or a string that encodes a number in a
|
||||
@@ -817,39 +819,6 @@ mod test {
|
||||
assert_deser(r#"1000000000000"#, NumberOrHex::Number(1000000000000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dry_run_result_is_substrate_compatible() {
|
||||
use sp_runtime::{
|
||||
transaction_validity::{
|
||||
InvalidTransaction as SpInvalidTransaction,
|
||||
TransactionValidityError as SpTransactionValidityError,
|
||||
},
|
||||
ApplyExtrinsicResult as SpApplyExtrinsicResult, DispatchError as SpDispatchError,
|
||||
};
|
||||
|
||||
let pairs = vec![
|
||||
// All ok
|
||||
(SpApplyExtrinsicResult::Ok(Ok(())), Ok(())),
|
||||
// Some transaction error
|
||||
(
|
||||
SpApplyExtrinsicResult::Err(SpTransactionValidityError::Invalid(
|
||||
SpInvalidTransaction::BadProof,
|
||||
)),
|
||||
Err(DryRunError::TransactionValidityError),
|
||||
),
|
||||
// Some dispatch error
|
||||
(
|
||||
SpApplyExtrinsicResult::Ok(Err(SpDispatchError::BadOrigin)),
|
||||
Err(DryRunError::DispatchError),
|
||||
),
|
||||
];
|
||||
|
||||
for (actual, expected) in pairs {
|
||||
let encoded = actual.encode();
|
||||
assert_eq!(decode_dry_run_result(&mut &*encoded).unwrap(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn justification_is_substrate_compatible() {
|
||||
use sp_runtime::Justification as SpJustification;
|
||||
|
||||
Reference in New Issue
Block a user