mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-11 16:16:14 +01:00
refactor: select proofs on spending condition
This commit is contained in:
@@ -277,7 +277,7 @@ impl JsWallet {
|
||||
self.inner
|
||||
.send(
|
||||
&mint_url,
|
||||
&unit.into(),
|
||||
unit.into(),
|
||||
memo,
|
||||
Amount::from(amount),
|
||||
&target,
|
||||
|
||||
@@ -5,7 +5,9 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use cdk::cdk_database;
|
||||
use cdk::cdk_database::WalletDatabase;
|
||||
use cdk::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State};
|
||||
use cdk::nuts::{
|
||||
CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
|
||||
};
|
||||
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
|
||||
@@ -399,6 +401,7 @@ impl WalletDatabase for RedbWalletDatabase {
|
||||
async fn get_proofs(
|
||||
&self,
|
||||
mint_url: Option<UncheckedUrl>,
|
||||
unit: Option<CurrencyUnit>,
|
||||
state: Option<Vec<State>>,
|
||||
spending_conditions: Option<Vec<SpendingConditions>>,
|
||||
) -> Result<Option<Vec<ProofInfo>>, Self::Err> {
|
||||
@@ -415,10 +418,14 @@ impl WalletDatabase for RedbWalletDatabase {
|
||||
let mut proof = None;
|
||||
|
||||
if let Ok(proof_info) = serde_json::from_str::<ProofInfo>(v.value()) {
|
||||
match proof_info.matches_conditions(&mint_url, &state, &spending_conditions) {
|
||||
Ok(true) => proof = Some(proof_info),
|
||||
Ok(false) => (),
|
||||
Err(_) => (),
|
||||
match proof_info.matches_conditions(
|
||||
&mint_url,
|
||||
&unit,
|
||||
&state,
|
||||
&spending_conditions,
|
||||
) {
|
||||
true => proof = Some(proof_info),
|
||||
false => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ use std::result::Result;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cdk::cdk_database::WalletDatabase;
|
||||
use cdk::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State};
|
||||
use cdk::nuts::{
|
||||
CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
|
||||
};
|
||||
use cdk::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use cdk::url::UncheckedUrl;
|
||||
use rexie::*;
|
||||
@@ -448,6 +450,7 @@ impl WalletDatabase for RexieWalletDatabase {
|
||||
async fn get_proofs(
|
||||
&self,
|
||||
mint_url: Option<UncheckedUrl>,
|
||||
unit: Option<CurrencyUnit>,
|
||||
state: Option<Vec<State>>,
|
||||
spending_conditions: Option<Vec<SpendingConditions>>,
|
||||
) -> Result<Option<Vec<ProofInfo>>, Self::Err> {
|
||||
@@ -472,12 +475,12 @@ impl WalletDatabase for RexieWalletDatabase {
|
||||
if let Ok(proof_info) = serde_wasm_bindgen::from_value::<ProofInfo>(v) {
|
||||
proof = match proof_info.matches_conditions(
|
||||
&mint_url,
|
||||
&unit,
|
||||
&state,
|
||||
&spending_conditions,
|
||||
) {
|
||||
Ok(true) => Some(proof_info),
|
||||
Ok(false) => None,
|
||||
Err(_) => None,
|
||||
true => Some(proof_info),
|
||||
false => None,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ use crate::mint::MintKeySetInfo;
|
||||
#[cfg(feature = "wallet")]
|
||||
use crate::nuts::State;
|
||||
#[cfg(feature = "mint")]
|
||||
use crate::nuts::{BlindSignature, CurrencyUnit, Proof};
|
||||
use crate::nuts::{BlindSignature, Proof};
|
||||
#[cfg(any(feature = "wallet", feature = "mint"))]
|
||||
use crate::nuts::{Id, MintInfo, PublicKey};
|
||||
use crate::nuts::{CurrencyUnit, Id, MintInfo, PublicKey};
|
||||
#[cfg(feature = "wallet")]
|
||||
use crate::nuts::{KeySetInfo, Keys, Proofs, SpendingConditions};
|
||||
#[cfg(feature = "mint")]
|
||||
@@ -81,6 +81,7 @@ pub trait WalletDatabase {
|
||||
async fn get_proofs(
|
||||
&self,
|
||||
mint_url: Option<UncheckedUrl>,
|
||||
unit: Option<CurrencyUnit>,
|
||||
state: Option<Vec<State>>,
|
||||
spending_conditions: Option<Vec<SpendingConditions>>,
|
||||
) -> Result<Option<Vec<ProofInfo>>, Self::Err>;
|
||||
|
||||
@@ -8,7 +8,9 @@ use tokio::sync::RwLock;
|
||||
|
||||
use super::WalletDatabase;
|
||||
use crate::cdk_database::Error;
|
||||
use crate::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State};
|
||||
use crate::nuts::{
|
||||
CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
|
||||
};
|
||||
use crate::types::{MeltQuote, MintQuote, ProofInfo};
|
||||
use crate::url::UncheckedUrl;
|
||||
|
||||
@@ -169,6 +171,7 @@ impl WalletDatabase for WalletMemoryDatabase {
|
||||
async fn get_proofs(
|
||||
&self,
|
||||
mint_url: Option<UncheckedUrl>,
|
||||
unit: Option<CurrencyUnit>,
|
||||
state: Option<Vec<State>>,
|
||||
spending_conditions: Option<Vec<SpendingConditions>>,
|
||||
) -> Result<Option<Vec<ProofInfo>>, Error> {
|
||||
@@ -178,10 +181,10 @@ impl WalletDatabase for WalletMemoryDatabase {
|
||||
.clone()
|
||||
.into_values()
|
||||
.filter_map(|proof_info| {
|
||||
match proof_info.matches_conditions(&mint_url, &state, &spending_conditions) {
|
||||
Ok(true) => Some(proof_info),
|
||||
Ok(false) => None,
|
||||
Err(_) => None,
|
||||
match proof_info.matches_conditions(&mint_url, &unit, &state, &spending_conditions)
|
||||
{
|
||||
true => Some(proof_info),
|
||||
false => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -129,32 +129,39 @@ impl ProofInfo {
|
||||
pub fn matches_conditions(
|
||||
&self,
|
||||
mint_url: &Option<UncheckedUrl>,
|
||||
unit: &Option<CurrencyUnit>,
|
||||
state: &Option<Vec<State>>,
|
||||
spending_conditions: &Option<Vec<SpendingConditions>>,
|
||||
) -> Result<bool, Error> {
|
||||
) -> bool {
|
||||
if let Some(mint_url) = mint_url {
|
||||
if mint_url.ne(&self.mint_url) {
|
||||
return Ok(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(unit) = unit {
|
||||
if unit.ne(&self.unit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(state) = state {
|
||||
if !state.contains(&self.state) {
|
||||
return Ok(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(spending_conditions) = spending_conditions {
|
||||
match &self.spending_condition {
|
||||
None => return Ok(false),
|
||||
None => return false,
|
||||
Some(s) => {
|
||||
if !spending_conditions.contains(s) {
|
||||
return Ok(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ impl Wallet {
|
||||
|
||||
if let Some(proofs) = self
|
||||
.localstore
|
||||
.get_proofs(None, Some(vec![State::Unspent]), None)
|
||||
.get_proofs(None, None, Some(vec![State::Unspent]), None)
|
||||
.await?
|
||||
{
|
||||
for proof in proofs {
|
||||
@@ -164,7 +164,7 @@ impl Wallet {
|
||||
|
||||
if let Some(proofs) = self
|
||||
.localstore
|
||||
.get_proofs(None, Some(vec![State::Pending]), None)
|
||||
.get_proofs(None, None, Some(vec![State::Pending]), None)
|
||||
.await?
|
||||
{
|
||||
let amount = proofs.iter().map(|p| p.proof.amount).sum();
|
||||
@@ -186,7 +186,7 @@ impl Wallet {
|
||||
for (mint, _) in mints {
|
||||
if let Some(proofs) = self
|
||||
.localstore
|
||||
.get_proofs(Some(mint.clone()), None, None)
|
||||
.get_proofs(Some(mint.clone()), None, None, None)
|
||||
.await?
|
||||
{
|
||||
let mut balances = HashMap::new();
|
||||
@@ -211,7 +211,7 @@ impl Wallet {
|
||||
pub async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Error> {
|
||||
Ok(self
|
||||
.localstore
|
||||
.get_proofs(Some(mint_url), Some(vec![State::Unspent]), None)
|
||||
.get_proofs(Some(mint_url), None, Some(vec![State::Unspent]), None)
|
||||
.await?
|
||||
.map(|p| p.into_iter().map(|p| p.proof).collect()))
|
||||
}
|
||||
@@ -373,8 +373,9 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(mint.clone()),
|
||||
Some(vec![State::Unspent, State::Pending]),
|
||||
None,
|
||||
Some(vec![State::Unspent, State::Pending]),
|
||||
Some(vec![]),
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -858,33 +859,62 @@ impl Wallet {
|
||||
pub async fn send(
|
||||
&mut self,
|
||||
mint_url: &UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
unit: CurrencyUnit,
|
||||
memo: Option<String>,
|
||||
amount: Amount,
|
||||
amount_split_target: &SplitTarget,
|
||||
conditions: Option<SpendingConditions>,
|
||||
) -> Result<String, Error> {
|
||||
let input_proofs = self.select_proofs(mint_url.clone(), unit, amount).await?;
|
||||
let (condition_input_proofs, input_proofs) = self
|
||||
.select_proofs(mint_url.clone(), unit.clone(), amount, conditions.clone())
|
||||
.await?;
|
||||
|
||||
let send_proofs = match (
|
||||
input_proofs
|
||||
.iter()
|
||||
.map(|p| p.amount)
|
||||
.sum::<Amount>()
|
||||
.eq(&amount),
|
||||
&conditions,
|
||||
) {
|
||||
(true, None) => Some(input_proofs),
|
||||
_ => {
|
||||
self.swap(
|
||||
mint_url,
|
||||
unit,
|
||||
Some(amount),
|
||||
amount_split_target,
|
||||
input_proofs,
|
||||
conditions,
|
||||
let send_proofs = match conditions {
|
||||
Some(_) => {
|
||||
let needed_amount = condition_input_proofs
|
||||
.iter()
|
||||
.map(|p| p.amount)
|
||||
.sum::<Amount>();
|
||||
|
||||
let top_up_proofs = self
|
||||
.swap(
|
||||
mint_url,
|
||||
&unit,
|
||||
Some(needed_amount),
|
||||
amount_split_target,
|
||||
input_proofs,
|
||||
conditions,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Some(
|
||||
[
|
||||
condition_input_proofs,
|
||||
top_up_proofs.ok_or(Error::InsufficientFunds)?,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
match input_proofs
|
||||
.iter()
|
||||
.map(|p| p.amount)
|
||||
.sum::<Amount>()
|
||||
.eq(&amount)
|
||||
{
|
||||
true => Some(input_proofs),
|
||||
false => {
|
||||
self.swap(
|
||||
mint_url,
|
||||
&unit,
|
||||
Some(amount),
|
||||
amount_split_target,
|
||||
input_proofs,
|
||||
conditions,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -962,32 +992,83 @@ impl Wallet {
|
||||
pub async fn select_proofs(
|
||||
&self,
|
||||
mint_url: UncheckedUrl,
|
||||
unit: &CurrencyUnit,
|
||||
unit: CurrencyUnit,
|
||||
amount: Amount,
|
||||
) -> Result<Proofs, Error> {
|
||||
let mint_proofs: Proofs = self
|
||||
.localstore
|
||||
.get_proofs(Some(mint_url.clone()), Some(vec![State::Unspent]), None)
|
||||
.await?
|
||||
.ok_or(Error::InsufficientFunds)?
|
||||
.into_iter()
|
||||
.map(|p| p.proof)
|
||||
.collect();
|
||||
conditions: Option<SpendingConditions>,
|
||||
) -> Result<(Proofs, Proofs), Error> {
|
||||
let mut condition_mint_proofs = Vec::new();
|
||||
|
||||
if conditions.is_some() {
|
||||
condition_mint_proofs = self
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(mint_url.clone()),
|
||||
Some(unit.clone()),
|
||||
Some(vec![State::Unspent]),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|p| p.proof)
|
||||
.collect();
|
||||
}
|
||||
|
||||
let mint_keysets = self
|
||||
.localstore
|
||||
.get_mint_keysets(mint_url)
|
||||
.get_mint_keysets(mint_url.clone())
|
||||
.await?
|
||||
.ok_or(Error::UnknownKey)?;
|
||||
|
||||
let (active, inactive): (HashSet<KeySetInfo>, HashSet<KeySetInfo>) = mint_keysets
|
||||
.into_iter()
|
||||
.filter(|p| p.unit.eq(unit))
|
||||
.filter(|p| p.unit.eq(&unit.clone()))
|
||||
.partition(|x| x.active);
|
||||
|
||||
let active: HashSet<Id> = active.iter().map(|k| k.id).collect();
|
||||
let inactive: HashSet<Id> = inactive.iter().map(|k| k.id).collect();
|
||||
|
||||
let (mut condition_active_proofs, mut condition_inactive_proofs): (Proofs, Proofs) =
|
||||
condition_mint_proofs
|
||||
.into_iter()
|
||||
.partition(|p| active.contains(&p.keyset_id));
|
||||
|
||||
condition_active_proofs.reverse();
|
||||
condition_inactive_proofs.reverse();
|
||||
|
||||
let condition_proofs = [condition_inactive_proofs, condition_active_proofs].concat();
|
||||
|
||||
let mut condition_selected_proofs: Proofs = Vec::new();
|
||||
|
||||
for proof in condition_proofs {
|
||||
if condition_selected_proofs
|
||||
.iter()
|
||||
.map(|p| p.amount)
|
||||
.sum::<Amount>()
|
||||
< amount
|
||||
{
|
||||
condition_selected_proofs.push(proof);
|
||||
} else {
|
||||
return Ok((condition_selected_proofs, vec![]));
|
||||
}
|
||||
}
|
||||
|
||||
let condition_proof_total = condition_selected_proofs.iter().map(|p| p.amount).sum();
|
||||
|
||||
let mint_proofs: Proofs = self
|
||||
.localstore
|
||||
.get_proofs(
|
||||
Some(mint_url.clone()),
|
||||
Some(unit.clone()),
|
||||
Some(vec![State::Unspent]),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.ok_or(Error::InsufficientFunds)?
|
||||
.into_iter()
|
||||
.map(|p| p.proof)
|
||||
.collect();
|
||||
|
||||
let mut active_proofs: Proofs = Vec::new();
|
||||
let mut inactive_proofs: Proofs = Vec::new();
|
||||
|
||||
@@ -1002,15 +1083,15 @@ impl Wallet {
|
||||
active_proofs.reverse();
|
||||
inactive_proofs.reverse();
|
||||
|
||||
inactive_proofs.append(&mut active_proofs);
|
||||
|
||||
let proofs = inactive_proofs;
|
||||
|
||||
let mut selected_proofs: Proofs = Vec::new();
|
||||
|
||||
for proof in proofs {
|
||||
if selected_proofs.iter().map(|p| p.amount).sum::<Amount>() < amount {
|
||||
for proof in [inactive_proofs, active_proofs].concat() {
|
||||
if selected_proofs.iter().map(|p| p.amount).sum::<Amount>() + condition_proof_total
|
||||
< amount
|
||||
{
|
||||
selected_proofs.push(proof);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1018,7 +1099,7 @@ impl Wallet {
|
||||
return Err(Error::InsufficientFunds);
|
||||
}
|
||||
|
||||
Ok(selected_proofs)
|
||||
Ok((condition_selected_proofs, selected_proofs))
|
||||
}
|
||||
|
||||
/// Melt
|
||||
@@ -1042,8 +1123,14 @@ impl Wallet {
|
||||
};
|
||||
|
||||
let proofs = self
|
||||
.select_proofs(mint_url.clone(), "e_info.unit, quote_info.amount)
|
||||
.await?;
|
||||
.select_proofs(
|
||||
mint_url.clone(),
|
||||
quote_info.unit.clone(),
|
||||
quote_info.amount,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let proofs_amount = proofs.iter().map(|p| p.amount).sum();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user