feat: try_sum for amounts

This commit is contained in:
thesimplekid
2024-09-05 17:33:09 +01:00
parent 5dca9af70f
commit 1f81b24f40
15 changed files with 114 additions and 95 deletions

View File

@@ -51,13 +51,13 @@ impl JsSwapRequest {
/// Proofs Amount /// Proofs Amount
#[wasm_bindgen(js_name = proofsAmount)] #[wasm_bindgen(js_name = proofsAmount)]
pub fn proofs_amount(&self) -> JsAmount { pub fn proofs_amount(&self) -> JsAmount {
self.inner.input_amount().into() self.inner.input_amount().expect("Amount overflow").into()
} }
/// Output Amount /// Output Amount
#[wasm_bindgen(js_name = outputAmount)] #[wasm_bindgen(js_name = outputAmount)]
pub fn output_amount(&self) -> JsAmount { pub fn output_amount(&self) -> JsAmount {
self.inner.output_amount().into() self.inner.output_amount().expect("Amount overflow").into()
} }
} }
@@ -99,6 +99,9 @@ impl JsSwapResponse {
/// Promises Amount /// Promises Amount
#[wasm_bindgen(js_name = promisesAmount)] #[wasm_bindgen(js_name = promisesAmount)]
pub fn promises_amount(&self) -> JsAmount { pub fn promises_amount(&self) -> JsAmount {
self.inner.promises_amount().into() self.inner
.promises_amount()
.expect("Amount overflow")
.into()
} }
} }

View File

@@ -102,7 +102,7 @@ impl JsMintBolt11Request {
#[wasm_bindgen(js_name = totalAmount)] #[wasm_bindgen(js_name = totalAmount)]
pub fn total_amount(&self) -> JsAmount { pub fn total_amount(&self) -> JsAmount {
self.inner.total_amount().into() self.inner.total_amount().expect("Amount overflow").into()
} }
} }

View File

