mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-22 23:25:22 +01:00
feat: try_sum for amounts
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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!(
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`]
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
Ok(Amount::try_sum(
|
||||||
self.token
|
self.token
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.proofs.iter().map(|p| p.amount).sum())
|
.map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
|
||||||
.sum()
|
.collect::<Result<Vec<Amount>, _>>()?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -305,11 +307,13 @@ impl TokenV4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn value(&self) -> Amount {
|
fn value(&self) -> Result<Amount, Error> {
|
||||||
|
Ok(Amount::try_sum(
|
||||||
self.token
|
self.token
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.proofs.iter().map(|p| p.amount).sum())
|
.map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
|
||||||
.sum()
|
.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));
|
||||||
|
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
Amount::try_sum(
|
||||||
self.signatures
|
self.signatures
|
||||||
.iter()
|
.iter()
|
||||||
.map(|BlindSignature { amount, .. }| *amount)
|
.map(|BlindSignature { amount, .. }| *amount),
|
||||||
.sum()
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
Amount::try_sum(
|
||||||
self.outputs
|
self.outputs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|BlindedMessage { amount, .. }| *amount)
|
.map(|BlindedMessage { amount, .. }| *amount),
|
||||||
.sum()
|
)
|
||||||
|
.map_err(|_| Error::AmountOverflow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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("e_info.id).await?;
|
self.localstore.remove_mint_quote("e_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()
|
||||||
|
|||||||
Reference in New Issue
Block a user