Add get_spendable_notes method to WalletRead
This commit is contained in:
parent
48f226f8b5
commit
011eda364f
|
@ -165,6 +165,13 @@ pub trait WalletRead {
|
|||
/// with which they are associated.
|
||||
fn get_nullifiers(&self) -> Result<Vec<(Nullifier, AccountId)>, Self::Error>;
|
||||
|
||||
/// Return all spendable notes.
|
||||
fn get_spendable_notes(
|
||||
&self,
|
||||
account: AccountId,
|
||||
anchor_height: BlockHeight,
|
||||
) -> Result<Vec<SpendableNote>, Self::Error>;
|
||||
|
||||
/// Returns a list of spendable notes sufficient to cover the specified
|
||||
/// target value, if possible.
|
||||
fn select_spendable_notes(
|
||||
|
|
|
@ -233,6 +233,15 @@ impl<P: consensus::Parameters> WalletRead for WalletDB<P> {
|
|||
wallet::get_nullifiers(self).map_err(Error::Database)
|
||||
}
|
||||
|
||||
fn get_spendable_notes(
|
||||
&self,
|
||||
account: AccountId,
|
||||
anchor_height: BlockHeight,
|
||||
) -> Result<Vec<SpendableNote>, Self::Error> {
|
||||
wallet::transact::get_spendable_notes(self, account, anchor_height)
|
||||
.map_err(Error::Database)
|
||||
}
|
||||
|
||||
fn select_spendable_notes(
|
||||
&self,
|
||||
account: AccountId,
|
||||
|
@ -345,6 +354,14 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
|
|||
self.wallet_db.get_nullifiers()
|
||||
}
|
||||
|
||||
fn get_spendable_notes(
|
||||
&self,
|
||||
account: AccountId,
|
||||
anchor_height: BlockHeight,
|
||||
) -> Result<Vec<SpendableNote>, Self::Error> {
|
||||
self.wallet_db.get_spendable_notes(account, anchor_height)
|
||||
}
|
||||
|
||||
fn select_spendable_notes(
|
||||
&self,
|
||||
account: AccountId,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Functions for creating transactions.
|
||||
//!
|
||||
use rusqlite::named_params;
|
||||
use rusqlite::{named_params, Row};
|
||||
use std::convert::TryInto;
|
||||
|
||||
use ff::PrimeField;
|
||||
|
@ -18,6 +18,77 @@ use zcash_client_backend::{
|
|||
|
||||
use crate::{error::SqliteClientError, WalletDB};
|
||||
|
||||
fn to_spendable_note(row: &Row) -> Result<SpendableNote, SqliteClientError> {
|
||||
let diversifier = {
|
||||
let d: Vec<_> = row.get(0)?;
|
||||
if d.len() != 11 {
|
||||
return Err(SqliteClientError::CorruptedData(
|
||||
"Invalid diversifier length".to_string(),
|
||||
));
|
||||
}
|
||||
let mut tmp = [0; 11];
|
||||
tmp.copy_from_slice(&d);
|
||||
Diversifier(tmp)
|
||||
};
|
||||
|
||||
let note_value = Amount::from_i64(row.get(1)?).unwrap();
|
||||
|
||||
let rseed = {
|
||||
let rcm_bytes: Vec<_> = row.get(2)?;
|
||||
|
||||
// We store rcm directly in the data DB, regardless of whether the note
|
||||
// used a v1 or v2 note plaintext, so for the purposes of spending let's
|
||||
// pretend this is a pre-ZIP 212 note.
|
||||
let rcm = jubjub::Fr::from_repr(
|
||||
rcm_bytes[..]
|
||||
.try_into()
|
||||
.map_err(|_| SqliteClientError::InvalidNote)?,
|
||||
)
|
||||
.ok_or(SqliteClientError::InvalidNote)?;
|
||||
Rseed::BeforeZip212(rcm)
|
||||
};
|
||||
|
||||
let witness = {
|
||||
let d: Vec<_> = row.get(3)?;
|
||||
IncrementalWitness::read(&d[..])?
|
||||
};
|
||||
|
||||
Ok(SpendableNote {
|
||||
diversifier,
|
||||
note_value,
|
||||
rseed,
|
||||
witness,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_spendable_notes<P>(
|
||||
wdb: &WalletDB<P>,
|
||||
account: AccountId,
|
||||
anchor_height: BlockHeight,
|
||||
) -> Result<Vec<SpendableNote>, SqliteClientError> {
|
||||
let mut stmt_select_notes = wdb.conn.prepare(
|
||||
"SELECT diversifier, value, rcm, witness
|
||||
FROM received_notes
|
||||
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
||||
INNER JOIN sapling_witnesses ON sapling_witnesses.note = received_notes.id_note
|
||||
WHERE account = :account
|
||||
AND spent IS NULL
|
||||
AND transactions.block <= :anchor_height
|
||||
AND sapling_witnesses.block = :anchor_height"
|
||||
)?;
|
||||
|
||||
// Select notes
|
||||
let notes = stmt_select_notes.query_and_then_named::<_, SqliteClientError, _>(
|
||||
named_params![
|
||||
":account": &i64::from(account.0),
|
||||
":anchor_height": &u32::from(anchor_height),
|
||||
],
|
||||
to_spendable_note
|
||||
)?;
|
||||
|
||||
notes.collect::<Result<_, _>>()
|
||||
}
|
||||
|
||||
pub fn select_spendable_notes<P>(
|
||||
wdb: &WalletDB<P>,
|
||||
account: AccountId,
|
||||
|
@ -71,52 +142,10 @@ pub fn select_spendable_notes<P>(
|
|||
":anchor_height": &u32::from(anchor_height),
|
||||
":target_value": &i64::from(target_value),
|
||||
],
|
||||
|row| {
|
||||
let diversifier = {
|
||||
let d: Vec<_> = row.get(0)?;
|
||||
if d.len() != 11 {
|
||||
return Err(SqliteClientError::CorruptedData(
|
||||
"Invalid diversifier length".to_string(),
|
||||
));
|
||||
}
|
||||
let mut tmp = [0; 11];
|
||||
tmp.copy_from_slice(&d);
|
||||
Diversifier(tmp)
|
||||
};
|
||||
|
||||
let note_value = Amount::from_i64(row.get(1)?).unwrap();
|
||||
|
||||
let rseed = {
|
||||
let rcm_bytes: Vec<_> = row.get(2)?;
|
||||
|
||||
// We store rcm directly in the data DB, regardless of whether the note
|
||||
// used a v1 or v2 note plaintext, so for the purposes of spending let's
|
||||
// pretend this is a pre-ZIP 212 note.
|
||||
let rcm = jubjub::Fr::from_repr(
|
||||
rcm_bytes[..]
|
||||
.try_into()
|
||||
.map_err(|_| SqliteClientError::InvalidNote)?,
|
||||
)
|
||||
.ok_or(SqliteClientError::InvalidNote)?;
|
||||
Rseed::BeforeZip212(rcm)
|
||||
};
|
||||
|
||||
let witness = {
|
||||
let d: Vec<_> = row.get(3)?;
|
||||
IncrementalWitness::read(&d[..])?
|
||||
};
|
||||
|
||||
Ok(SpendableNote {
|
||||
diversifier,
|
||||
note_value,
|
||||
rseed,
|
||||
witness,
|
||||
})
|
||||
},
|
||||
to_spendable_note
|
||||
)?;
|
||||
|
||||
let notes: Vec<SpendableNote> = notes.collect::<Result<_, _>>()?;
|
||||
Ok(notes)
|
||||
notes.collect::<Result<_, _>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Reference in New Issue