Skip to content

Commit bf42f66

Browse files
authored
Clean up Api Error and indicate future status clearly (#424)
* remove finalitytimeout from is_valid rename is_supported to is_valid rename is_valid to is_expected some more clean up add unexpected Tx status enum add some comment * clean up traits
1 parent 74993c6 commit bf42f66

File tree

5 files changed

+91
-58
lines changed

5 files changed

+91
-58
lines changed

examples/examples/custom_nonce.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall};
2020
use sp_keyring::AccountKeyring;
2121
use sp_runtime::{generic::Era, MultiAddress};
2222
use substrate_api_client::{
23-
rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, GenericAdditionalParams, GetHeader,
24-
SubmitAndWatch, XtStatus,
23+
rpc::JsonrpseeClient, Api, AssetTipExtrinsicParams, Error, GenericAdditionalParams, GetHeader,
24+
SubmitAndWatch, UnexpectedTxStatus, XtStatus,
2525
};
2626

2727
#[tokio::main]
@@ -58,11 +58,12 @@ async fn main() {
5858
println!("[+] Composed Extrinsic:\n {:?}\n", xt);
5959

6060
// Send and watch extrinsic until InBlock.
61-
match api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) {
62-
Err(error) => {
63-
println!("Retrieved error {:?}", error);
64-
assert!(format!("{:?}", error).contains("Future"));
61+
let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock);
62+
println!("Returned Result {:?}", result);
63+
match result {
64+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Future)) => {
65+
// All good, we expected a Future Error.
6566
},
66-
_ => panic!("Expected an error upon a future extrinsic"),
67+
_ => panic!("Expected a future error"),
6768
}
6869
}

node-api/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use alloc::{borrow::ToOwned, vec::Vec};
2121
use codec::{Decode, Encode};
2222

2323
pub use decoder::*;
24-
pub use error::*;
2524
pub use events::*;
2625
pub use metadata::*;
2726
pub use storage::*;

src/api/error.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,45 @@
1515
1616
*/
1717

18-
use crate::{api::XtStatus, rpc::Error as RpcClientError};
18+
use crate::{api::UnexpectedTxStatus, rpc::Error as RpcClientError};
1919
use ac_node_api::{
20+
error::DispatchError,
2021
metadata::{InvalidMetadataError, MetadataError},
21-
DispatchError,
2222
};
23-
use alloc::{boxed::Box, string::String};
23+
use alloc::boxed::Box;
2424

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

2727
#[derive(Debug, derive_more::From)]
2828
pub enum Error {
29+
/// Could not fetch the genesis hash from node.
2930
FetchGenesisHash,
31+
/// Expected a signer, but none is assigned.
3032
NoSigner,
33+
/// Rpc Client Error.
3134
RpcClient(RpcClientError),
35+
/// Metadata Error.
3236
Metadata(MetadataError),
37+
/// Invalid Metadata Error.
3338
InvalidMetadata(InvalidMetadataError),
39+
/// Node Api Error.
3440
NodeApi(ac_node_api::error::Error),
35-
StorageValueDecode(codec::Error),
36-
UnsupportedXtStatus(XtStatus),
41+
/// Encode / Decode Error.
42+
Codec(codec::Error),
43+
/// Could not convert NumberOrHex with try_from.
3744
TryFromIntError,
45+
/// Node Api Dispatch Error.
3846
Dispatch(DispatchError),
39-
Extrinsic(String),
47+
/// Encountered unexpected tx status during watch process.
48+
UnexpectedTxStatus(UnexpectedTxStatus),
49+
/// Could not send update because the Stream has been closed unexpectedly.
4050
NoStream,
41-
NoBlockHash,
42-
NoBlock,
51+
/// Could not find the expected extrinsic.
52+
ExtrinsicNotFound,
53+
/// Could not find the expected block hash.
54+
BlockHashNotFound,
55+
/// Could not find the expected block.
56+
BlockNotFound,
57+
/// Any custom Error.
4358
Other(Box<dyn core::error::Error + Send + Sync + 'static>),
4459
}

src/api/mod.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ pub enum XtStatus {
6767
Finalized = 6,
6868
}
6969

70+
/// TxStatus that is not expected during the watch process. Will be returned
71+
/// as unexpected error if encountered due to the potential danger of endless loops.
72+
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
73+
pub enum UnexpectedTxStatus {
74+
Future,
75+
Retracted,
76+
FinalityTimeout,
77+
Usurped,
78+
Dropped,
79+
Invalid,
80+
}
81+
7082
/// Possible transaction status events.
7183
// Copied from `sc-transaction-pool`
7284
// (https://github.com/paritytech/substrate/blob/dddfed3d9260cf03244f15ba3db4edf9af7467e9/client/transaction-pool/api/src/lib.rs)
@@ -114,15 +126,24 @@ impl<Hash, BlockHash> TransactionStatus<Hash, BlockHash> {
114126
}
115127
}
116128