@@ -223,7 +223,10 @@ pub async fn post_melt_bolt11(
} }
}; };
let inputs_amount_quote_unit = payload.proofs_amount(); let inputs_amount_quote_unit = payload.proofs_amount().map_err(|_| {
tracing::error!("Proof inputs in melt quote overflowed");
into_response(Error::AmountOverflow)
})?;
let (preimage, amount_spent_quote_unit) = match mint_quote { let (preimage, amount_spent_quote_unit) = match mint_quote {
Some(mint_quote) => { Some(mint_quote) => {

View File

@@ -37,8 +37,7 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
sleep(Duration::from_secs(2)).await; sleep(Duration::from_secs(2)).await;
} }
let premint_secrets = let premint_secrets = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
PreMintSecrets::random(keyset_id.clone(), 1.into(), &SplitTarget::default())?;
let mint_response = wallet_client let mint_response = wallet_client
.post_mint( .post_mint(
@@ -60,12 +59,11 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
let amount = 2_u64.pow(63); let amount = 2_u64.pow(63);
let pre_mint_amount = let pre_mint_amount =
PreMintSecrets::random(keyset_id.clone(), amount.into(), &SplitTarget::default())?; PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
let pre_mint_amount_two = let pre_mint_amount_two =
PreMintSecrets::random(keyset_id.clone(), amount.into(), &SplitTarget::default())?; PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
let mut pre_mint = let mut pre_mint = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
PreMintSecrets::random(keyset_id.clone(), 1.into(), &SplitTarget::default())?;
pre_mint.combine(pre_mint_amount); pre_mint.combine(pre_mint_amount);
pre_mint.combine(pre_mint_amount_two); pre_mint.combine(pre_mint_amount_two);
@@ -91,11 +89,11 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
println!( println!(
"Pre swap amount: {:?}", "Pre swap amount: {:?}",
pre_swap_proofs.iter().map(|p| p.amount).sum::<Amount>() Amount::try_sum(pre_swap_proofs.iter().map(|p| p.amount)).expect("Amount overflowed")
); );
println!( println!(
"Post swap amount: {:?}", "Post swap amount: {:?}",
post_swap_proofs.iter().map(|p| p.amount).sum::<Amount>() Amount::try_sum(post_swap_proofs.iter().map(|p| p.amount)).expect("Amount Overflowed")
); );
println!( println!(

View File

@@ -54,7 +54,7 @@ impl Amount {
parts.extend(amount_left.split()); parts.extend(amount_left.split());
} }
parts_total = parts.clone().iter().copied().sum::<Amount>(); parts_total = Amount::try_sum(parts.clone().iter().copied())?;
if parts_total.eq(self) { if parts_total.eq(self) {
break; break;
@@ -65,7 +65,7 @@ impl Amount {
parts parts
} }
SplitTarget::Values(values) => { SplitTarget::Values(values) => {
let values_total: Amount = values.clone().into_iter().sum(); let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
match self.cmp(&values_total) { match self.cmp(&values_total) {
Ordering::Equal => values.clone(), Ordering::Equal => values.clone(),
@@ -190,15 +190,6 @@ impl std::ops::Div for Amount {
} }
} }
impl core::iter::Sum for Amount {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Amount::ZERO, |acc, x| {
acc.checked_add(x)
.unwrap_or_else(|| panic!("Addition overflow"))
})
}
}
/// Kinds of targeting that are supported /// Kinds of targeting that are supported
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
pub enum SplitTarget { pub enum SplitTarget {
@@ -314,7 +305,7 @@ mod tests {
let amounts = vec![amount_one, amount_two]; let amounts = vec![amount_one, amount_two];
let _total: Amount = amounts.into_iter().sum(); let _total: Amount = Amount::try_sum(amounts).unwrap();
} }
#[test] #[test]

View File

@@ -20,6 +20,9 @@ pub enum Error {
/// Amount is not what is expected /// Amount is not what is expected
#[error("Amount")] #[error("Amount")]
Amount, Amount,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
/// Not engough inputs provided /// Not engough inputs provided
#[error("Inputs: `{0}`, Outputs: `{0}`, Fee: `{0}`")] #[error("Inputs: `{0}`, Outputs: `{0}`, Fee: `{0}`")]
InsufficientInputs(u64, u64, u64), InsufficientInputs(u64, u64, u64),
@@ -89,6 +92,12 @@ pub enum Error {
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error), NUT00(#[from] crate::nuts::nut00::Error),
/// NUT04 Error
#[error(transparent)]
NUT04(#[from] crate::nuts::nut04::Error),
/// NUT05 Error
#[error(transparent)]
NUT05(#[from] crate::nuts::nut05::Error),
/// NUT11 Error /// NUT11 Error
#[error(transparent)] #[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error), NUT11(#[from] crate::nuts::nut11::Error),

View File

@@ -705,13 +705,13 @@ impl Mint {
return Err(Error::BlindedMessageAlreadySigned); return Err(Error::BlindedMessageAlreadySigned);
} }
let proofs_total = swap_request.input_amount(); let proofs_total = swap_request.input_amount()?;
let output_total = swap_request.output_amount(); let output_total = swap_request.output_amount()?;
let fee = self.get_proofs_fee(&swap_request.inputs).await?; let fee = self.get_proofs_fee(&swap_request.inputs).await?;
if proofs_total < output_total + fee { if proofs_total < output_total.checked_add(fee).ok_or(Error::AmountOverflow)? {
tracing::info!( tracing::info!(
"Swap request without enough inputs: {}, outputs {}, fee {}", "Swap request without enough inputs: {}, outputs {}, fee {}",
proofs_total, proofs_total,
@@ -989,7 +989,7 @@ impl Mint {
.await? .await?
.ok_or(Error::UnknownQuote)?; .ok_or(Error::UnknownQuote)?;
let proofs_total = melt_request.proofs_amount(); let proofs_total = melt_request.proofs_amount()?;
let fee = self.get_proofs_fee(&melt_request.inputs).await?; let fee = self.get_proofs_fee(&melt_request.inputs).await?;
@@ -1121,7 +1121,7 @@ impl Mint {
let mut change = None; let mut change = None;
// Check if there is change to return // Check if there is change to return
if melt_request.proofs_amount() > total_spent { if melt_request.proofs_amount()? > total_spent {
// Check if wallet provided change outputs // Check if wallet provided change outputs
if let Some(outputs) = melt_request.outputs.clone() { if let Some(outputs) = melt_request.outputs.clone() {
let blinded_messages: Vec<PublicKey> = let blinded_messages: Vec<PublicKey> =
@@ -1141,7 +1141,7 @@ impl Mint {
return Err(Error::BlindedMessageAlreadySigned); return Err(Error::BlindedMessageAlreadySigned);
} }
let change_target = melt_request.proofs_amount() - total_spent; let change_target = melt_request.proofs_amount()? - total_spent;
let mut amounts = change_target.split(); let mut amounts = change_target.split();
let mut change_sigs = Vec::with_capacity(amounts.len()); let mut change_sigs = Vec::with_capacity(amounts.len());
@@ -1271,7 +1271,7 @@ impl Mint {
.get_blind_signatures_for_keyset(&keyset.id) .get_blind_signatures_for_keyset(&keyset.id)
.await?; .await?;
let total = blinded.iter().map(|b| b.amount).sum(); let total = Amount::try_sum(blinded.iter().map(|b| b.amount))?;
total_issued.insert(keyset.id, total); total_issued.insert(keyset.id, total);
} }
@@ -1289,14 +1289,13 @@ impl Mint {
for keyset in keysets { for keyset in keysets {
let (proofs, state) = self.localstore.get_proofs_by_keyset_id(&keyset.id).await?; let (proofs, state) = self.localstore.get_proofs_by_keyset_id(&keyset.id).await?;
let total_spent = proofs let total_spent =
.iter() Amount::try_sum(proofs.iter().zip(state).filter_map(|(p, s)| {
.zip(state) match s == Some(State::Spent) {
.filter_map(|(p, s)| match s == Some(State::Spent) { true => Some(p.amount),
true => Some(p.amount), false => None,
false => None, }
}) }))?;
.sum();
total_redeemed.insert(keyset.id, total_spent); total_redeemed.insert(keyset.id, total_spent);
} }

View File

@@ -609,11 +609,10 @@ impl PreMintSecrets {
} }
/// Totoal amount of secrets /// Totoal amount of secrets
pub fn total_amount(&self) -> Amount { pub fn total_amount(&self) -> Result<Amount, Error> {
self.secrets Ok(Amount::try_sum(
.iter() self.secrets.iter().map(|PreMint { amount, .. }| *amount),
.map(|PreMint { amount, .. }| *amount) )?)
.sum()
} }
/// [`BlindedMessage`]s from [`PreMintSecrets`] /// [`BlindedMessage`]s from [`PreMintSecrets`]

View File

@@ -74,7 +74,7 @@ impl Token {
} }
/// Total value of [`Token`] /// Total value of [`Token`]
pub fn value(&self) -> Amount { pub fn value(&self) -> Result<Amount, Error> {
match self { match self {
Self::TokenV3(token) => token.value(), Self::TokenV3(token) => token.value(),
Self::TokenV4(token) => token.value(), Self::TokenV4(token) => token.value(),
@@ -207,11 +207,13 @@ impl TokenV3 {
} }
#[inline] #[inline]
fn value(&self) -> Amount { fn value(&self) -> Result<Amount, Error> {
self.token Ok(Amount::try_sum(
.iter() self.token
.map(|t| t.proofs.iter().map(|p| p.amount).sum()) .iter()
.sum() .map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
.collect::<Result<Vec<Amount>, _>>()?,
)?)
} }
#[inline] #[inline]
@@ -305,11 +307,13 @@ impl TokenV4 {
} }
#[inline] #[inline]
fn value(&self) -> Amount { fn value(&self) -> Result<Amount, Error> {
self.token Ok(Amount::try_sum(
.iter() self.token
.map(|t| t.proofs.iter().map(|p| p.amount).sum()) .iter()
.sum() .map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
.collect::<Result<Vec<Amount>, _>>()?,
)?)
} }
#[inline] #[inline]
@@ -464,7 +468,7 @@ mod tests {
let token_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA=="; let token_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA==";
let token = Token::from_str(token_str_multi_keysets).unwrap(); let token = Token::from_str(token_str_multi_keysets).unwrap();
let amount = token.value(); let amount = token.value()?;
assert_eq!(amount, Amount::from(4)); assert_eq!(amount, Amount::from(4));

View File

@@ -5,7 +5,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs}; use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
use crate::Amount; use crate::{error::Error, Amount};
/// Preswap information /// Preswap information
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
@@ -36,13 +36,13 @@ impl SwapRequest {
} }
/// Total value of proofs in [`SwapRequest`] /// Total value of proofs in [`SwapRequest`]
pub fn input_amount(&self) -> Amount { pub fn input_amount(&self) -> Result<Amount, Error> {
self.inputs.iter().map(|proof| proof.amount).sum() Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))
} }
/// Total value of outputs in [`SwapRequest`] /// Total value of outputs in [`SwapRequest`]
pub fn output_amount(&self) -> Amount { pub fn output_amount(&self) -> Result<Amount, Error> {
self.outputs.iter().map(|proof| proof.amount).sum() Amount::try_sum(self.outputs.iter().map(|proof| proof.amount))
} }
} }
@@ -62,10 +62,11 @@ impl SwapResponse {
} }
/// Total [`Amount`] of promises /// Total [`Amount`] of promises
pub fn promises_amount(&self) -> Amount { pub fn promises_amount(&self) -> Result<Amount, Error> {
self.signatures Amount::try_sum(
.iter() self.signatures
.map(|BlindSignature { amount, .. }| *amount) .iter()
.sum() .map(|BlindSignature { amount, .. }| *amount),
)
} }
} }

View File

@@ -19,6 +19,9 @@ pub enum Error {
/// Unknown Quote State /// Unknown Quote State
#[error("Unknown Quote State")] #[error("Unknown Quote State")]
UnknownState, UnknownState,
/// Amount overflow
#[error("Amount overflow")]
AmountOverflow,
} }
/// Mint quote request [NUT-04] /// Mint quote request [NUT-04]
@@ -179,11 +182,13 @@ pub struct MintBolt11Request {
impl MintBolt11Request { impl MintBolt11Request {
/// Total [`Amount`] of outputs /// Total [`Amount`] of outputs
pub fn total_amount(&self) -> Amount { pub fn total_amount(&self) -> Result<Amount, Error> {
self.outputs Amount::try_sum(
.iter() self.outputs
.map(|BlindedMessage { amount, .. }| *amount) .iter()
.sum() .map(|BlindedMessage { amount, .. }| *amount),
)
.map_err(|_| Error::AmountOverflow)
} }
} }

View File

@@ -21,6 +21,9 @@ pub enum Error {
/// Unknown Quote State /// Unknown Quote State
#[error("Unknown quote state")] #[error("Unknown quote state")]
UnknownState, UnknownState,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
} }
/// Melt quote request [NUT-05] /// Melt quote request [NUT-05]
@@ -210,8 +213,9 @@ pub struct MeltBolt11Request {
impl MeltBolt11Request { impl MeltBolt11Request {
/// Total [`Amount`] of [`Proofs`] /// Total [`Amount`] of [`Proofs`]
pub fn proofs_amount(&self) -> Amount { pub fn proofs_amount(&self) -> Result<Amount, Error> {
self.inputs.iter().map(|proof| proof.amount).sum() Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))
.map_err(|_| Error::AmountOverflow)
} }
} }

View File

@@ -10,7 +10,7 @@ impl MeltBolt11Request {
pub fn output_amount(&self) -> Option<Amount> { pub fn output_amount(&self) -> Option<Amount> {
self.outputs self.outputs
.as_ref() .as_ref()
.map(|o| o.iter().map(|proof| proof.amount).sum()) .and_then(|o| Amount::try_sum(o.iter().map(|proof| proof.amount)).ok())
} }
} }
@@ -19,6 +19,6 @@ impl MeltQuoteBolt11Response {
pub fn change_amount(&self) -> Option<Amount> { pub fn change_amount(&self) -> Option<Amount> {
self.change self.change
.as_ref() .as_ref()
.map(|c| c.iter().map(|b| b.amount).sum()) .and_then(|o| Amount::try_sum(o.iter().map(|proof| proof.amount)).ok())
} }
} }

View File

@@ -39,6 +39,9 @@ pub enum Error {
/// Unknown Key /// Unknown Key
#[error("Unknown key")] #[error("Unknown key")]
UnknownKey, UnknownKey,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
/// Spending Locktime not provided /// Spending Locktime not provided
#[error("Spending condition locktime not provided")] #[error("Spending condition locktime not provided")]
LocktimeNotProvided, LocktimeNotProvided,

View File

@@ -145,7 +145,7 @@ impl Wallet {
None, None,
) )
.await?; .await?;
let balance = proofs.iter().map(|p| p.proof.amount).sum::<Amount>(); let balance = Amount::try_sum(proofs.iter().map(|p| p.proof.amount))?;
Ok(balance) Ok(balance)
} }
@@ -457,7 +457,7 @@ impl Wallet {
.into_iter() .into_iter()
.partition(|p| pending_states.contains(&p.y)); .partition(|p| pending_states.contains(&p.y));
let amount = pending_proofs.iter().map(|p| p.proof.amount).sum(); let amount = Amount::try_sum(pending_proofs.iter().map(|p| p.proof.amount))?;
self.localstore self.localstore
.update_proofs( .update_proofs(
@@ -678,7 +678,7 @@ impl Wallet {
&keys, &keys,
)?; )?;
let minted_amount = proofs.iter().map(|p| p.amount).sum(); let minted_amount = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
// Remove filled quote from store // Remove filled quote from store
self.localstore.remove_mint_quote(&quote_info.id).await?; self.localstore.remove_mint_quote(&quote_info.id).await?;
@@ -754,7 +754,7 @@ impl Wallet {
let mut values = Vec::new(); let mut values = Vec::new();
for amount in amounts_needed_refill { for amount in amounts_needed_refill {
let values_sum: Amount = values.clone().into_iter().sum(); let values_sum = Amount::try_sum(values.clone().into_iter())?;
if values_sum + amount <= change_amount { if values_sum + amount <= change_amount {
values.push(amount); values.push(amount);
} }
@@ -776,7 +776,7 @@ impl Wallet {
let active_keyset_id = self.get_active_mint_keyset().await?.id; let active_keyset_id = self.get_active_mint_keyset().await?.id;
// Desired amount is either amount passed or value of all proof // Desired amount is either amount passed or value of all proof
let proofs_total: Amount = proofs.iter().map(|p| p.amount).sum(); let proofs_total = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
let ys: Vec<PublicKey> = proofs.iter().map(|p| p.y()).collect::<Result<_, _>>()?; let ys: Vec<PublicKey> = proofs.iter().map(|p| p.y()).collect::<Result<_, _>>()?;
self.localstore.set_pending_proofs(ys).await?; self.localstore.set_pending_proofs(ys).await?;
@@ -953,7 +953,7 @@ impl Wallet {
for proof in all_proofs { for proof in all_proofs {
let proofs_to_send_amount = let proofs_to_send_amount =
proofs_to_send.iter().map(|p| p.amount).sum::<Amount>(); Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
if proof.amount + proofs_to_send_amount <= amount + pre_swap.fee { if proof.amount + proofs_to_send_amount <= amount + pre_swap.fee {
proofs_to_send.push(proof); proofs_to_send.push(proof);
} else { } else {
@@ -965,7 +965,7 @@ impl Wallet {
} }
}; };
let send_amount: Amount = proofs_to_send.iter().map(|p| p.amount).sum(); let send_amount = Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
if send_amount.ne(&(amount + pre_swap.fee)) { if send_amount.ne(&(amount + pre_swap.fee)) {
tracing::warn!( tracing::warn!(
@@ -1158,7 +1158,7 @@ impl Wallet {
// Handle exact matches offline // Handle exact matches offline
(SendKind::OfflineExact, Ok(selected_proofs), _) => { (SendKind::OfflineExact, Ok(selected_proofs), _) => {
let selected_proofs_amount = let selected_proofs_amount =
selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let amount_to_send = match include_fees { let amount_to_send = match include_fees {
true => amount + self.get_proofs_fee(&selected_proofs).await?, true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1175,7 +1175,7 @@ impl Wallet {
// Handle exact matches // Handle exact matches
(SendKind::OnlineExact, Ok(selected_proofs), _) => { (SendKind::OnlineExact, Ok(selected_proofs), _) => {
let selected_proofs_amount = let selected_proofs_amount =
selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let amount_to_send = match include_fees { let amount_to_send = match include_fees {
true => amount + self.get_proofs_fee(&selected_proofs).await?, true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1196,7 +1196,7 @@ impl Wallet {
// Handle offline tolerance // Handle offline tolerance
(SendKind::OfflineTolerance(tolerance), Ok(selected_proofs), _) => { (SendKind::OfflineTolerance(tolerance), Ok(selected_proofs), _) => {
let selected_proofs_amount = let selected_proofs_amount =
selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let amount_to_send = match include_fees { let amount_to_send = match include_fees {
true => amount + self.get_proofs_fee(&selected_proofs).await?, true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1222,7 +1222,7 @@ impl Wallet {
// Handle online tolerance with successful selection // Handle online tolerance with successful selection
(SendKind::OnlineTolerance(tolerance), Ok(selected_proofs), _) => { (SendKind::OnlineTolerance(tolerance), Ok(selected_proofs), _) => {
let selected_proofs_amount = let selected_proofs_amount =
selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let amount_to_send = match include_fees { let amount_to_send = match include_fees {
true => amount + self.get_proofs_fee(&selected_proofs).await?, true => amount + self.get_proofs_fee(&selected_proofs).await?,
false => amount, false => amount,
@@ -1435,7 +1435,7 @@ impl Wallet {
Some(change_proofs) => { Some(change_proofs) => {
tracing::debug!( tracing::debug!(
"Change amount returned from melt: {}", "Change amount returned from melt: {}",
change_proofs.iter().map(|p| p.amount).sum::<Amount>() Amount::try_sum(change_proofs.iter().map(|p| p.amount))?
); );
// Update counter for keyset // Update counter for keyset
@@ -1532,7 +1532,7 @@ impl Wallet {
) -> Result<Proofs, Error> { ) -> Result<Proofs, Error> {
// TODO: Check all proofs are same unit // TODO: Check all proofs are same unit
if proofs.iter().map(|p| p.amount).sum::<Amount>() < amount { if Amount::try_sum(proofs.iter().map(|p| p.amount))? < amount {
return Err(Error::InsufficientFunds); return Err(Error::InsufficientFunds);
} }
@@ -1571,8 +1571,8 @@ impl Wallet {
break; break;
} }
remaining_amount = remaining_amount = amount.checked_add(fees).ok_or(Error::AmountOverflow)?
amount + fees - selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); - Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
(proofs_larger, proofs_smaller) = proofs_smaller (proofs_larger, proofs_smaller) = proofs_smaller
.into_iter() .into_iter()
.skip(1) .skip(1)
@@ -1608,7 +1608,7 @@ impl Wallet {
for inactive_proof in inactive_proofs { for inactive_proof in inactive_proofs {
selected_proofs.push(inactive_proof); selected_proofs.push(inactive_proof);
let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let fees = self.get_proofs_fee(&selected_proofs).await?; let fees = self.get_proofs_fee(&selected_proofs).await?;
if selected_total >= amount + fees { if selected_total >= amount + fees {
@@ -1620,7 +1620,7 @@ impl Wallet {
for active_proof in active_proofs { for active_proof in active_proofs {
selected_proofs.push(active_proof); selected_proofs.push(active_proof);
let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>(); let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
let fees = self.get_proofs_fee(&selected_proofs).await?; let fees = self.get_proofs_fee(&selected_proofs).await?;
if selected_total >= amount + fees { if selected_total >= amount + fees {
@@ -1767,7 +1767,7 @@ impl Wallet {
let mut total_amount = Amount::ZERO; let mut total_amount = Amount::ZERO;
for (mint, proofs) in received_proofs { for (mint, proofs) in received_proofs {
total_amount += proofs.iter().map(|p| p.amount).sum(); total_amount += Amount::try_sum(proofs.iter().map(|p| p.amount))?;
let proofs = proofs let proofs = proofs
.into_iter() .into_iter()
.map(|proof| ProofInfo::new(proof, mint.clone(), State::Unspent, self.unit)) .map(|proof| ProofInfo::new(proof, mint.clone(), State::Unspent, self.unit))
@@ -1927,7 +1927,7 @@ impl Wallet {
.cloned() .cloned()
.collect(); .collect();
restored_value += unspent_proofs.iter().map(|p| p.amount).sum(); restored_value += Amount::try_sum(unspent_proofs.iter().map(|p| p.amount))?;
let unspent_proofs = unspent_proofs let unspent_proofs = unspent_proofs
.into_iter() .into_iter()