use crate::{
client::{secret::SecretManage, Error as ClientError},
types::{
api::core::response::LedgerInclusionState,
block::{
payload::{transaction::TransactionId, Payload},
Block, BlockId,
},
},
wallet::{
account::{types::InclusionState, Account},
Error,
},
};
const DEFAULT_RETRY_UNTIL_INCLUDED_INTERVAL: u64 = 1;
const DEFAULT_RETRY_UNTIL_INCLUDED_MAX_AMOUNT: u64 = 40;
impl<S: 'static + SecretManage> Account<S>
where
Error: From<S::Error>,
{
pub async fn retry_until_included(
&self,
block_id: &BlockId,
interval: Option<u64>,
max_attempts: Option<u64>,
) -> crate::wallet::Result<Vec<(BlockId, Block)>> {
Ok(self
.client()
.retry_until_included(block_id, interval, max_attempts)
.await?)
}
pub async fn retry_transaction_until_included(
&self,
transaction_id: &TransactionId,
interval: Option<u64>,
max_attempts: Option<u64>,
) -> crate::wallet::Result<BlockId> {
log::debug!("[retry_transaction_until_included]");
let transaction = self.details().await.transactions.get(transaction_id).cloned();
if let Some(transaction) = transaction {
if transaction.inclusion_state == InclusionState::Confirmed {
return transaction.block_id.ok_or(Error::MissingParameter("block id"));
}
if transaction.inclusion_state == InclusionState::Conflicting
|| transaction.inclusion_state == InclusionState::UnknownPruned
{
return Err(ClientError::TangleInclusion(format!(
"transaction id: {} inclusion state: {:?}",
transaction_id, transaction.inclusion_state
))
.into());
}
let block_id = match transaction.block_id {
Some(block_id) => block_id,
None => self
.client()
.build_block()
.finish_block(Some(Payload::Transaction(Box::new(transaction.payload.clone()))))
.await?
.id(),
};
let mut block_ids = vec![block_id];
for _ in 0..max_attempts.unwrap_or(DEFAULT_RETRY_UNTIL_INCLUDED_MAX_AMOUNT) {
let duration =
std::time::Duration::from_secs(interval.unwrap_or(DEFAULT_RETRY_UNTIL_INCLUDED_INTERVAL));
#[cfg(target_family = "wasm")]
gloo_timers::future::TimeoutFuture::new(duration.as_millis() as u32).await;
#[cfg(not(target_family = "wasm"))]
tokio::time::sleep(duration).await;
let block_ids_len = block_ids.len();
let mut conflicting = false;
for (index, block_id_) in block_ids.clone().iter().enumerate() {
let block_metadata = self.client().get_block_metadata(block_id_).await?;
if let Some(inclusion_state) = block_metadata.ledger_inclusion_state {
match inclusion_state {
LedgerInclusionState::Included | LedgerInclusionState::NoTransaction => {
return Ok(*block_id_);
}
LedgerInclusionState::Conflicting => conflicting = true,
};
}
if index == block_ids_len - 1 {
if block_metadata.should_promote.unwrap_or(false) {
self.client().promote_unchecked(block_ids.last().unwrap()).await?;
} else if block_metadata.should_reattach.unwrap_or(false) {
let reattached_block = self
.client()
.build_block()
.finish_block(Some(Payload::Transaction(Box::new(transaction.payload.clone()))))
.await?;
block_ids.push(reattached_block.id());
}
}
}
if conflicting {
let included_block = self.client().get_included_block(transaction_id).await.map_err(|e| {
if matches!(e, ClientError::Node(crate::client::node_api::error::Error::NotFound(_))) {
ClientError::TangleInclusion(block_id.to_string())
} else {
e
}
})?;
return Ok(included_block.id());
}
}
Err(ClientError::TangleInclusion(block_id.to_string()).into())
} else {
Err(Error::TransactionNotFound(*transaction_id))
}
}
}