From ee70fb223c947da4de127c6aafdb92d360138503 Mon Sep 17 00:00:00 2001 From: haerdib Date: Mon, 19 Feb 2024 16:11:39 +0100 Subject: [PATCH 1/3] add failed extrinsic error --- src/api/error.rs | 33 +++++++++++++++++-- src/api/rpc_api/author.rs | 14 ++++++-- .../async/examples/dispatch_errors_tests.rs | 25 +++++++++++--- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/api/error.rs b/src/api/error.rs index 589374042..c3a188e18 100644 --- a/src/api/error.rs +++ b/src/api/error.rs @@ -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 codec::{Decode, Encode}; pub type Result = core::result::Result; @@ -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. @@ -57,3 +58,29 @@ pub enum Error { /// Any custom Error. Other(Box), } + +/// Encountered unexpected tx status during watch process or the extrinsic failed. +#[derive(Debug)] +pub struct FailedExtrinsicError { + dispatch_error: DispatchError, + encoded_report: Vec, +} + +impl FailedExtrinsicError { + pub fn new(dispatch_error: DispatchError, encoded_report: Vec) -> Self { + Self { dispatch_error, encoded_report } + } + + pub fn dispatch_error(&self) -> &DispatchError { + &self.dispatch_error + } + + pub fn get_report(&self) -> Result> { + let report = Decode::decode(&mut self.encoded_report.as_slice())?; + Ok(report) + } + + pub fn encoded_report(&self) -> &[u8] { + &self.encoded_report + } +} diff --git a/src/api/rpc_api/author.rs b/src/api/rpc_api/author.rs index dac237f9a..752226967 100644 --- a/src/api/rpc_api/author.rs +++ b/src/api/rpc_api/author.rs @@ -15,6 +15,7 @@ use crate::{ api::{rpc_api::events::FetchEvents, Error, Result}, + error::FailedExtrinsicError, rpc::{HandleSubscription, Request, Subscribe}, Api, ExtrinsicReport, TransactionStatus, XtStatus, }; @@ -279,12 +280,21 @@ 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 } } + + if let Some(dispatch_error) = maybe_dispatch_error { + return Err(Error::FailedExtrinsic(FailedExtrinsicError::new( + dispatch_error, + report.encode(), + ))) + } report.events = Some(extrinsic_events.into_iter().map(|event| event.to_raw()).collect()); Ok(report) diff --git a/testing/async/examples/dispatch_errors_tests.rs b/testing/async/examples/dispatch_errors_tests.rs index 8761da431..63bd9c8fa 100644 --- a/testing/async/examples/dispatch_errors_tests.rs +++ b/testing/async/examples/dispatch_errors_tests.rs @@ -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] @@ -51,8 +52,15 @@ 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::().unwrap(); + assert!(report.block_hash.is_some()); + assert!(format!("{dispatch_error:?}").contains("BadOrigin")); + }, + _ => panic!("Expected Failed Extrinisc Error"), + } println!("[+] BadOrigin error: Bob can't force set balance"); //BelowMinimum @@ -62,7 +70,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::().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"); } From a5522ad45c7e56456b885508767a0d2b2d0a613c Mon Sep 17 00:00:00 2001 From: haerdib Date: Mon, 19 Feb 2024 16:18:53 +0100 Subject: [PATCH 2/3] fix no_std build --- src/api/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/error.rs b/src/api/error.rs index c3a188e18..80944c764 100644 --- a/src/api/error.rs +++ b/src/api/error.rs @@ -20,7 +20,7 @@ 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 = core::result::Result; From e3d107b8211ca403822e2dd195100e87ecd6f998 Mon Sep 17 00:00:00 2001 From: haerdib Date: Thu, 22 Feb 2024 11:22:49 +0100 Subject: [PATCH 3/3] ensure events are in the error report --- src/api/rpc_api/author.rs | 3 ++- testing/async/examples/dispatch_errors_tests.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/rpc_api/author.rs b/src/api/rpc_api/author.rs index 752226967..a5fcdcf25 100644 --- a/src/api/rpc_api/author.rs +++ b/src/api/rpc_api/author.rs @@ -289,13 +289,14 @@ where } } + 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(), ))) } - report.events = Some(extrinsic_events.into_iter().map(|event| event.to_raw()).collect()); Ok(report) } diff --git a/testing/async/examples/dispatch_errors_tests.rs b/testing/async/examples/dispatch_errors_tests.rs index 63bd9c8fa..0ebc516b7 100644 --- a/testing/async/examples/dispatch_errors_tests.rs +++ b/testing/async/examples/dispatch_errors_tests.rs @@ -57,6 +57,7 @@ async fn main() { let dispatch_error = extrinsic_error.dispatch_error(); let report = extrinsic_error.get_report::().unwrap(); assert!(report.block_hash.is_some()); + assert!(report.events.is_some()); assert!(format!("{dispatch_error:?}").contains("BadOrigin")); }, _ => panic!("Expected Failed Extrinisc Error"),