Merge pull request #688 from nuttycom/wallet/spend_internal_notes
Correctly construct outputs when spending internal notes.
This commit is contained in:
commit
c5d8484f19
|
@ -131,6 +131,9 @@ and this library adheres to Rust's notion of
|
|||
with this wallet.
|
||||
- `Error::ChildIndexOutOfRange` to indicate that a diversifier index for an
|
||||
address is out of range for valid transparent child indices.
|
||||
- `Error::NoteMismatch` to indicate that a note being spent is not associated
|
||||
with either the internal or external full viewing keys corresponding to the
|
||||
provided spending key.
|
||||
- `zcash_client_backend::decrypt`:
|
||||
- `decrypt_transaction` now takes a `HashMap<_, UnifiedFullViewingKey>`
|
||||
instead of `HashMap<_, ExtendedFullViewingKey>`.
|
||||
|
@ -174,7 +177,8 @@ and this library adheres to Rust's notion of
|
|||
- Setters (use direct field access instead).
|
||||
- The hardcoded `data_api::wallet::ANCHOR_OFFSET` constant.
|
||||
- `zcash_client_backend::wallet::AccountId` (moved to `zcash_primitives::zip32::AccountId`).
|
||||
|
||||
- The implementation of `welding_rig::ScanningKey` for `ExtendedFullViewingKey`
|
||||
has been removed. Use `DiversifiableFullViewingKey` instead.
|
||||
|
||||
## [0.5.0] - 2021-03-26
|
||||
### Added
|
||||
|
|
|
@ -253,7 +253,7 @@ impl RecipientAddress {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use zcash_address::test_vectors;
|
||||
use zcash_primitives::{consensus::MAIN_NETWORK, zip32::ExtendedFullViewingKey};
|
||||
use zcash_primitives::consensus::MAIN_NETWORK;
|
||||
|
||||
use super::{RecipientAddress, UnifiedAddress};
|
||||
use crate::keys::sapling;
|
||||
|
@ -268,8 +268,8 @@ mod tests {
|
|||
|
||||
let sapling = {
|
||||
let extsk = sapling::spending_key(&[0; 32], 0, 0.into());
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
Some(extfvk.default_address().1)
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
Some(dfvk.default_address().1)
|
||||
};
|
||||
|
||||
let transparent = { None };
|
||||
|
|
|
@ -86,6 +86,11 @@ pub enum Error<NoteId> {
|
|||
/// identifier.
|
||||
KeyDerivationError(AccountId),
|
||||
|
||||
/// A note being spent does not correspond to either the internal or external
|
||||
/// full viewing key for an account.
|
||||
// TODO: Return the note id for the note that caused the failure
|
||||
NoteMismatch,
|
||||
|
||||
/// An error indicating that a call was attempted to a method providing
|
||||
/// support
|
||||
#[cfg(not(feature = "transparent-inputs"))]
|
||||
|
@ -151,6 +156,7 @@ impl<N: fmt::Display> fmt::Display for Error<N> {
|
|||
Error::SaplingNotActive => write!(f, "Could not determine Sapling upgrade activation height."),
|
||||
Error::MemoForbidden => write!(f, "It is not possible to send a memo to a transparent address."),
|
||||
Error::KeyDerivationError(acct_id) => write!(f, "Key derivation failed for account {:?}", acct_id),
|
||||
Error::NoteMismatch => write!(f, "A note being spent does not correspond to either the internal or external full viewing key for the provided spending key."),
|
||||
|
||||
#[cfg(not(feature = "transparent-inputs"))]
|
||||
Error::TransparentInputsNotSupported => {
|
||||
|
|
|
@ -307,18 +307,28 @@ where
|
|||
// Create the transaction
|
||||
let mut builder = Builder::new(params.clone(), target_height);
|
||||
for selected in spendable_notes {
|
||||
let from = dfvk
|
||||
.fvk()
|
||||
.vk
|
||||
.to_payment_address(selected.diversifier)
|
||||
.unwrap(); //DiversifyHash would have to unexpectedly return the zero point for this to be None
|
||||
|
||||
let note = from
|
||||
.create_note(selected.note_value.into(), selected.rseed)
|
||||
.unwrap();
|
||||
|
||||
let merkle_path = selected.witness.path().expect("the tree is not empty");
|
||||
|
||||
// Attempt to reconstruct the note being spent using both the internal and external dfvks
|
||||
// corresponding to the unified spending key, checking against the witness we are using
|
||||
// to spend the note that we've used the correct key.
|
||||
let note = {
|
||||
let external_note = dfvk
|
||||
.diversified_address(selected.diversifier)
|
||||
.and_then(|addr| addr.create_note(selected.note_value.into(), selected.rseed));
|
||||
let internal_note = dfvk
|
||||
.diversified_change_address(selected.diversifier)
|
||||
.and_then(|addr| addr.create_note(selected.note_value.into(), selected.rseed));
|
||||
|
||||
let expected_root = selected.witness.root();
|
||||
external_note
|
||||
.filter(|n| expected_root == merkle_path.root(n.commitment()))
|
||||
.or_else(|| {
|
||||
internal_note.filter(|n| expected_root == merkle_path.root(n.commitment()))
|
||||
})
|
||||
.ok_or_else(|| E::from(Error::NoteMismatch))
|
||||
}?;
|
||||
|
||||
builder
|
||||
.add_sapling_spend(
|
||||
usk.sapling().clone(),
|
||||
|
|
|
@ -217,7 +217,7 @@ pub fn decode_extended_spending_key(
|
|||
/// use zcash_primitives::zip32::ExtendedFullViewingKey;
|
||||
///
|
||||
/// let extsk = sapling::spending_key(&[0; 32][..], COIN_TYPE, AccountId::from(0));
|
||||
/// let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
/// let extfvk = extsk.to_extended_full_viewing_key();
|
||||
/// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk);
|
||||
/// ```
|
||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
||||
|
@ -517,8 +517,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn extended_full_viewing_key() {
|
||||
let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into();
|
||||
let extfvk = ExtendedSpendingKey::master(&[0; 32][..]).to_extended_full_viewing_key();
|
||||
|
||||
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
|
||||
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
|
||||
|
|
|
@ -175,7 +175,7 @@ impl UnifiedSpendingKey {
|
|||
UnifiedFullViewingKey {
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
transparent: Some(self.transparent.to_account_pubkey()),
|
||||
sapling: Some(sapling::ExtendedFullViewingKey::from(&self.sapling).into()),
|
||||
sapling: Some(self.sapling.to_diversifiable_full_viewing_key()),
|
||||
orchard: Some((&self.orchard).into()),
|
||||
unknown: vec![],
|
||||
}
|
||||
|
@ -584,10 +584,7 @@ mod tests {
|
|||
use proptest::prelude::proptest;
|
||||
|
||||
use super::{sapling, UnifiedFullViewingKey};
|
||||
use zcash_primitives::{
|
||||
consensus::MAIN_NETWORK,
|
||||
zip32::{AccountId, ExtendedFullViewingKey},
|
||||
};
|
||||
use zcash_primitives::{consensus::MAIN_NETWORK, zip32::AccountId};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
|
@ -647,7 +644,7 @@ mod tests {
|
|||
|
||||
let sapling = {
|
||||
let extsk = sapling::spending_key(&[0; 32], 0, account);
|
||||
Some(ExtendedFullViewingKey::from(&extsk).into())
|
||||
Some(extsk.to_diversifiable_full_viewing_key())
|
||||
};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use group::ff::PrimeField;
|
||||
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
use zcash_note_encryption::batch;
|
||||
use zcash_primitives::{
|
||||
|
@ -15,10 +14,7 @@ use zcash_primitives::{
|
|||
Node, Note, Nullifier, NullifierDerivingKey, SaplingIvk,
|
||||
},
|
||||
transaction::components::sapling::CompactOutputDescription,
|
||||
zip32::{
|
||||
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||
AccountId, Scope,
|
||||
},
|
||||
zip32::{sapling::DiversifiableFullViewingKey, AccountId, Scope},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -98,29 +94,6 @@ impl ScanningKey for DiversifiableFullViewingKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// The [`ScanningKey`] implementation for [`ExtendedFullViewingKey`]s.
|
||||
/// Nullifiers may be derived when scanning with these keys.
|
||||
///
|
||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
||||
impl ScanningKey for ExtendedFullViewingKey {
|
||||
type Scope = Scope;
|
||||
type SaplingNk = NullifierDerivingKey;
|
||||
type SaplingKeys = [(Self::Scope, SaplingIvk, Self::SaplingNk); 1];
|
||||
type Nf = sapling::Nullifier;
|
||||
|
||||
fn to_sapling_keys(&self) -> Self::SaplingKeys {
|
||||
[(Scope::External, self.fvk.vk.ivk(), self.fvk.vk.nk)]
|
||||
}
|
||||
|
||||
fn sapling_nf(
|
||||
key: &Self::SaplingNk,
|
||||
note: &Note,
|
||||
witness: &IncrementalWitness<Node>,
|
||||
) -> Self::Nf {
|
||||
note.nf(key, witness.position() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ScanningKey`] implementation for [`SaplingIvk`]s.
|
||||
/// Nullifiers cannot be derived when scanning with these keys.
|
||||
///
|
||||
|
@ -359,7 +332,7 @@ pub(crate) fn scan_block_with_runner<
|
|||
.collect();
|
||||
|
||||
// Increment tree and witnesses
|
||||
let node = Node::new(output.cmu.to_repr());
|
||||
let node = Node::from_scalar(output.cmu);
|
||||
for witness in &mut *existing_witnesses {
|
||||
witness.append(node).unwrap();
|
||||
}
|
||||
|
@ -430,7 +403,7 @@ mod tests {
|
|||
Note, Nullifier, SaplingIvk,
|
||||
},
|
||||
transaction::components::Amount,
|
||||
zip32::{AccountId, ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
zip32::{AccountId, DiversifiableFullViewingKey, ExtendedSpendingKey},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -480,11 +453,11 @@ mod tests {
|
|||
fn fake_compact_block(
|
||||
height: BlockHeight,
|
||||
nf: Nullifier,
|
||||
extfvk: ExtendedFullViewingKey,
|
||||
dfvk: &DiversifiableFullViewingKey,
|
||||
value: Amount,
|
||||
tx_after: bool,
|
||||
) -> CompactBlock {
|
||||
let to = extfvk.default_address().1;
|
||||
let to = dfvk.default_address().1;
|
||||
|
||||
// Create a fake Note for the account
|
||||
let mut rng = OsRng;
|
||||
|
@ -496,7 +469,7 @@ mod tests {
|
|||
rseed,
|
||||
};
|
||||
let encryptor = sapling_note_encryption::<_, Network>(
|
||||
Some(extfvk.fvk.ovk),
|
||||
Some(dfvk.fvk().ovk),
|
||||
note.clone(),
|
||||
to,
|
||||
MemoBytes::empty(),
|
||||
|
@ -554,12 +527,12 @@ mod tests {
|
|||
fn go(scan_multithreaded: bool) {
|
||||
let account = AccountId::from(0);
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
|
||||
let cb = fake_compact_block(
|
||||
1u32.into(),
|
||||
Nullifier([0; 32]),
|
||||
extfvk.clone(),
|
||||
&dfvk,
|
||||
Amount::from_u64(5).unwrap(),
|
||||
false,
|
||||
);
|
||||
|
@ -569,8 +542,7 @@ mod tests {
|
|||
let mut batch_runner = if scan_multithreaded {
|
||||
let mut runner = BatchRunner::<_, _, _, ()>::new(
|
||||
10,
|
||||
extfvk
|
||||
.to_sapling_keys()
|
||||
dfvk.to_sapling_keys()
|
||||
.iter()
|
||||
.map(|(scope, ivk, _)| ((account, *scope), ivk))
|
||||
.map(|(tag, ivk)| (tag, PreparedIncomingViewingKey::new(ivk))),
|
||||
|
@ -587,7 +559,7 @@ mod tests {
|
|||
let txs = scan_block_with_runner(
|
||||
&Network::TestNetwork,
|
||||
cb,
|
||||
&[(&account, &extfvk)],
|
||||
&[(&account, &dfvk)],
|
||||
&[],
|
||||
&mut tree,
|
||||
&mut [],
|
||||
|
@ -618,12 +590,12 @@ mod tests {
|
|||
fn go(scan_multithreaded: bool) {
|
||||
let account = AccountId::from(0);
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
|
||||
let cb = fake_compact_block(
|
||||
1u32.into(),
|
||||
Nullifier([0; 32]),
|
||||
extfvk.clone(),
|
||||
&dfvk,
|
||||
Amount::from_u64(5).unwrap(),
|
||||
true,
|
||||
);
|
||||
|
@ -633,8 +605,7 @@ mod tests {
|
|||
let mut batch_runner = if scan_multithreaded {
|
||||
let mut runner = BatchRunner::<_, _, _, ()>::new(
|
||||
10,
|
||||
extfvk
|
||||
.to_sapling_keys()
|
||||
dfvk.to_sapling_keys()
|
||||
.iter()
|
||||
.map(|(scope, ivk, _)| ((account, *scope), ivk))
|
||||
.map(|(tag, ivk)| (tag, PreparedIncomingViewingKey::new(ivk))),
|
||||
|
@ -651,7 +622,7 @@ mod tests {
|
|||
let txs = scan_block_with_runner(
|
||||
&Network::TestNetwork,
|
||||
cb,
|
||||
&[(&AccountId::from(0), &extfvk)],
|
||||
&[(&AccountId::from(0), &dfvk)],
|
||||
&[],
|
||||
&mut tree,
|
||||
&mut [],
|
||||
|
@ -680,11 +651,11 @@ mod tests {
|
|||
#[test]
|
||||
fn scan_block_with_my_spend() {
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
let nf = Nullifier([7; 32]);
|
||||
let account = AccountId::from(12);
|
||||
|
||||
let cb = fake_compact_block(1u32.into(), nf, extfvk, Amount::from_u64(5).unwrap(), false);
|
||||
let cb = fake_compact_block(1u32.into(), nf, &dfvk, Amount::from_u64(5).unwrap(), false);
|
||||
assert_eq!(cb.vtx.len(), 2);
|
||||
let vks: Vec<(&AccountId, &SaplingIvk)> = vec![];
|
||||
|
||||
|
|
|
@ -933,7 +933,7 @@ mod tests {
|
|||
PaymentAddress,
|
||||
},
|
||||
transaction::components::Amount,
|
||||
zip32::sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||
zip32::sapling::DiversifiableFullViewingKey,
|
||||
};
|
||||
|
||||
use zcash_client_backend::{
|
||||
|
@ -990,7 +990,7 @@ mod tests {
|
|||
let seed = [0u8; 32];
|
||||
let account = AccountId::from(0);
|
||||
let extsk = sapling::spending_key(&seed, network().coin_type(), account);
|
||||
let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
let (tkey, taddr) = {
|
||||
|
|
|
@ -175,7 +175,7 @@ fn init_wallet_db_internal<P: consensus::Parameters + 'static>(
|
|||
///
|
||||
/// use zcash_primitives::{
|
||||
/// consensus::{Network, Parameters},
|
||||
/// zip32::{AccountId, ExtendedFullViewingKey, ExtendedSpendingKey}
|
||||
/// zip32::{AccountId, ExtendedSpendingKey}
|
||||
/// };
|
||||
///
|
||||
/// use zcash_client_backend::{
|
||||
|
@ -197,7 +197,7 @@ fn init_wallet_db_internal<P: consensus::Parameters + 'static>(
|
|||
/// let seed = [0u8; 32]; // insecure; replace with a strong random seed
|
||||
/// let account = AccountId::from(0);
|
||||
/// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account);
|
||||
/// let dfvk = ExtendedFullViewingKey::from(&extsk).into();
|
||||
/// let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
/// let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk), None).unwrap();
|
||||
/// let ufvks = HashMap::from([(account, ufvk)]);
|
||||
/// init_accounts_table(&db_data, &ufvks).unwrap();
|
||||
|
@ -309,7 +309,7 @@ mod tests {
|
|||
block::BlockHash,
|
||||
consensus::{BlockHeight, BranchId, Parameters},
|
||||
transaction::{TransactionData, TxVersion},
|
||||
zip32::sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||
zip32::sapling::ExtendedFullViewingKey,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -693,7 +693,7 @@ mod tests {
|
|||
let seed = [0xab; 32];
|
||||
let account = AccountId::from(0);
|
||||
let secret_key = sapling::spending_key(&seed, tests::network().coin_type(), account);
|
||||
let extfvk = ExtendedFullViewingKey::from(&secret_key);
|
||||
let extfvk = secret_key.to_extended_full_viewing_key();
|
||||
let data_file = NamedTempFile::new().unwrap();
|
||||
let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
|
||||
init_0_3_0(&mut db_data, &extfvk, account).unwrap();
|
||||
|
@ -856,7 +856,7 @@ mod tests {
|
|||
let seed = [0xab; 32];
|
||||
let account = AccountId::from(0);
|
||||
let secret_key = sapling::spending_key(&seed, tests::network().coin_type(), account);
|
||||
let extfvk = ExtendedFullViewingKey::from(&secret_key);
|
||||
let extfvk = secret_key.to_extended_full_viewing_key();
|
||||
let data_file = NamedTempFile::new().unwrap();
|
||||
let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
|
||||
init_autoshielding(&db_data, &extfvk, account).unwrap();
|
||||
|
@ -1025,7 +1025,7 @@ mod tests {
|
|||
|
||||
// First call with data should initialise the accounts table
|
||||
let extsk = sapling::spending_key(&seed, network().coin_type(), account);
|
||||
let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
let ufvk = UnifiedFullViewingKey::new(
|
||||
|
|
|
@ -167,7 +167,7 @@ mod tests {
|
|||
legacy::TransparentAddress,
|
||||
sapling::{note_encryption::try_sapling_output_recovery, prover::TxProver},
|
||||
transaction::{components::Amount, Transaction},
|
||||
zip32::sapling::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
zip32::sapling::ExtendedSpendingKey,
|
||||
};
|
||||
|
||||
use zcash_client_backend::{
|
||||
|
@ -525,7 +525,7 @@ mod tests {
|
|||
let (cb, _) = fake_compact_block(
|
||||
sapling_activation_height() + i,
|
||||
cb.hash(),
|
||||
&ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])).into(),
|
||||
&ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(),
|
||||
value,
|
||||
);
|
||||
insert_into_cache(&db_cache, &cb);
|
||||
|
@ -558,7 +558,7 @@ mod tests {
|
|||
let (cb, _) = fake_compact_block(
|
||||
sapling_activation_height() + 22,
|
||||
cb.hash(),
|
||||
&ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])).into(),
|
||||
&ExtendedSpendingKey::master(&[22]).to_diversifiable_full_viewing_key(),
|
||||
value,
|
||||
);
|
||||
insert_into_cache(&db_cache, &cb);
|
||||
|
@ -674,7 +674,7 @@ mod tests {
|
|||
let (cb, _) = fake_compact_block(
|
||||
sapling_activation_height() + i,
|
||||
cb.hash(),
|
||||
&ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])).into(),
|
||||
&ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(),
|
||||
value,
|
||||
);
|
||||
insert_into_cache(&db_cache, &cb);
|
||||
|
|
|
@ -477,7 +477,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<B> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use blake2b_simd::Params;
|
||||
use ff::{Field, PrimeField};
|
||||
use ff::Field;
|
||||
use rand_core::OsRng;
|
||||
|
||||
use zcash_proofs::prover::LocalTxProver;
|
||||
|
@ -488,7 +488,7 @@ mod tests {
|
|||
extensions::transparent::{self as tze, Extension, FromPayload, ToPayload},
|
||||
legacy::TransparentAddress,
|
||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||
sapling::{Node, Rseed},
|
||||
sapling::Rseed,
|
||||
transaction::{
|
||||
builder::Builder,
|
||||
components::{
|
||||
|
@ -817,7 +817,7 @@ mod tests {
|
|||
let note1 = to
|
||||
.create_note(101000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
||||
.unwrap();
|
||||
let cm1 = Node::new(note1.cmu().to_repr());
|
||||
let cm1 = note1.commitment();
|
||||
let mut tree = CommitmentTree::empty();
|
||||
// fake that the note appears in some previous
|
||||
// shielded output
|
||||
|
|
|
@ -30,6 +30,9 @@ and this library adheres to Rust's notion of
|
|||
- Added to `zcash_primitives::transaction::components::transparent::builder`
|
||||
- `TransparentBuilder::{inputs, outputs}`: accessors for transparent builder state.
|
||||
- `zcash_primitives::transaction::components::transparent::fees`
|
||||
- `zcash_primitives::sapling::Note::commitment`
|
||||
- Added to `zcash_primitives::zip32::sapling::DiversifiableFullViewingKey`
|
||||
- `DiversifiableFullViewingKey::{diversified_address, diversified_change_address}`
|
||||
|
||||
### Changed
|
||||
- `zcash_primitives::transaction::builder::Builder::build` now takes a `FeeRule`
|
||||
|
@ -39,6 +42,10 @@ and this library adheres to Rust's notion of
|
|||
longer fixes the fee for transactions to 0.00001 ZEC; the builder instead
|
||||
computes the fee using a `FeeRule` implementation at build time.
|
||||
|
||||
### Deprecated
|
||||
- `zcash_primitives::zip32::sapling::to_extended_full_viewing_key` Use
|
||||
`to_diversifiable_full_viewing_key` instead.
|
||||
|
||||
### Removed
|
||||
- Removed from `zcash_primitives::transaction::builder::Builder`
|
||||
- `Builder::{new_with_fee, new_with_rng_and_fee`} (use `Builder::{new, new_with_rng}`
|
||||
|
@ -50,6 +57,10 @@ and this library adheres to Rust's notion of
|
|||
- `Error::NoChangeAddress`
|
||||
- `zcash_primitives::transaction::components::sapling::builder::SaplingBuilder::get_candidate_change_address`
|
||||
has been removed; change outputs must now be added by the caller.
|
||||
- The `From<&ExtendedSpendingKey>` instance for `ExtendedFullViewingKey` has been
|
||||
removed. Use `ExtendedSpendingKey::to_diversifiable_full_viewing_key` instead.
|
||||
- `zcash_primitives::sapling::Node::new` has been removed. Use
|
||||
`Node::from_scalar` or (preferably) `Note::commitment` instead.
|
||||
|
||||
## [0.8.1] - 2022-10-19
|
||||
### Added
|
||||
|
|
|
@ -323,13 +323,13 @@ impl<Node: Hashable> CommitmentTree<Node> {
|
|||
///
|
||||
/// let mut tree = CommitmentTree::<Node>::empty();
|
||||
///
|
||||
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
|
||||
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
|
||||
/// tree.append(Node::from_scalar(bls12_381::Scalar::random(&mut rng)));
|
||||
/// tree.append(Node::from_scalar(bls12_381::Scalar::random(&mut rng)));
|
||||
/// let mut witness = IncrementalWitness::from_tree(&tree);
|
||||
/// assert_eq!(witness.position(), 1);
|
||||
/// assert_eq!(tree.root(), witness.root());
|
||||
///
|
||||
/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr());
|
||||
/// let cmu = Node::from_scalar(bls12_381::Scalar::random(&mut rng));
|
||||
/// tree.append(cmu);
|
||||
/// witness.append(cmu);
|
||||
/// assert_eq!(tree.root(), witness.root());
|
||||
|
|
|
@ -77,9 +77,17 @@ pub struct Node {
|
|||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(repr: [u8; 32]) -> Self {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(repr: [u8; 32]) -> Self {
|
||||
Node { repr }
|
||||
}
|
||||
|
||||
/// Constructs a new note commitment tree node from a [`bls12_381::Scalar`]
|
||||
pub fn from_scalar(cmu: bls12_381::Scalar) -> Self {
|
||||
Self {
|
||||
repr: cmu.to_repr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl incrementalmerkletree::Hashable for Node {
|
||||
|
@ -104,7 +112,7 @@ impl HashSer for Node {
|
|||
fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let mut repr = [0u8; 32];
|
||||
reader.read_exact(&mut repr)?;
|
||||
Ok(Node::new(repr))
|
||||
Ok(Node { repr })
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
|
@ -517,6 +525,14 @@ impl Note {
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`self.cmu`] in the correct representation for inclusion in the Sapling
|
||||
/// note commitment tree.
|
||||
pub fn commitment(&self) -> Node {
|
||||
Node {
|
||||
repr: self.cmu().to_repr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
|
@ -563,7 +579,9 @@ pub mod testing {
|
|||
|
||||
prop_compose! {
|
||||
pub fn arb_node()(value in prop::array::uniform32(prop::num::u8::ANY)) -> Node {
|
||||
Node::new(value)
|
||||
Node {
|
||||
repr: value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -544,7 +544,7 @@ mod testing {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ff::{Field, PrimeField};
|
||||
use ff::Field;
|
||||
use rand_core::OsRng;
|
||||
|
||||
use crate::{
|
||||
|
@ -552,13 +552,13 @@ mod tests {
|
|||
legacy::TransparentAddress,
|
||||
memo::MemoBytes,
|
||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||
sapling::{Node, Rseed},
|
||||
sapling::Rseed,
|
||||
transaction::components::{
|
||||
amount::{Amount, DEFAULT_FEE},
|
||||
sapling::builder::{self as build_s},
|
||||
transparent::builder::{self as build_t},
|
||||
},
|
||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
zip32::ExtendedSpendingKey,
|
||||
};
|
||||
|
||||
use super::{Builder, Error};
|
||||
|
@ -583,9 +583,9 @@ mod tests {
|
|||
#[test]
|
||||
fn fails_on_negative_output() {
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let ovk = extfvk.fvk.ovk;
|
||||
let to = extfvk.default_address().1;
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
let ovk = dfvk.fvk().ovk;
|
||||
let to = dfvk.default_address().1;
|
||||
|
||||
let sapling_activation_height = TEST_NETWORK
|
||||
.activation_height(NetworkUpgrade::Sapling)
|
||||
|
@ -665,15 +665,15 @@ mod tests {
|
|||
#[test]
|
||||
fn binding_sig_present_if_shielded_spend() {
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let to = extfvk.default_address().1;
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
let to = dfvk.default_address().1;
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
let note1 = to
|
||||
.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
||||
.unwrap();
|
||||
let cmu1 = Node::new(note1.cmu().to_repr());
|
||||
let cmu1 = note1.commitment();
|
||||
let mut tree = CommitmentTree::empty();
|
||||
tree.append(cmu1).unwrap();
|
||||
let witness1 = IncrementalWitness::from_tree(&tree);
|
||||
|
@ -738,9 +738,9 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let ovk = Some(extfvk.fvk.ovk);
|
||||
let to = extfvk.default_address().1;
|
||||
let dfvk = extsk.to_diversifiable_full_viewing_key();
|
||||
let ovk = Some(dfvk.fvk().ovk);
|
||||
let to = dfvk.default_address().1;
|
||||
|
||||
// Fail if there is only a Sapling output
|
||||
// 0.0005 z-ZEC out, 0.00001 t-ZEC fee
|
||||
|
@ -783,7 +783,7 @@ mod tests {
|
|||
let note1 = to
|
||||
.create_note(50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
||||
.unwrap();
|
||||
let cmu1 = Node::new(note1.cmu().to_repr());
|
||||
let cmu1 = note1.commitment();
|
||||
let mut tree = CommitmentTree::empty();
|
||||
tree.append(cmu1).unwrap();
|
||||
let mut witness1 = IncrementalWitness::from_tree(&tree);
|
||||
|
@ -823,7 +823,7 @@ mod tests {
|
|||
let note2 = to
|
||||
.create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
||||
.unwrap();
|
||||
let cmu2 = Node::new(note2.cmu().to_repr());
|
||||
let cmu2 = note2.commitment();
|
||||
tree.append(cmu2).unwrap();
|
||||
witness1.append(cmu2).unwrap();
|
||||
let witness2 = IncrementalWitness::from_tree(&tree);
|
||||
|
|
|
@ -272,14 +272,14 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
merkle_path: MerklePath<Node>,
|
||||
) -> Result<(), Error> {
|
||||
// Consistency check: all anchors must equal the first one
|
||||
let cmu = Node::new(note.cmu().into());
|
||||
let node = note.commitment();
|
||||
if let Some(anchor) = self.anchor {
|
||||
let path_root: bls12_381::Scalar = merkle_path.root(cmu).into();
|
||||
let path_root: bls12_381::Scalar = merkle_path.root(node).into();
|
||||
if path_root != anchor {
|
||||
return Err(Error::AnchorMismatch);
|
||||
}
|
||||
} else {
|
||||
self.anchor = Some(merkle_path.root(cmu).into())
|
||||
self.anchor = Some(merkle_path.root(node).into())
|
||||
}
|
||||
|
||||
let alpha = jubjub::Fr::random(&mut rng);
|
||||
|
|
|
@ -417,7 +417,7 @@ impl ExtendedSpendingKey {
|
|||
/// Returns the address with the lowest valid diversifier index, along with
|
||||
/// the diversifier index that generated that address.
|
||||
pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
|
||||
ExtendedFullViewingKey::from(self).default_address()
|
||||
self.to_diversifiable_full_viewing_key().default_address()
|
||||
}
|
||||
|
||||
/// Derives an internal spending key given an external spending key.
|
||||
|
@ -456,15 +456,22 @@ impl ExtendedSpendingKey {
|
|||
}
|
||||
}
|
||||
|
||||
#[deprecated(note = "Use `to_diversifiable_full_viewing_key` instead.")]
|
||||
pub fn to_extended_full_viewing_key(&self) -> ExtendedFullViewingKey {
|
||||
self.into()
|
||||
ExtendedFullViewingKey {
|
||||
depth: self.depth,
|
||||
parent_fvk_tag: self.parent_fvk_tag,
|
||||
child_index: self.child_index,
|
||||
chain_code: self.chain_code,
|
||||
fvk: FullViewingKey::from_expanded_spending_key(&self.expsk),
|
||||
dk: self.dk,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_diversifiable_full_viewing_key(&self) -> DiversifiableFullViewingKey {
|
||||
let extfvk = self.to_extended_full_viewing_key();
|
||||
DiversifiableFullViewingKey {
|
||||
fvk: extfvk.fvk,
|
||||
dk: extfvk.dk,
|
||||
fvk: FullViewingKey::from_expanded_spending_key(&self.expsk),
|
||||
dk: self.dk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -503,19 +510,6 @@ impl std::fmt::Debug for ExtendedFullViewingKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey {
|
||||
fn from(xsk: &ExtendedSpendingKey) -> Self {
|
||||
ExtendedFullViewingKey {
|
||||
depth: xsk.depth,
|
||||
parent_fvk_tag: xsk.parent_fvk_tag,
|
||||
child_index: xsk.child_index,
|
||||
chain_code: xsk.chain_code,
|
||||
fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk),
|
||||
dk: xsk.dk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedFullViewingKey {
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let depth = reader.read_u8()?;
|
||||
|
@ -752,6 +746,14 @@ impl DiversifiableFullViewingKey {
|
|||
sapling_default_address(&self.fvk, &self.dk)
|
||||
}
|
||||
|
||||
/// Returns the payment address corresponding to the specified diversifier, if any.
|
||||
///
|
||||
/// In general, it is preferable to use `find_address` instead, but this method is
|
||||
/// useful in some cases for matching keys to existing payment addresses.
|
||||
pub fn diversified_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
|
||||
self.fvk.vk.to_payment_address(diversifier)
|
||||
}
|
||||
|
||||
/// Returns the internal address corresponding to the smallest valid diversifier index,
|
||||
/// along with that index.
|
||||
///
|
||||
|
@ -762,6 +764,17 @@ impl DiversifiableFullViewingKey {
|
|||
sapling_default_address(&internal_dfvk.fvk, &internal_dfvk.dk)
|
||||
}
|
||||
|
||||
/// Returns the change address corresponding to the specified diversifier, if any.
|
||||
///
|
||||
/// In general, it is preferable to use `change_address` instead, but this method is
|
||||
/// useful in some cases for matching keys to existing payment addresses.
|
||||
pub fn diversified_change_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
|
||||
self.derive_internal()
|
||||
.fvk
|
||||
.vk
|
||||
.to_payment_address(diversifier)
|
||||
}
|
||||
|
||||
/// Attempts to decrypt the given address's diversifier with this full viewing key.
|
||||
///
|
||||
/// This method extracts the diversifier from the given address and decrypts it as a
|
||||
|
@ -798,24 +811,26 @@ mod tests {
|
|||
use group::GroupEncoding;
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn derive_nonhardened_child() {
|
||||
let seed = [0; 32];
|
||||
let xsk_m = ExtendedSpendingKey::master(&seed);
|
||||
let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
|
||||
let xfvk_m = xsk_m.to_extended_full_viewing_key();
|
||||
|
||||
let i_5 = ChildIndex::NonHardened(5);
|
||||
let xsk_5 = xsk_m.derive_child(i_5);
|
||||
let xfvk_5 = xfvk_m.derive_child(i_5);
|
||||
|
||||
assert!(xfvk_5.is_ok());
|
||||
assert_eq!(ExtendedFullViewingKey::from(&xsk_5), xfvk_5.unwrap());
|
||||
assert_eq!(xsk_5.to_extended_full_viewing_key(), xfvk_5.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn derive_hardened_child() {
|
||||
let seed = [0; 32];
|
||||
let xsk_m = ExtendedSpendingKey::master(&seed);
|
||||
let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
|
||||
let xfvk_m = xsk_m.to_extended_full_viewing_key();
|
||||
|
||||
let i_5h = ChildIndex::Hardened(5);
|
||||
let xsk_5h = xsk_m.derive_child(i_5h);
|
||||
|
@ -823,7 +838,7 @@ mod tests {
|
|||
|
||||
// Cannot derive a hardened child from an ExtendedFullViewingKey
|
||||
assert!(xfvk_5h.is_err());
|
||||
let xfvk_5h = ExtendedFullViewingKey::from(&xsk_5h);
|
||||
let xfvk_5h = xsk_5h.to_extended_full_viewing_key();
|
||||
|
||||
let i_7 = ChildIndex::NonHardened(7);
|
||||
let xsk_5h_7 = xsk_5h.derive_child(i_7);
|
||||
|
@ -831,7 +846,7 @@ mod tests {
|
|||
|
||||
// But we *can* derive a non-hardened child from a hardened parent
|
||||
assert!(xfvk_5h_7.is_ok());
|
||||
assert_eq!(ExtendedFullViewingKey::from(&xsk_5h_7), xfvk_5h_7.unwrap());
|
||||
assert_eq!(xsk_5h_7.to_extended_full_viewing_key(), xfvk_5h_7.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -933,7 +948,8 @@ mod tests {
|
|||
fn dfvk_round_trip() {
|
||||
let dfvk = {
|
||||
let extsk = ExtendedSpendingKey::master(&[]);
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
#[allow(deprecated)]
|
||||
let extfvk = extsk.to_extended_full_viewing_key();
|
||||
DiversifiableFullViewingKey::from(extfvk)
|
||||
};
|
||||
|
||||
|
@ -953,7 +969,7 @@ mod tests {
|
|||
fn address() {
|
||||
let seed = [0; 32];
|
||||
let xsk_m = ExtendedSpendingKey::master(&seed);
|
||||
let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
|
||||
let xfvk_m = xsk_m.to_diversifiable_full_viewing_key();
|
||||
let j_0 = DiversifierIndex::new();
|
||||
let addr_m = xfvk_m.address(j_0).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -980,10 +996,11 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn read_write() {
|
||||
let seed = [0; 32];
|
||||
let xsk = ExtendedSpendingKey::master(&seed);
|
||||
let fvk = ExtendedFullViewingKey::from(&xsk);
|
||||
let fvk = xsk.to_extended_full_viewing_key();
|
||||
|
||||
let mut ser = vec![];
|
||||
xsk.write(&mut ser).unwrap();
|
||||
|
@ -997,6 +1014,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn test_vectors() {
|
||||
struct TestVector {
|
||||
ask: Option<[u8; 32]>,
|
||||
|
@ -1689,13 +1707,13 @@ mod tests {
|
|||
let m = ExtendedSpendingKey::master(&seed);
|
||||
let m_1 = m.derive_child(i1);
|
||||
let m_1_2h = ExtendedSpendingKey::from_path(&m, &[i1, i2h]);
|
||||
let m_1_2hv = ExtendedFullViewingKey::from(&m_1_2h);
|
||||
let m_1_2hv = m_1_2h.to_extended_full_viewing_key();
|
||||
let m_1_2hv_3 = m_1_2hv.derive_child(i3).unwrap();
|
||||
|
||||
let xfvks = [
|
||||
ExtendedFullViewingKey::from(&m),
|
||||
ExtendedFullViewingKey::from(&m_1),
|
||||
ExtendedFullViewingKey::from(&m_1_2h),
|
||||
m.to_extended_full_viewing_key(),
|
||||
m_1.to_extended_full_viewing_key(),
|
||||
m_1_2h.to_extended_full_viewing_key(),
|
||||
m_1_2hv, // Appears twice so we can de-duplicate test code below
|
||||
m_1_2hv_3,
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue