refactor(wallet/database): get_proofs returns Vec<ProofInfo> instead of Option<Vec<ProofInfo>>

This commit is contained in:
thesimplekid
2024-07-16 15:03:05 +01:00
parent e51f81f441
commit 4f240f3953
7 changed files with 117 additions and 122 deletions

View File

@@ -29,9 +29,10 @@
### Changed
- cdk(wallet): `fn send` returns `Token` so the user can use the struct of convert it to a v3 or v4 string ([thesimplekid]).
- cdk(wallet): Publicly export `MultiMintWallet` ([thesimplekid]).
- cdk(cdk-database): Get `pending` and `spent` `proofs` by `ys` or `secrets` instead of a single proofs ([thesimplekid]).
- cdk(cdk-database): Change `add_blind_signature` to `add_blind_signatures` ([thesimplekid]).
- cdk(cdk-database): Rename `add_active_keyset` to `set_active_keyset` ([thesimplekid]).
- cdk(cdk-database/mint): Get `pending` and `spent` `proofs` by `ys` or `secrets` instead of a single proofs ([thesimplekid]).
- cdk(cdk-database/mint): Change `add_blind_signature` to `add_blind_signatures` ([thesimplekid]).
- cdk(cdk-database/mint): Rename `add_active_keyset` to `set_active_keyset` ([thesimplekid]).
- cdk(cdk-database/wallet): Change `get_proofs` to return `Vec<ProofInfo>` instead of `Option<Vec<ProofInfo>>` ([thesimplekid]).
### Added
- cdk(NUT-11): Add `Copy` on `SigFlag` ([thesimplekid]).

View File

