use nonempty::NonEmpty;
use pepper_sync::wallet::WalletTransaction;
use zcash_client_backend::proposal::{Proposal, Step};
use zcash_primitives::transaction::TxId;
use crate::{lightclient::LightClient, wallet::LightWallet};
#[allow(missing_docs)] #[derive(Debug, thiserror::Error)]
pub enum ProposalToTransactionRecordComparisonError {
#[error("{0:?}")]
LookupError(#[from] LookupRecordsPairStepsError),
#[error("Mismatch: Recorded fee: {0:?} ; Expected fee: {1:?}")]
Mismatch(Result<u64, crate::wallet::error::FeeError>, u64),
}
pub fn compare_fee<NoteRef>(
wallet: &LightWallet,
transaction: &WalletTransaction,
step: &Step<NoteRef>,
) -> Result<u64, ProposalToTransactionRecordComparisonError> {
let recorded_fee_result = wallet.calculate_transaction_fee(transaction);
let proposed_fee = step.balance().fee_required().into_u64();
if let Ok(recorded_fee) = recorded_fee_result {
if recorded_fee == proposed_fee {
return Ok(recorded_fee);
}
}
Err(ProposalToTransactionRecordComparisonError::Mismatch(
recorded_fee_result,
proposed_fee,
))
}
pub async fn lookup_fees_with_proposal_check<N>(
client: &LightClient,
proposal: &Proposal<zcash_primitives::transaction::fees::zip317::FeeRule, N>,
txids: &NonEmpty<TxId>,
) -> Vec<Result<u64, ProposalToTransactionRecordComparisonError>> {
for_each_proposed_transaction(client, proposal, txids, |records, record, step| {
compare_fee(records, record, step)
})
.await
.into_iter()
.map(|stepwise_result| {
stepwise_result
.map_err(ProposalToTransactionRecordComparisonError::LookupError)
.and_then(|fee_comparison_result| fee_comparison_result)
})
.collect()
}
#[allow(missing_docs)] #[derive(Debug, thiserror::Error)]
pub enum LookupRecordsPairStepsError {
#[error("TxId missing from broadcast.")]
MissingFromBroadcast,
#[error("Could not look up TransactionRecord with txid {0:?}.")]
MissingRecord(TxId),
}
pub async fn for_each_proposed_transaction<N, Res>(
client: &LightClient,
proposal: &Proposal<zcash_primitives::transaction::fees::zip317::FeeRule, N>,
txids: &NonEmpty<TxId>,
f: fn(&LightWallet, &WalletTransaction, &Step<N>) -> Res,
) -> Vec<Result<Res, LookupRecordsPairStepsError>> {
let wallet = client.wallet.lock().await;
let mut step_results = vec![];
for (step_number, step) in proposal.steps().iter().enumerate() {
step_results.push({
if let Some(txid) = txids.get(step_number) {
if let Some(transaction) = wallet.wallet_transactions.get(txid) {
Ok(f(&wallet, transaction, step))
} else {
Err(LookupRecordsPairStepsError::MissingRecord(*txid))
}
} else {
Err(LookupRecordsPairStepsError::MissingFromBroadcast)
}
});
}
step_results
}