Allow block scanning using either IVKs or FVKs.
This commit is contained in:
parent
16289750e8
commit
0e022f2283
|
@ -9,7 +9,7 @@ use zcash_primitives::{
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||||
note_encryption::Memo,
|
note_encryption::Memo,
|
||||||
primitives::{Note, Nullifier, PaymentAddress},
|
primitives::{Nullifier, PaymentAddress},
|
||||||
sapling::Node,
|
sapling::Node,
|
||||||
transaction::{components::Amount, Transaction, TxId},
|
transaction::{components::Amount, Transaction, TxId},
|
||||||
zip32::ExtendedFullViewingKey,
|
zip32::ExtendedFullViewingKey,
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
data_api::wallet::ANCHOR_OFFSET,
|
data_api::wallet::ANCHOR_OFFSET,
|
||||||
decrypt::DecryptedOutput,
|
decrypt::DecryptedOutput,
|
||||||
proto::compact_formats::CompactBlock,
|
proto::compact_formats::CompactBlock,
|
||||||
wallet::{AccountId, SpendableNote, WalletShieldedOutput, WalletTx},
|
wallet::{AccountId, SpendableNote, WalletTx},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
|
@ -184,7 +184,7 @@ pub struct PrunedBlock<'a> {
|
||||||
pub block_hash: BlockHash,
|
pub block_hash: BlockHash,
|
||||||
pub block_time: u32,
|
pub block_time: u32,
|
||||||
pub commitment_tree: &'a CommitmentTree<Node>,
|
pub commitment_tree: &'a CommitmentTree<Node>,
|
||||||
pub transactions: &'a Vec<WalletTx>,
|
pub transactions: &'a Vec<WalletTx<Nullifier>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReceivedTransaction<'a> {
|
pub struct ReceivedTransaction<'a> {
|
||||||
|
@ -205,6 +205,7 @@ pub struct SentTransaction<'a> {
|
||||||
/// This trait encapsulates the write capabilities required to update stored
|
/// This trait encapsulates the write capabilities required to update stored
|
||||||
/// wallet data.
|
/// wallet data.
|
||||||
pub trait WalletWrite: WalletRead {
|
pub trait WalletWrite: WalletRead {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn insert_pruned_block(
|
fn insert_pruned_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &PrunedBlock,
|
block: &PrunedBlock,
|
||||||
|
@ -251,70 +252,6 @@ pub trait BlockSource {
|
||||||
F: FnMut(CompactBlock) -> Result<(), Self::Error>;
|
F: FnMut(CompactBlock) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This trait provides a generalization over shielded output representations
|
|
||||||
/// that allows a wallet to avoid coupling to a specific one.
|
|
||||||
// TODO: it'd probably be better not to unify the definitions of
|
|
||||||
// `WalletShieldedOutput` and `DecryptedOutput` via a compositional
|
|
||||||
// approach, if possible.
|
|
||||||
pub trait ShieldedOutput {
|
|
||||||
fn index(&self) -> usize;
|
|
||||||
fn account(&self) -> AccountId;
|
|
||||||
fn to(&self) -> &PaymentAddress;
|
|
||||||
fn note(&self) -> &Note;
|
|
||||||
fn memo(&self) -> Option<&Memo>;
|
|
||||||
fn is_change(&self) -> Option<bool>;
|
|
||||||
fn nullifier(&self) -> Option<Nullifier>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShieldedOutput for WalletShieldedOutput {
|
|
||||||
fn index(&self) -> usize {
|
|
||||||
self.index
|
|
||||||
}
|
|
||||||
fn account(&self) -> AccountId {
|
|
||||||
self.account
|
|
||||||
}
|
|
||||||
fn to(&self) -> &PaymentAddress {
|
|
||||||
&self.to
|
|
||||||
}
|
|
||||||
fn note(&self) -> &Note {
|
|
||||||
&self.note
|
|
||||||
}
|
|
||||||
fn memo(&self) -> Option<&Memo> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn is_change(&self) -> Option<bool> {
|
|
||||||
Some(self.is_change)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nullifier(&self) -> Option<Nullifier> {
|
|
||||||
self.nf.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShieldedOutput for DecryptedOutput {
|
|
||||||
fn index(&self) -> usize {
|
|
||||||
self.index
|
|
||||||
}
|
|
||||||
fn account(&self) -> AccountId {
|
|
||||||
self.account
|
|
||||||
}
|
|
||||||
fn to(&self) -> &PaymentAddress {
|
|
||||||
&self.to
|
|
||||||
}
|
|
||||||
fn note(&self) -> &Note {
|
|
||||||
&self.note
|
|
||||||
}
|
|
||||||
fn memo(&self) -> Option<&Memo> {
|
|
||||||
Some(&self.memo)
|
|
||||||
}
|
|
||||||
fn is_change(&self) -> Option<bool> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn nullifier(&self) -> Option<Nullifier> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "test-dependencies")]
|
#[cfg(feature = "test-dependencies")]
|
||||||
pub mod testing {
|
pub mod testing {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -447,6 +384,7 @@ pub mod testing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletWrite for MockWalletDB {
|
impl WalletWrite for MockWalletDB {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn insert_pruned_block(
|
fn insert_pruned_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
_block: &PrunedBlock,
|
_block: &PrunedBlock,
|
||||||
|
|
|
@ -291,7 +291,7 @@ where
|
||||||
let block_hash = BlockHash::from_slice(&block.hash);
|
let block_hash = BlockHash::from_slice(&block.hash);
|
||||||
let block_time = block.time;
|
let block_time = block.time;
|
||||||
|
|
||||||
let txs: Vec<WalletTx> = {
|
let txs: Vec<WalletTx<Nullifier>> = {
|
||||||
let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.1).collect();
|
let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.1).collect();
|
||||||
|
|
||||||
scan_block(
|
scan_block(
|
||||||
|
@ -344,11 +344,10 @@ where
|
||||||
.flat_map(|tx| tx.shielded_spends.iter().map(|spend| spend.nf))
|
.flat_map(|tx| tx.shielded_spends.iter().map(|spend| spend.nf))
|
||||||
.collect();
|
.collect();
|
||||||
nullifiers.retain(|(_, nf)| !spent_nf.contains(nf));
|
nullifiers.retain(|(_, nf)| !spent_nf.contains(nf));
|
||||||
nullifiers.extend(txs.iter().flat_map(|tx| {
|
nullifiers.extend(
|
||||||
tx.shielded_outputs
|
txs.iter()
|
||||||
.iter()
|
.flat_map(|tx| tx.shielded_outputs.iter().map(|out| (out.account, out.nf))),
|
||||||
.flat_map(|out| out.nf.map(|nf| (out.account, nf)))
|
);
|
||||||
}));
|
|
||||||
|
|
||||||
witnesses.extend(new_witnesses);
|
witnesses.extend(new_witnesses);
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,13 @@ impl ConditionallySelectable for AccountId {
|
||||||
/// A subset of a [`Transaction`] relevant to wallets and light clients.
|
/// A subset of a [`Transaction`] relevant to wallets and light clients.
|
||||||
///
|
///
|
||||||
/// [`Transaction`]: zcash_primitives::transaction::Transaction
|
/// [`Transaction`]: zcash_primitives::transaction::Transaction
|
||||||
pub struct WalletTx {
|
pub struct WalletTx<N> {
|
||||||
pub txid: TxId,
|
pub txid: TxId,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub num_spends: usize,
|
pub num_spends: usize,
|
||||||
pub num_outputs: usize,
|
pub num_outputs: usize,
|
||||||
pub shielded_spends: Vec<WalletShieldedSpend>,
|
pub shielded_spends: Vec<WalletShieldedSpend>,
|
||||||
pub shielded_outputs: Vec<WalletShieldedOutput>,
|
pub shielded_outputs: Vec<WalletShieldedOutput<N>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A subset of a [`SpendDescription`] relevant to wallets and light clients.
|
/// A subset of a [`SpendDescription`] relevant to wallets and light clients.
|
||||||
|
@ -51,7 +51,7 @@ pub struct WalletShieldedSpend {
|
||||||
/// A subset of an [`OutputDescription`] relevant to wallets and light clients.
|
/// A subset of an [`OutputDescription`] relevant to wallets and light clients.
|
||||||
///
|
///
|
||||||
/// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription
|
/// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription
|
||||||
pub struct WalletShieldedOutput {
|
pub struct WalletShieldedOutput<N> {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
pub cmu: bls12_381::Scalar,
|
pub cmu: bls12_381::Scalar,
|
||||||
pub epk: jubjub::ExtendedPoint,
|
pub epk: jubjub::ExtendedPoint,
|
||||||
|
@ -60,7 +60,7 @@ pub struct WalletShieldedOutput {
|
||||||
pub to: PaymentAddress,
|
pub to: PaymentAddress,
|
||||||
pub is_change: bool,
|
pub is_change: bool,
|
||||||
pub witness: IncrementalWitness<Node>,
|
pub witness: IncrementalWitness<Node>,
|
||||||
pub nf: Option<Nullifier>,
|
pub nf: N,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpendableNote {
|
pub struct SpendableNote {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use zcash_primitives::{
|
||||||
consensus::{self, BlockHeight},
|
consensus::{self, BlockHeight},
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||||
note_encryption::try_sapling_compact_note_decryption,
|
note_encryption::try_sapling_compact_note_decryption,
|
||||||
primitives::{Nullifier, ViewingKey},
|
primitives::{Note, Nullifier, PaymentAddress, SaplingIvk, ViewingKey},
|
||||||
sapling::Node,
|
sapling::Node,
|
||||||
transaction::TxId,
|
transaction::TxId,
|
||||||
};
|
};
|
||||||
|
@ -25,17 +25,17 @@ use crate::wallet::{AccountId, WalletShieldedOutput, WalletShieldedSpend, Wallet
|
||||||
///
|
///
|
||||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn scan_output<P: consensus::Parameters>(
|
fn scan_output<P: consensus::Parameters, K: ScanningKey>(
|
||||||
params: &P,
|
params: &P,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
(index, output): (usize, CompactOutput),
|
(index, output): (usize, CompactOutput),
|
||||||
vks: &[(AccountId, &ViewingKey)],
|
vks: &[(AccountId, &K)],
|
||||||
spent_from_accounts: &HashSet<AccountId>,
|
spent_from_accounts: &HashSet<AccountId>,
|
||||||
tree: &mut CommitmentTree<Node>,
|
tree: &mut CommitmentTree<Node>,
|
||||||
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||||
block_witnesses: &mut [&mut IncrementalWitness<Node>],
|
block_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||||
new_witnesses: &mut [&mut IncrementalWitness<Node>],
|
new_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||||
) -> Option<WalletShieldedOutput> {
|
) -> Option<WalletShieldedOutput<K::Nf>> {
|
||||||
let cmu = output.cmu().ok()?;
|
let cmu = output.cmu().ok()?;
|
||||||
let epk = output.epk().ok()?;
|
let epk = output.epk().ok()?;
|
||||||
let ct = output.ciphertext;
|
let ct = output.ciphertext;
|
||||||
|
@ -54,12 +54,10 @@ fn scan_output<P: consensus::Parameters>(
|
||||||
tree.append(node).unwrap();
|
tree.append(node).unwrap();
|
||||||
|
|
||||||
for (account, vk) in vks.iter() {
|
for (account, vk) in vks.iter() {
|
||||||
let ivk = vk.ivk();
|
let (note, to) = match vk.try_decryption(params, height, &epk, &cmu, &ct) {
|
||||||
let (note, to) =
|
Some(ret) => ret,
|
||||||
match try_sapling_compact_note_decryption(params, height, &ivk, &epk, &cmu, &ct) {
|
None => continue,
|
||||||
Some(ret) => ret,
|
};
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// A note is marked as "change" if the account that received it
|
// A note is marked as "change" if the account that received it
|
||||||
// also spent notes in the same transaction. This will catch,
|
// also spent notes in the same transaction. This will catch,
|
||||||
|
@ -70,7 +68,7 @@ fn scan_output<P: consensus::Parameters>(
|
||||||
let is_change = spent_from_accounts.contains(&account);
|
let is_change = spent_from_accounts.contains(&account);
|
||||||
|
|
||||||
let witness = IncrementalWitness::from_tree(tree);
|
let witness = IncrementalWitness::from_tree(tree);
|
||||||
let nf = note.nf(&vk, witness.position() as u64);
|
let nf = vk.nf(¬e, &witness);
|
||||||
|
|
||||||
return Some(WalletShieldedOutput {
|
return Some(WalletShieldedOutput {
|
||||||
index,
|
index,
|
||||||
|
@ -81,30 +79,81 @@ fn scan_output<P: consensus::Parameters>(
|
||||||
to,
|
to,
|
||||||
is_change,
|
is_change,
|
||||||
witness,
|
witness,
|
||||||
nf: Some(nf),
|
nf,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scans a [`CompactBlock`] with a set of [`ViewingKey`]s.
|
pub trait ScanningKey {
|
||||||
|
type Nf;
|
||||||
|
|
||||||
|
fn try_decryption<P: consensus::Parameters>(
|
||||||
|
&self,
|
||||||
|
params: &P,
|
||||||
|
height: BlockHeight,
|
||||||
|
epk: &jubjub::ExtendedPoint,
|
||||||
|
cmu: &bls12_381::Scalar,
|
||||||
|
ct: &[u8],
|
||||||
|
) -> Option<(Note, PaymentAddress)>;
|
||||||
|
|
||||||
|
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScanningKey for ViewingKey {
|
||||||
|
type Nf = Nullifier;
|
||||||
|
|
||||||
|
fn try_decryption<P: consensus::Parameters>(
|
||||||
|
&self,
|
||||||
|
params: &P,
|
||||||
|
height: BlockHeight,
|
||||||
|
epk: &jubjub::ExtendedPoint,
|
||||||
|
cmu: &bls12_381::Scalar,
|
||||||
|
ct: &[u8],
|
||||||
|
) -> Option<(Note, PaymentAddress)> {
|
||||||
|
try_sapling_compact_note_decryption(params, height, &self.ivk(), &epk, &cmu, &ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf {
|
||||||
|
note.nf(self, witness.position() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScanningKey for SaplingIvk {
|
||||||
|
type Nf = ();
|
||||||
|
|
||||||
|
fn try_decryption<P: consensus::Parameters>(
|
||||||
|
&self,
|
||||||
|
params: &P,
|
||||||
|
height: BlockHeight,
|
||||||
|
epk: &jubjub::ExtendedPoint,
|
||||||
|
cmu: &bls12_381::Scalar,
|
||||||
|
ct: &[u8],
|
||||||
|
) -> Option<(Note, PaymentAddress)> {
|
||||||
|
try_sapling_compact_note_decryption(params, height, self, &epk, &cmu, &ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nf(&self, _note: &Note, _witness: &IncrementalWitness<Node>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scans a [`CompactBlock`] with a set of [`ScanningKeys`]s.
|
||||||
///
|
///
|
||||||
/// Returns a vector of [`WalletTx`]s belonging to any of the given
|
/// Returns a vector of [`WalletTx`]s belonging to any of the given
|
||||||
/// [`ViewingKey`]s.
|
/// [`ScanningKey`]s. If scanning with a full viewing key, the nullifiers
|
||||||
|
/// of the resulting [`WalletShieldedOutput`]s will also be computed.
|
||||||
///
|
///
|
||||||
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
|
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
|
||||||
/// incremented appropriately.
|
/// incremented appropriately.
|
||||||
///
|
pub fn scan_block<P: consensus::Parameters, K: ScanningKey>(
|
||||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
|
||||||
pub fn scan_block<P: consensus::Parameters>(
|
|
||||||
params: &P,
|
params: &P,
|
||||||
block: CompactBlock,
|
block: CompactBlock,
|
||||||
vks: &[(AccountId, &ViewingKey)],
|
vks: &[(AccountId, &K)],
|
||||||
nullifiers: &[(AccountId, Nullifier)],
|
nullifiers: &[(AccountId, Nullifier)],
|
||||||
tree: &mut CommitmentTree<Node>,
|
tree: &mut CommitmentTree<Node>,
|
||||||
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||||
) -> Vec<WalletTx> {
|
) -> Vec<WalletTx<K::Nf>> {
|
||||||
let mut wtxs: Vec<WalletTx> = vec![];
|
let mut wtxs: Vec<WalletTx<K::Nf>> = vec![];
|
||||||
let block_height = block.height();
|
let block_height = block.height();
|
||||||
|
|
||||||
for tx in block.vtx.into_iter() {
|
for tx in block.vtx.into_iter() {
|
||||||
|
@ -145,7 +194,7 @@ pub fn scan_block<P: consensus::Parameters>(
|
||||||
shielded_spends.iter().map(|spend| spend.account).collect();
|
shielded_spends.iter().map(|spend| spend.account).collect();
|
||||||
|
|
||||||
// Check for incoming notes while incrementing tree and witnesses
|
// Check for incoming notes while incrementing tree and witnesses
|
||||||
let mut shielded_outputs: Vec<WalletShieldedOutput> = vec![];
|
let mut shielded_outputs: Vec<WalletShieldedOutput<K::Nf>> = vec![];
|
||||||
{
|
{
|
||||||
// Grab mutable references to new witnesses from previous transactions
|
// Grab mutable references to new witnesses from previous transactions
|
||||||
// in this block so that we can update them. Scoped so we don't hold
|
// in this block so that we can update them. Scoped so we don't hold
|
||||||
|
@ -211,7 +260,7 @@ mod tests {
|
||||||
constants::SPENDING_KEY_GENERATOR,
|
constants::SPENDING_KEY_GENERATOR,
|
||||||
merkle_tree::CommitmentTree,
|
merkle_tree::CommitmentTree,
|
||||||
note_encryption::{Memo, SaplingNoteEncryption},
|
note_encryption::{Memo, SaplingNoteEncryption},
|
||||||
primitives::{Note, Nullifier},
|
primitives::{Note, Nullifier, SaplingIvk},
|
||||||
transaction::components::Amount,
|
transaction::components::Amount,
|
||||||
util::generate_random_rseed,
|
util::generate_random_rseed,
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||||
|
@ -408,12 +457,13 @@ mod tests {
|
||||||
|
|
||||||
let cb = fake_compact_block(1u32.into(), nf, extfvk, Amount::from_u64(5).unwrap(), false);
|
let cb = fake_compact_block(1u32.into(), nf, extfvk, Amount::from_u64(5).unwrap(), false);
|
||||||
assert_eq!(cb.vtx.len(), 2);
|
assert_eq!(cb.vtx.len(), 2);
|
||||||
|
let vks: Vec<(AccountId, &SaplingIvk)> = vec![];
|
||||||
|
|
||||||
let mut tree = CommitmentTree::empty();
|
let mut tree = CommitmentTree::empty();
|
||||||
let txs = scan_block(
|
let txs = scan_block(
|
||||||
&Network::TestNetwork,
|
&Network::TestNetwork,
|
||||||
cb,
|
cb,
|
||||||
&[],
|
&vks[..],
|
||||||
&[(account, nf)],
|
&[(account, nf)],
|
||||||
&mut tree,
|
&mut tree,
|
||||||
&mut [],
|
&mut [],
|
||||||
|
|
|
@ -388,6 +388,7 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
|
impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn insert_pruned_block(
|
fn insert_pruned_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &PrunedBlock,
|
block: &PrunedBlock,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use zcash_primitives::{
|
||||||
consensus::{self, BlockHeight, NetworkUpgrade},
|
consensus::{self, BlockHeight, NetworkUpgrade},
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||||
note_encryption::Memo,
|
note_encryption::Memo,
|
||||||
primitives::{Nullifier, PaymentAddress},
|
primitives::{Note, Nullifier, PaymentAddress},
|
||||||
sapling::Node,
|
sapling::Node,
|
||||||
transaction::{components::Amount, Transaction, TxId},
|
transaction::{components::Amount, Transaction, TxId},
|
||||||
zip32::ExtendedFullViewingKey,
|
zip32::ExtendedFullViewingKey,
|
||||||
|
@ -17,12 +17,12 @@ use zcash_primitives::{
|
||||||
|
|
||||||
use zcash_client_backend::{
|
use zcash_client_backend::{
|
||||||
address::RecipientAddress,
|
address::RecipientAddress,
|
||||||
data_api::{error::Error, ShieldedOutput},
|
data_api::error::Error,
|
||||||
encoding::{
|
encoding::{
|
||||||
decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key,
|
decode_extended_full_viewing_key, decode_payment_address, encode_extended_full_viewing_key,
|
||||||
encode_payment_address,
|
encode_payment_address,
|
||||||
},
|
},
|
||||||
wallet::{AccountId, WalletTx},
|
wallet::{AccountId, WalletShieldedOutput, WalletTx},
|
||||||
DecryptedOutput,
|
DecryptedOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,6 +31,70 @@ use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDB};
|
||||||
pub mod init;
|
pub mod init;
|
||||||
pub mod transact;
|
pub mod transact;
|
||||||
|
|
||||||
|
/// This trait provides a generalization over shielded output representations
|
||||||
|
/// that allows a wallet to avoid coupling to a specific one.
|
||||||
|
// TODO: it'd probably be better not to unify the definitions of
|
||||||
|
// `WalletShieldedOutput` and `DecryptedOutput` via a compositional
|
||||||
|
// approach, if possible.
|
||||||
|
pub trait ShieldedOutput {
|
||||||
|
fn index(&self) -> usize;
|
||||||
|
fn account(&self) -> AccountId;
|
||||||
|
fn to(&self) -> &PaymentAddress;
|
||||||
|
fn note(&self) -> &Note;
|
||||||
|
fn memo(&self) -> Option<&Memo>;
|
||||||
|
fn is_change(&self) -> Option<bool>;
|
||||||
|
fn nullifier(&self) -> Option<Nullifier>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShieldedOutput for WalletShieldedOutput<Nullifier> {
|
||||||
|
fn index(&self) -> usize {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
fn account(&self) -> AccountId {
|
||||||
|
self.account
|
||||||
|
}
|
||||||
|
fn to(&self) -> &PaymentAddress {
|
||||||
|
&self.to
|
||||||
|
}
|
||||||
|
fn note(&self) -> &Note {
|
||||||
|
&self.note
|
||||||
|
}
|
||||||
|
fn memo(&self) -> Option<&Memo> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn is_change(&self) -> Option<bool> {
|
||||||
|
Some(self.is_change)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nullifier(&self) -> Option<Nullifier> {
|
||||||
|
Some(self.nf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShieldedOutput for DecryptedOutput {
|
||||||
|
fn index(&self) -> usize {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
fn account(&self) -> AccountId {
|
||||||
|
self.account
|
||||||
|
}
|
||||||
|
fn to(&self) -> &PaymentAddress {
|
||||||
|
&self.to
|
||||||
|
}
|
||||||
|
fn note(&self) -> &Note {
|
||||||
|
&self.note
|
||||||
|
}
|
||||||
|
fn memo(&self) -> Option<&Memo> {
|
||||||
|
Some(&self.memo)
|
||||||
|
}
|
||||||
|
fn is_change(&self) -> Option<bool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn nullifier(&self) -> Option<Nullifier> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the address for the account.
|
/// Returns the address for the account.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -458,9 +522,9 @@ pub fn insert_block<'a, P>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_tx_meta<'a, P>(
|
pub fn put_tx_meta<'a, P, N>(
|
||||||
stmts: &mut DataConnStmtCache<'a, P>,
|
stmts: &mut DataConnStmtCache<'a, P>,
|
||||||
tx: &WalletTx,
|
tx: &WalletTx<N>,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
) -> Result<i64, SqliteClientError> {
|
) -> Result<i64, SqliteClientError> {
|
||||||
let txid = tx.txid.0.to_vec();
|
let txid = tx.txid.0.to_vec();
|
||||||
|
|
Loading…
Reference in New Issue