substrate_api_client/api/
mod.rsuse crate::error::FailedExtrinsicError;
use ac_node_api::{events::RawEventDetails, EventDetails, Metadata};
use alloc::{string::String, vec::Vec};
use codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
use sp_core::Bytes;
pub use api_client::Api;
pub use error::{Error, Result};
pub use rpc_api::{
FetchEvents, GetAccountInformation, GetBalance, GetChainInfo, GetStorage,
GetTransactionPayment, SubmitAndWatch, SubmitExtrinsic, SubscribeChain, SubscribeEvents,
SystemApi,
};
pub mod api_client;
pub mod error;
pub mod rpc_api;
pub mod runtime_api;
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub struct ExtrinsicReport<Hash: Encode + Decode> {
pub extrinsic_hash: Hash,
pub block_hash: Option<Hash>,
pub status: TransactionStatus<Hash, Hash>,
pub events: Option<Vec<RawEventDetails<Hash>>>,
}
impl<Hash: Encode + Decode> ExtrinsicReport<Hash> {
pub fn new(
extrinsic_hash: Hash,
block_hash: Option<Hash>,
status: TransactionStatus<Hash, Hash>,
events: Option<Vec<RawEventDetails<Hash>>>,
) -> Self {
Self { extrinsic_hash, block_hash, status, events }
}
pub fn add_events(&mut self, events: Vec<EventDetails<Hash>>) {
self.events = Some(events.into_iter().map(|event| event.to_raw()).collect());
}
pub fn check_events_for_dispatch_error(&self, metadata: &Metadata) -> Result<()> {
if self.events.is_none() {
return Err(Error::EventsMissing)
}
let events = self.events.as_ref().unwrap();
for event in events {
if let Some(dispatch_error) = event.get_associated_dispatch_error(metadata) {
return Err(Error::FailedExtrinsic(FailedExtrinsicError::new(
dispatch_error,
self.encode(),
)))
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, PartialOrd, Eq, Copy, Clone)]
pub enum XtStatus {
Ready = 1,
Broadcast = 2,
InBlock = 3,
Retracted = 4,
Finalized = 6,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum UnexpectedTxStatus {
Future,
FinalityTimeout,
Usurped,
Dropped,
Invalid,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "camelCase")]
pub enum TransactionStatus<Hash: Encode + Decode, BlockHash: Encode + Decode> {
Future,
Ready,
Broadcast(Vec<String>),
InBlock(BlockHash),
Retracted(BlockHash),
FinalityTimeout(BlockHash),
Finalized(BlockHash),
Usurped(Hash),
Dropped,
Invalid,
}
impl<Hash: Encode + Decode, BlockHash: Encode + Decode> TransactionStatus<Hash, BlockHash> {
pub fn as_u8(&self) -> u8 {
match self {
Self::Future => 0,
Self::Ready => 1,
Self::Broadcast(_) => 2,
Self::InBlock(_) => 3,
Self::Retracted(_) => 4,
Self::FinalityTimeout(_) => 5,
Self::Finalized(_) => 6,
Self::Usurped(_) => 7,
Self::Dropped => 8,
Self::Invalid => 9,
}
}
pub fn is_expected(&self) -> Result<()> {
match self {
Self::Ready
| Self::Broadcast(_)
| Self::InBlock(_)
| Self::Retracted(_)
| Self::Finalized(_) => Ok(()),
Self::Future => Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Future)),
Self::FinalityTimeout(_) =>
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::FinalityTimeout)),
Self::Usurped(_) => Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Usurped)),
Self::Dropped => Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Dropped)),
Self::Invalid => Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Invalid)),
}
}
pub fn reached_status(&self, status: XtStatus) -> bool {
self.as_u8() >= status as u8
}
pub fn get_maybe_block_hash(&self) -> Option<&BlockHash> {
match self {
Self::InBlock(block_hash) => Some(block_hash),
Self::Retracted(block_hash) => Some(block_hash),
Self::FinalityTimeout(block_hash) => Some(block_hash),
Self::Finalized(block_hash) => Some(block_hash),
_ => None,
}
}
pub fn is_final(&self) -> bool {
matches!(
self,
Self::Usurped(_)
| Self::Finalized(_)
| Self::FinalityTimeout(_)
| Self::Invalid
| Self::Dropped
)
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadProof<Hash> {
pub at: Hash,
pub proof: Vec<Bytes>,
}
#[cfg(test)]
mod tests {
use super::{TransactionStatus as GenericTransactionStatus, *};
use sp_core::H256;
type TransactionStatus = GenericTransactionStatus<H256, H256>;
#[test]
fn test_xt_status_as_u8() {
assert_eq!(1, XtStatus::Ready as u8);
assert_eq!(2, XtStatus::Broadcast as u8);
assert_eq!(3, XtStatus::InBlock as u8);
assert_eq!(6, XtStatus::Finalized as u8);
}
#[test]
fn test_transaction_status_as_u8() {
assert_eq!(0, TransactionStatus::Future.as_u8());
assert_eq!(1, TransactionStatus::Ready.as_u8());
assert_eq!(2, TransactionStatus::Broadcast(vec![]).as_u8());
assert_eq!(3, TransactionStatus::InBlock(H256::random()).as_u8());
assert_eq!(4, TransactionStatus::Retracted(H256::random()).as_u8());
assert_eq!(5, TransactionStatus::FinalityTimeout(H256::random()).as_u8());
assert_eq!(6, TransactionStatus::Finalized(H256::random()).as_u8());
assert_eq!(7, TransactionStatus::Usurped(H256::random()).as_u8());
assert_eq!(8, TransactionStatus::Dropped.as_u8());
assert_eq!(9, TransactionStatus::Invalid.as_u8());
}
#[test]
fn test_transaction_status_is_expected() {
assert!(TransactionStatus::Ready.is_expected().is_ok());
assert!(TransactionStatus::Broadcast(vec![]).is_expected().is_ok());
assert!(TransactionStatus::InBlock(H256::random()).is_expected().is_ok());
assert!(TransactionStatus::Retracted(H256::random()).is_expected().is_ok());
assert!(TransactionStatus::Finalized(H256::random()).is_expected().is_ok());
assert!(TransactionStatus::Future.is_expected().is_err());
assert!(TransactionStatus::FinalityTimeout(H256::random()).is_expected().is_err());
assert!(TransactionStatus::Usurped(H256::random()).is_expected().is_err());
assert!(TransactionStatus::Dropped.is_expected().is_err());
assert!(TransactionStatus::Invalid.is_expected().is_err());
}
#[test]
fn test_reached_xt_status_for_ready() {
let status = XtStatus::Ready;
assert!(!TransactionStatus::Future.reached_status(status));
assert!(TransactionStatus::Ready.reached_status(status));
assert!(TransactionStatus::Broadcast(vec![]).reached_status(status));
assert!(TransactionStatus::InBlock(H256::random()).reached_status(status));
assert!(TransactionStatus::FinalityTimeout(H256::random()).reached_status(status));
assert!(TransactionStatus::Finalized(H256::random()).reached_status(status));
assert!(TransactionStatus::Retracted(H256::random()).reached_status(status));
assert!(TransactionStatus::Usurped(H256::random()).reached_status(status));
assert!(TransactionStatus::Dropped.reached_status(status));
assert!(TransactionStatus::Invalid.reached_status(status));
}
#[test]
fn test_reached_xt_status_for_broadcast() {
let status = XtStatus::Broadcast;
assert!(!TransactionStatus::Future.reached_status(status));
assert!(!TransactionStatus::Ready.reached_status(status));
assert!(TransactionStatus::Broadcast(vec![]).reached_status(status));
assert!(TransactionStatus::InBlock(H256::random()).reached_status(status));
assert!(TransactionStatus::FinalityTimeout(H256::random()).reached_status(status));
assert!(TransactionStatus::Finalized(H256::random()).reached_status(status));
assert!(TransactionStatus::Retracted(H256::random()).reached_status(status));
assert!(TransactionStatus::Usurped(H256::random()).reached_status(status));
assert!(TransactionStatus::Dropped.reached_status(status));
assert!(TransactionStatus::Invalid.reached_status(status));
}
#[test]
fn test_reached_xt_status_for_in_block() {
let status = XtStatus::InBlock;
assert!(!TransactionStatus::Future.reached_status(status));
assert!(!TransactionStatus::Ready.reached_status(status));
assert!(!TransactionStatus::Broadcast(vec![]).reached_status(status));
assert!(TransactionStatus::InBlock(H256::random()).reached_status(status));
assert!(TransactionStatus::FinalityTimeout(H256::random()).reached_status(status));
assert!(TransactionStatus::Finalized(H256::random()).reached_status(status));
assert!(TransactionStatus::Retracted(H256::random()).reached_status(status));
assert!(TransactionStatus::Usurped(H256::random()).reached_status(status));
assert!(TransactionStatus::Dropped.reached_status(status));
assert!(TransactionStatus::Invalid.reached_status(status));
}
#[test]
fn test_reached_xt_status_for_finalized() {
let status = XtStatus::Finalized;
assert!(!TransactionStatus::Future.reached_status(status));
assert!(!TransactionStatus::Ready.reached_status(status));
assert!(!TransactionStatus::Broadcast(vec![]).reached_status(status));
assert!(!TransactionStatus::InBlock(H256::random()).reached_status(status));
assert!(!TransactionStatus::Retracted(H256::random()).reached_status(status));
assert!(!TransactionStatus::FinalityTimeout(H256::random()).reached_status(status));
assert!(TransactionStatus::Finalized(H256::random()).reached_status(status));
assert!(TransactionStatus::Usurped(H256::random()).reached_status(status));
assert!(TransactionStatus::Dropped.reached_status(status));
assert!(TransactionStatus::Invalid.reached_status(status));
}
#[test]
fn encode_decode_extrinsic_report() {
let hash = H256::random();
let block_hash = H256::random();
let status = TransactionStatus::InBlock(block_hash.clone());
let report = ExtrinsicReport::new(hash, Some(block_hash), status, None);
let encoded = report.encode();
let decoded = ExtrinsicReport::<H256>::decode(&mut encoded.as_slice()).unwrap();
assert_eq!(report, decoded);
}
}