117-
pub fn is_supported(&self) -> bool {
118-
matches!(
119-
self,
129+
pub fn is_expected(&self) -> Result<()> {
130+
match self {
120131
TransactionStatus::Ready
121-
| TransactionStatus::Broadcast(_)
122-
| TransactionStatus::InBlock(_)
123-
| TransactionStatus::FinalityTimeout(_)
124-
| TransactionStatus::Finalized(_)
125-
)
132+
| TransactionStatus::Broadcast(_)
133+
| TransactionStatus::InBlock(_)
134+
| TransactionStatus::Finalized(_) => Ok(()),
135+
TransactionStatus::Future => Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Future)),
136+
TransactionStatus::Retracted(_) =>
137+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Retracted)),
138+
TransactionStatus::FinalityTimeout(_) =>
139+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::FinalityTimeout)),
140+
TransactionStatus::Usurped(_) =>
141+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Usurped)),
142+
TransactionStatus::Dropped =>
143+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Dropped)),
144+
TransactionStatus::Invalid =>
145+
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Invalid)),
146+
}
126147
}
127148

128149
/// Returns true if the input status has been reached (or overreached)
@@ -184,20 +205,20 @@ mod tests {
184205
}
185206

186207
#[test]
187-
fn test_transaction_status_is_supported() {
208+
fn test_transaction_status_is_expected() {
188209
// Supported.
189-
assert!(TransactionStatus::Ready.is_supported());
190-
assert!(TransactionStatus::Broadcast(vec![]).is_supported());
191-
assert!(TransactionStatus::InBlock(H256::random()).is_supported());
192-
assert!(TransactionStatus::FinalityTimeout(H256::random()).is_supported());
193-
assert!(TransactionStatus::Finalized(H256::random()).is_supported());
210+
assert!(TransactionStatus::Ready.is_expected().is_ok());
211+
assert!(TransactionStatus::Broadcast(vec![]).is_expected().is_ok());
212+
assert!(TransactionStatus::InBlock(H256::random()).is_expected().is_ok());
213+
assert!(TransactionStatus::Finalized(H256::random()).is_expected().is_ok());
194214

195215
// Not supported.
196-
assert!(!TransactionStatus::Future.is_supported());
197-
assert!(!TransactionStatus::Retracted(H256::random()).is_supported());
198-
assert!(!TransactionStatus::Usurped(H256::random()).is_supported());
199-
assert!(!TransactionStatus::Dropped.is_supported());
200-
assert!(!TransactionStatus::Invalid.is_supported());
216+
assert!(TransactionStatus::Future.is_expected().is_err());
217+
assert!(TransactionStatus::Retracted(H256::random()).is_expected().is_err());
218+
assert!(TransactionStatus::FinalityTimeout(H256::random()).is_expected().is_err());
219+
assert!(TransactionStatus::Usurped(H256::random()).is_expected().is_err());
220+
assert!(TransactionStatus::Dropped.is_expected().is_err());
221+
assert!(TransactionStatus::Invalid.is_expected().is_err());
201222
}
202223

203224
#[test]

src/api/rpc_api/author.rs

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
use ac_compose_macros::rpc_params;
2323
use ac_node_api::EventDetails;
2424
use ac_primitives::{ExtrinsicParams, FrameSystemConfig};
25-
use alloc::{format, string::ToString, vec::Vec};
25+
use alloc::vec::Vec;
2626
use codec::Encode;
2727
use log::*;
2828
use serde::de::DeserializeOwned;
@@ -208,24 +208,22 @@ where
208208

209209
while let Some(transaction_status) = subscription.next() {
210210
let transaction_status = transaction_status?;
211-
if transaction_status.is_supported() {
212-
if transaction_status.reached_status(watch_until) {
211+
match transaction_status.is_expected() {
212+
Ok(_) =>
213+
if transaction_status.reached_status(watch_until) {
214+
subscription.unsubscribe()?;
215+
let block_hash = transaction_status.get_maybe_block_hash();
216+
return Ok(ExtrinsicReport::new(
217+
tx_hash,
218+
block_hash.copied(),
219+
transaction_status,
220+
None,
221+
))
222+
},
223+
Err(e) => {
213224
subscription.unsubscribe()?;
214-
let block_hash = transaction_status.get_maybe_block_hash();
215-
return Ok(ExtrinsicReport::new(
216-
tx_hash,
217-
block_hash.copied(),
218-
transaction_status,
219-
None,
220-
))
221-
}
222-
} else {
223-
subscription.unsubscribe()?;
224-
let error = Error::Extrinsic(format!(
225-
"Unsupported transaction status: {transaction_status:?}, stopping watch process."
226-
227-
));
228-
return Err(error)
225+
return Err(e)
226+
},
229227
}
230228
}
231229
Err(Error::NoStream)
@@ -237,7 +235,6 @@ impl<Signer, Client, Params, Runtime> SubmitAndWatchUntilSuccess<Client, Runtime
237235
where
238236
Client: Subscribe + Request,
239237
Params: ExtrinsicParams<Runtime::Index, Runtime::Hash>,
240-
Runtime::Hashing: HashTrait<Output = Runtime::Hash>,
241238
Runtime: FrameSystemConfig + GetRuntimeBlockType,
242239
Runtime::RuntimeBlock: BlockTrait + DeserializeOwned,
243240
Runtime::Hashing: HashTrait<Output = Runtime::Hash>,
@@ -267,7 +264,7 @@ where
267264
let mut report =
268265
self.submit_and_watch_opaque_extrinsic_until(encoded_extrinsic, xt_status)?;
269266

270-
let block_hash = report.block_hash.ok_or(Error::NoBlockHash)?;
267+
let block_hash = report.block_hash.ok_or(Error::BlockHashNotFound)?;
271268
let extrinsic_index =
272269
self.retrieve_extrinsic_index_from_block(block_hash, report.extrinsic_hash)?;
273270
let block_events = self.fetch_events_from_block(block_hash)?;
@@ -293,7 +290,7 @@ where
293290
block_hash: Runtime::Hash,
294291
extrinsic_hash: Runtime::Hash,
295292
) -> Result<u32> {
296-
let block = self.get_block(Some(block_hash))?.ok_or(Error::NoBlock)?;
293+
let block = self.get_block(Some(block_hash))?.ok_or(Error::BlockNotFound)?;
297294
let xt_index = block
298295
.extrinsics()
299296
.iter()
@@ -302,7 +299,7 @@ where
302299
trace!("Looking for: {:?}, got xt_hash {:?}", extrinsic_hash, xt_hash);
303300
extrinsic_hash == xt_hash
304301
})
305-
.ok_or(Error::Extrinsic("Could not find extrinsic hash".to_string()))?;
302+
.ok_or(Error::ExtrinsicNotFound)?;
306303
Ok(xt_index as u32)
307304
}
308305

@@ -311,7 +308,7 @@ where
311308
let key = utils::storage_key("System", "Events");
312309
let event_bytes = self
313310
.get_opaque_storage_by_key_hash(key, Some(block_hash))?
314-
.ok_or(Error::NoBlock)?;
311+
.ok_or(Error::BlockNotFound)?;
315312
let events =
316313
Events::<Runtime::Hash>::new(self.metadata().clone(), Default::default(), event_bytes);
317314
Ok(events)

0 commit comments

Comments
 (0)