@@ -231,19 +231,17 @@ impl WalletDatabase for WalletRedbDatabase {
.await
.map_err(Error::from)?;
if let Some(proofs) = proofs {
// Proofs with new url
let updated_proofs: Vec<ProofInfo> = proofs
.clone()
.into_iter()
.map(|mut p| {
p.mint_url = new_mint_url.clone();
p
})
.collect();
println!("{:?}", updated_proofs);
// Proofs with new url
let updated_proofs: Vec<ProofInfo> = proofs
.clone()
.into_iter()
.map(|mut p| {
p.mint_url = new_mint_url.clone();
p
})
.collect();
if !updated_proofs.is_empty() {
self.add_proofs(updated_proofs).await?;
}
}
@@ -577,7 +575,7 @@ impl WalletDatabase for WalletRedbDatabase {
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Self::Err> {
) -> Result<Vec<ProofInfo>, Self::Err> {
let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?;
@@ -606,11 +604,7 @@ impl WalletDatabase for WalletRedbDatabase {
})
.collect();
if proofs.is_empty() {
return Ok(None);
}
Ok(Some(proofs))
Ok(proofs)
}
#[instrument(skip(self, proofs))]

View File

@@ -211,16 +211,15 @@ impl WalletDatabase for WalletRexieDatabase {
.await
.map_err(Error::from)?;
if let Some(proofs) = proofs {
let updated_proofs: Vec<ProofInfo> = proofs
.clone()
.into_iter()
.map(|mut p| {
p.mint_url = new_mint_url.clone();
p
})
.collect();
let updated_proofs: Vec<ProofInfo> = proofs
.into_iter()
.map(|mut p| {
p.mint_url = new_mint_url.clone();
p
})
.collect();
if !updated_proofs.is_empty() {
self.add_proofs(updated_proofs).await?;
}
@@ -583,7 +582,7 @@ impl WalletDatabase for WalletRexieDatabase {
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Self::Err> {
) -> Result<Vec<ProofInfo>, Self::Err> {
let rexie = self.db.lock().await;
let transaction = rexie
@@ -620,11 +619,7 @@ impl WalletDatabase for WalletRexieDatabase {
transaction.done().await.map_err(Error::from)?;
if proofs.is_empty() {
return Ok(None);
}
Ok(Some(proofs))
Ok(proofs)
}
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err> {

View File

@@ -507,7 +507,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Self::Err> {
) -> Result<Vec<ProofInfo>, Self::Err> {
let recs = sqlx::query(
r#"
SELECT *
@@ -520,13 +520,11 @@ FROM proof;
let recs = match recs {
Ok(rec) => rec,
Err(err) => match err {
sqlx::Error::RowNotFound => return Ok(None),
sqlx::Error::RowNotFound => return Ok(vec![]),
_ => return Err(Error::SQLX(err).into()),
},
};
tracing::debug!("{}", recs.len());
let proofs: Vec<ProofInfo> = recs
.iter()
.filter_map(|p| match sqlite_row_to_proof_info(p) {
@@ -547,11 +545,10 @@ FROM proof;
}
})
.collect();
tracing::debug!("{}", proofs.len());
match proofs.is_empty() {
false => Ok(Some(proofs)),
true => return Ok(None),
false => Ok(proofs),
true => return Ok(vec![]),
}
}
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err> {

View File

@@ -131,7 +131,7 @@ pub trait WalletDatabase: Debug {
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Self::Err>;
) -> Result<Vec<ProofInfo>, Self::Err>;
/// Remove proofs from storage
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err>;

View File

@@ -99,9 +99,9 @@ impl WalletDatabase for WalletMemoryDatabase {
.await
.map_err(Error::from)?;
if let Some(proofs) = proofs {
// Update proofs
{
let updated_proofs: Vec<ProofInfo> = proofs
.clone()
.into_iter()
.map(|mut p| {
p.mint_url = new_mint_url.clone();
@@ -257,7 +257,7 @@ impl WalletDatabase for WalletMemoryDatabase {
unit: Option<CurrencyUnit>,
state: Option<Vec<State>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Option<Vec<ProofInfo>>, Error> {
) -> Result<Vec<ProofInfo>, Error> {
let proofs = self.proofs.read().await;
let proofs: Vec<ProofInfo> = proofs
@@ -272,11 +272,7 @@ impl WalletDatabase for WalletMemoryDatabase {
})
.collect();
if proofs.is_empty() {
return Ok(None);
}
Ok(Some(proofs))
Ok(proofs)
}
async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Error> {

View File

@@ -113,7 +113,7 @@ impl Wallet {
/// Total unspent balance of wallet
#[instrument(skip(self))]
pub async fn total_balance(&self) -> Result<Amount, Error> {
if let Some(proofs) = self
let proofs = self
.localstore
.get_proofs(
Some(self.mint_url.clone()),
@@ -121,22 +121,16 @@ impl Wallet {
Some(vec![State::Unspent]),
None,
)
.await?
{
let balance = proofs.iter().map(|p| p.proof.amount).sum::<Amount>();
.await?;
let balance = proofs.iter().map(|p| p.proof.amount).sum::<Amount>();
return Ok(balance);
}
Ok(Amount::ZERO)
Ok(balance)
}
/// Total pending balance
#[instrument(skip(self))]
pub async fn total_pending_balance(&self) -> Result<HashMap<CurrencyUnit, Amount>, Error> {
let mut balances = HashMap::new();
if let Some(proofs) = self
let proofs = self
.localstore
.get_proofs(
Some(self.mint_url.clone()),
@@ -144,15 +138,12 @@ impl Wallet {
Some(vec![State::Pending]),
None,
)
.await?
{
for proof in proofs {
balances
.entry(proof.unit)
.and_modify(|ps| *ps += proof.proof.amount)
.or_insert(proof.proof.amount);
}
}
.await?;
let balances = proofs.iter().fold(HashMap::new(), |mut acc, proof| {
*acc.entry(proof.unit).or_insert(Amount::ZERO) += proof.proof.amount;
acc
});
Ok(balances)
}
@@ -160,9 +151,7 @@ impl Wallet {
/// Total reserved balance
#[instrument(skip(self))]
pub async fn total_reserved_balance(&self) -> Result<HashMap<CurrencyUnit, Amount>, Error> {
let mut balances = HashMap::new();
if let Some(proofs) = self
let proofs = self
.localstore
.get_proofs(
Some(self.mint_url.clone()),
@@ -170,15 +159,12 @@ impl Wallet {
Some(vec![State::Reserved]),
None,
)
.await?
{
for proof in proofs {
balances
.entry(proof.unit)
.and_modify(|ps| *ps += proof.proof.amount)
.or_insert(proof.proof.amount);
}
}
.await?;
let balances = proofs.iter().fold(HashMap::new(), |mut acc, proof| {
*acc.entry(proof.unit).or_insert(Amount::ZERO) += proof.proof.amount;
acc
});
Ok(balances)
}
@@ -208,8 +194,9 @@ impl Wallet {
None,
)
.await?
.map(|p| p.into_iter().map(|p| p.proof).collect())
.unwrap_or_default())
.into_iter()
.map(|p| p.proof)
.collect())
}
/// Get pending [`Proofs`]
@@ -224,8 +211,9 @@ impl Wallet {
None,
)
.await?
.map(|p| p.into_iter().map(|p| p.proof).collect())
.unwrap_or_default())
.into_iter()
.map(|p| p.proof)
.collect())
}
/// Get reserved [`Proofs`]
@@ -240,8 +228,9 @@ impl Wallet {
None,
)
.await?
.map(|p| p.into_iter().map(|p| p.proof).collect())
.unwrap_or_default())
.into_iter()
.map(|p| p.proof)
.collect())
}
/// Return proofs to unspent allowing them to be selected and spent
@@ -405,7 +394,7 @@ impl Wallet {
pub async fn check_all_pending_proofs(&self) -> Result<Amount, Error> {
let mut balance = Amount::ZERO;
if let Some(proofs) = self
let proofs = self
.localstore
.get_proofs(
Some(self.mint_url.clone()),
@@ -413,34 +402,37 @@ impl Wallet {
Some(vec![State::Pending, State::Reserved]),
None,
)
.await?
{
let states = self
.check_proofs_spent(proofs.clone().into_iter().map(|p| p.proof).collect())
.await?;
.await?;
// Both `State::Pending` and `State::Unspent` should be included in the pending table.
// This is because a proof that has been crated to send will be stored in the pending table
// in order to avoid accidentally double spending but to allow it to be explicitly reclaimed
let pending_states: HashSet<PublicKey> = states
.into_iter()
.filter(|s| s.state.ne(&State::Spent))
.map(|s| s.y)
.collect();
let (pending_proofs, non_pending_proofs): (Vec<ProofInfo>, Vec<ProofInfo>) = proofs
.into_iter()
.partition(|p| pending_states.contains(&p.y));
let amount = pending_proofs.iter().map(|p| p.proof.amount).sum();
self.localstore
.remove_proofs(&non_pending_proofs.into_iter().map(|p| p.proof).collect())
.await?;
balance += amount;
if proofs.is_empty() {
return Ok(Amount::ZERO);
}
let states = self
.check_proofs_spent(proofs.clone().into_iter().map(|p| p.proof).collect())
.await?;
// Both `State::Pending` and `State::Unspent` should be included in the pending table.
// This is because a proof that has been crated to send will be stored in the pending table
// in order to avoid accidentally double spending but to allow it to be explicitly reclaimed
let pending_states: HashSet<PublicKey> = states
.into_iter()
.filter(|s| s.state.ne(&State::Spent))
.map(|s| s.y)
.collect();
let (pending_proofs, non_pending_proofs): (Vec<ProofInfo>, Vec<ProofInfo>) = proofs
.into_iter()
.partition(|p| pending_states.contains(&p.y));
let amount = pending_proofs.iter().map(|p| p.proof.amount).sum();
self.localstore
.remove_proofs(&non_pending_proofs.into_iter().map(|p| p.proof).collect())
.await?;
balance += amount;
Ok(balance)
}
@@ -946,10 +938,20 @@ impl Wallet {
Some(vec![State::Unspent]),
None,
)
.await?
.ok_or(Error::InsufficientFunds)?;
.await?;
let available_proofs = available_proofs.into_iter().map(|p| p.proof).collect();
let (available_proofs, proofs_sum) = available_proofs.into_iter().map(|p| p.proof).fold(
(Vec::new(), Amount::ZERO),
|(mut acc1, mut acc2), p| {
acc2 += p.amount;
acc1.push(p);
(acc1, acc2)
},
);
if proofs_sum < amount {
return Err(Error::InsufficientFunds);
}
let proofs = self.select_proofs_to_swap(amount, available_proofs).await?;
@@ -1015,10 +1017,20 @@ impl Wallet {
Some(vec![State::Unspent]),
conditions.clone().map(|c| vec![c]),
)
.await?
.unwrap_or_default();
.await?;
let available_proofs = available_proofs.into_iter().map(|p| p.proof).collect();
let (available_proofs, proofs_sum) = available_proofs.into_iter().map(|p| p.proof).fold(
(Vec::new(), Amount::ZERO),
|(mut acc1, mut acc2), p| {
acc2 += p.amount;
acc1.push(p);
(acc1, acc2)
},
);
if proofs_sum < amount {
return Err(Error::InsufficientFunds);
}
let selected = self
.select_proofs_to_send(amount, available_proofs, include_fees)