use std::sync::Arc;
use std::time::Duration;
use algonaut_core::TransactionId;
use algonaut_model::algod::PendingTransactionResponse;
use algonaut_transaction::{
SignedTransaction, Signer, SigningRequest, Transaction, signed_transaction,
};
use instant::Instant;
use crate::{Error, algod::v2::Algod};
use super::TransactionWithSigner;
const COMPOSER_CONFIRM_TIMEOUT: Duration = Duration::from_secs(60);
pub(super) async fn poll_until_confirmed(
algod: &Algod,
transaction_id: &TransactionId,
) -> Result<PendingTransactionResponse, Error> {
let start = Instant::now();
let mut last_round = algod.status().await?.last_round;
loop {
let pending = algod.pending_transaction(transaction_id).await?;
if pending.confirmed_round.is_some() {
return Ok(pending);
}
if !pending.pool_error.is_empty() {
return Err(Error::PendingTransactionPoolError {
reason: pending.pool_error,
});
}
if start.elapsed() >= COMPOSER_CONFIRM_TIMEOUT {
return Err(Error::PendingTransactionTimeout {
timeout: COMPOSER_CONFIRM_TIMEOUT,
});
}
last_round = algod.status_after_block(last_round).await?.last_round;
}
}
pub(super) async fn sign_group(
txs: &[TransactionWithSigner],
) -> Result<Vec<SignedTransaction>, Error> {
let all_txs: Vec<Transaction> = txs.iter().map(|t| t.transaction.clone()).collect();
let mut signed: Vec<Option<SignedTransaction>> = (0..txs.len()).map(|_| None).collect();
let mut groups: Vec<(Arc<dyn Signer>, Vec<usize>)> = Vec::new();
for (i, tx_with_signer) in txs.iter().enumerate() {
match &tx_with_signer.signer {
Some(signer) => match groups.iter_mut().find(|(s, _)| Arc::ptr_eq(s, signer)) {
Some((_, indexes)) => indexes.push(i),
None => groups.push((Arc::clone(signer), vec![i])),
},
None => return Err(Error::MissingSigner { index: i }),
}
}
for (signer, indexes) in &groups {
let request = SigningRequest {
transactions: &all_txs,
indexes,
};
let result = signer.sign_transactions(request).await?;
if result.len() != indexes.len() {
return Err(Error::SignerOutputInvalid {
reason: format!(
"signer returned {} signed transaction(s) for {} requested",
result.len(),
indexes.len()
),
});
}
for (&index, signed_tx) in indexes.iter().zip(result) {
let expected_id = all_txs[index].id()?;
if signed_tx.transaction_id() != &expected_id {
return Err(Error::SignerOutputInvalid {
reason: format!(
"signer returned a signature for an unexpected transaction at index {index}"
),
});
}
signed[index] = Some(signed_tx);
}
}
signed
.into_iter()
.enumerate()
.map(|(i, slot)| slot.ok_or(Error::InternalSigningIncomplete { index: i }))
.collect()
}
pub(super) fn placeholder_group(
txs: &[TransactionWithSigner],
) -> Result<Vec<SignedTransaction>, Error> {
txs.iter()
.map(|t| signed_transaction::placeholder(t.transaction.clone()).map_err(Error::from))
.collect()
}
pub(super) fn transaction_ids(signed_txs: &[SignedTransaction]) -> Vec<TransactionId> {
signed_txs
.iter()
.map(|t| t.transaction_id().clone())
.collect()
}