Skip to content

Commit

Permalink
Add failed extrinsic error (#725)
Browse files Browse the repository at this point in the history
* add failed extrinsic error

* fix no_std build

* ensure events are in the error report
  • Loading branch information
haerdib authored Feb 22, 2024
1 parent 763164c commit 303eaa9
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 11 deletions.
35 changes: 31 additions & 4 deletions src/api/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
*/

use crate::{api::UnexpectedTxStatus, rpc::Error as RpcClientError};
use crate::{api::UnexpectedTxStatus, rpc::Error as RpcClientError, ExtrinsicReport};
use ac_node_api::{
error::DispatchError,
metadata::{MetadataConversionError, MetadataError},
};
use alloc::boxed::Box;
use alloc::{boxed::Box, vec::Vec};
use codec::{Decode, Encode};

pub type Result<T> = core::result::Result<T, Error>;

Expand All @@ -42,8 +43,8 @@ pub enum Error {
Codec(codec::Error),
/// Could not convert NumberOrHex with try_from.
TryFromIntError,
/// Node Api Dispatch Error.
Dispatch(DispatchError),
/// Extrinsic failed onchain. Contains the encoded report and the associated dispatch error.
FailedExtrinsic(FailedExtrinsicError),
/// Encountered unexpected tx status during watch process.
UnexpectedTxStatus(UnexpectedTxStatus),
/// Could not send update because the Stream has been closed unexpectedly.
Expand All @@ -57,3 +58,29 @@ pub enum Error {
/// Any custom Error.
Other(Box<dyn core::error::Error + Send + Sync + 'static>),
}

/// Encountered unexpected tx status during watch process or the extrinsic failed.
#[derive(Debug)]
pub struct FailedExtrinsicError {
dispatch_error: DispatchError,
encoded_report: Vec<u8>,
}

impl FailedExtrinsicError {
pub fn new(dispatch_error: DispatchError, encoded_report: Vec<u8>) -> Self {
Self { dispatch_error, encoded_report }
}

pub fn dispatch_error(&self) -> &DispatchError {
&self.dispatch_error
}

pub fn get_report<Hash: Encode + Decode>(&self) -> Result<ExtrinsicReport<Hash>> {
let report = Decode::decode(&mut self.encoded_report.as_slice())?;
Ok(report)
}

pub fn encoded_report(&self) -> &[u8] {
&self.encoded_report
}
}
15 changes: 13 additions & 2 deletions src/api/rpc_api/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use crate::{
api::{rpc_api::events::FetchEvents, Error, Result},
error::FailedExtrinsicError,
rpc::{HandleSubscription, Request, Subscribe},
Api, ExtrinsicReport, TransactionStatus, XtStatus,
};
Expand Down Expand Up @@ -279,14 +280,24 @@ where
let extrinsic_events =
self.fetch_events_for_extrinsic(block_hash, report.extrinsic_hash).await?;

// Ensure the extrinsic was successful. If not, return an error.
// Check if the extrinsic was succesfull or not.
let mut maybe_dispatch_error = None;
for event in &extrinsic_events {
if let Some(dispatch_error) = event.get_associated_dispatch_error() {
return Err(Error::Dispatch(dispatch_error))
maybe_dispatch_error = Some(dispatch_error);
break
}
}

report.events = Some(extrinsic_events.into_iter().map(|event| event.to_raw()).collect());

if let Some(dispatch_error) = maybe_dispatch_error {
return Err(Error::FailedExtrinsic(FailedExtrinsicError::new(
dispatch_error,
report.encode(),
)))
}

Ok(report)
}

Expand Down
26 changes: 21 additions & 5 deletions testing/async/examples/dispatch_errors_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@

//! Tests for the dispatch error.

use sp_core::H256;
use sp_keyring::AccountKeyring;
use sp_runtime::MultiAddress;
use substrate_api_client::{
ac_primitives::AssetRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api,
GetAccountInformation, SubmitAndWatch, XtStatus,
Error, GetAccountInformation, SubmitAndWatch, XtStatus,
};

#[tokio::main]
Expand Down Expand Up @@ -51,8 +52,16 @@ async fn main() {
.unwrap();

let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).await;
assert!(result.is_err());
assert!(format!("{result:?}").contains("BadOrigin"));
match result {
Err(Error::FailedExtrinsic(extrinsic_error)) => {
let dispatch_error = extrinsic_error.dispatch_error();
let report = extrinsic_error.get_report::<H256>().unwrap();
assert!(report.block_hash.is_some());
assert!(report.events.is_some());
assert!(format!("{dispatch_error:?}").contains("BadOrigin"));
},
_ => panic!("Expected Failed Extrinisc Error"),
}
println!("[+] BadOrigin error: Bob can't force set balance");

//BelowMinimum
Expand All @@ -62,7 +71,14 @@ async fn main() {
.await
.unwrap();
let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).await;
assert!(result.is_err());
assert!(format!("{result:?}").contains("(BelowMinimum"));
match result {
Err(Error::FailedExtrinsic(extrinsic_error)) => {
let dispatch_error = extrinsic_error.dispatch_error();
let report = extrinsic_error.get_report::<H256>().unwrap();
assert!(report.block_hash.is_some());
assert!(format!("{dispatch_error:?}").contains("BelowMinimum"));
},
_ => panic!("Expected Failed Extrinisc Error"),
}
println!("[+] BelowMinimum error: balance (999999) is below the existential deposit");
}

0 comments on commit 303eaa9

Please sign in to comment.