Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZIP212 implementation #258

Merged
merged 31 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b537f0f
Pass height to methods which encrypt or decrypt Sapling outputs
therealyingtong Jul 30, 2020
65504d9
Add enum Rseed<E::Fs> to Note struct
therealyingtong Jul 30, 2020
eda00ec
Pass esk to SaplingNoteEncryption::new and add generate_or_derive_esk()
therealyingtong Jul 30, 2020
6904c8f
Implement plaintext_version_is_valid()
therealyingtong Jul 30, 2020
895e251
Fix tests
therealyingtong Jul 30, 2020
b1ddd55
Check derived esk against claimed epk when decrypting note
therealyingtong Jul 31, 2020
eba542c
Add activation heights as consts in consensus::Parameters
therealyingtong Aug 3, 2020
b34e8b9
Add invalid version byte tests for ZIP212
therealyingtong Aug 3, 2020
0a47a9d
Pass rseed to Prover
therealyingtong Aug 4, 2020
74b2f0a
Pass height to decrypt_transaction()
therealyingtong Aug 4, 2020
a25348d
Revert passing Parameters to methods
therealyingtong Aug 5, 2020
d5f8061
Switch plaintext version on height in commit_to_address() in zcash_cl…
therealyingtong Aug 5, 2020
a3ae1b2
Switch plaintext version on height for dummy outputs in Builder
therealyingtong Aug 5, 2020
2ed9b6f
Refactor contextual random rseed generation into util method in zcash…
therealyingtong Aug 5, 2020
c8fcdeb
Minor changes in note_encryption.rs
therealyingtong Aug 5, 2020
d6deadd
Only query last_height when needed in decrypt_and_store_transaction()
therealyingtong Aug 5, 2020
88072d6
Remove network cfg from zcash_client_backend and zcash_primitives
therealyingtong Aug 6, 2020
44f46e5
Add <P: consensus::Parameters> type parameter to Builder struct
therealyingtong Aug 6, 2020
b05e257
Fix database queries in scan.rs and transact.rs
therealyingtong Aug 6, 2020
9970a8a
Hard-code NetworkUpgrade::Canopy in generate_random_rseed
therealyingtong Aug 6, 2020
4f22077
Remove const activation heights from consensus.rs
therealyingtong Aug 6, 2020
c3d8964
Revert SaplingNoteEncryption::new() API to take rng instead of esk
therealyingtong Aug 6, 2020
8786468
Switch on leadbyte instead of tx height when decrypting outputs
therealyingtong Aug 6, 2020
8968547
Document pub enum Rseed<Fs> with link to ZIP 212
therealyingtong Aug 6, 2020
40a908e
Refactor leadbyte_array in note_encryption tests
therealyingtong Aug 6, 2020
0f8f1b3
Fix parsing of rseed in parse_note_plaintext_without_memo() and try_s…
therealyingtong Aug 6, 2020
7cee29b
Use <P: consensus::Parameters> type for Builder impl
therealyingtong Aug 6, 2020
5480a37
Extract derive_esk() into separate function
therealyingtong Aug 6, 2020
d54fd09
Add esk check in parse_note_plaintext_without_memo() and try_sapling_…
therealyingtong Aug 6, 2020
13f4d08
Avoid using unwrap() when calling derive_esk()
therealyingtong Aug 6, 2020
72cc8fc
Minor refactor of enumeration in tests in note_encryption.rs
therealyingtong Aug 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions zcash_client_backend/src/decrypt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use pairing::bls12_381::Bls12;
use zcash_primitives::{
consensus,
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo},
primitives::{Note, PaymentAddress},
transaction::Transaction,
Expand Down Expand Up @@ -30,7 +31,8 @@ pub struct DecryptedOutput {

/// Scans a [`Transaction`] for any information that can be decrypted by the set of
/// [`ExtendedFullViewingKey`]s.
pub fn decrypt_transaction(
pub fn decrypt_transaction<P: consensus::Parameters>(
height: u32,
tx: &Transaction,
extfvks: &[ExtendedFullViewingKey],
) -> Vec<DecryptedOutput> {
Expand All @@ -49,21 +51,27 @@ pub fn decrypt_transaction(
};

for (account, (ivk, ovk)) in vks.iter().enumerate() {
let ((note, to, memo), outgoing) =
match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) {
Some(ret) => (ret, false),
None => match try_sapling_output_recovery(
ovk,
&output.cv,
&output.cmu,
&epk,
&output.enc_ciphertext,
&output.out_ciphertext,
) {
Some(ret) => (ret, true),
None => continue,
},
};
let ((note, to, memo), outgoing) = match try_sapling_note_decryption::<P>(
height,
ivk,
&epk,
&output.cmu,
&output.enc_ciphertext,
) {
Some(ret) => (ret, false),
None => match try_sapling_output_recovery::<P>(
height,
ovk,
&output.cv,
&output.cmu,
&epk,
&output.enc_ciphertext,
&output.out_ciphertext,
) {
Some(ret) => (ret, true),
None => continue,
},
};
decrypted.push(DecryptedOutput {
index,
note,
Expand Down
29 changes: 18 additions & 11 deletions zcash_client_backend/src/welding_rig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ff::PrimeField;
use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{
consensus,
jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
Expand All @@ -22,7 +23,8 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented
/// with this output's commitment.
fn scan_output(
fn scan_output<P: consensus::Parameters>(
height: u32,
(index, output): (usize, CompactOutput),
ivks: &[Fs],
spent_from_accounts: &HashSet<usize>,
Expand All @@ -49,10 +51,11 @@ fn scan_output(
tree.append(node).unwrap();

for (account, ivk) in ivks.iter().enumerate() {
let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};
let (note, to) =
match try_sapling_compact_note_decryption::<P>(height, ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};

// A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch,
Expand Down Expand Up @@ -83,7 +86,7 @@ fn scan_output(
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
/// incremented appropriately.
pub fn scan_block(
pub fn scan_block<P: consensus::Parameters>(
block: CompactBlock,
extfvks: &[ExtendedFullViewingKey],
nullifiers: &[(&[u8], usize)],
Expand Down Expand Up @@ -150,7 +153,8 @@ pub fn scan_block(
.map(|output| &mut output.witness)
.collect();

if let Some(output) = scan_output(
if let Some(output) = scan_output::<P>(
block.height as u32,
to_scan,
&ivks,
&spent_from_accounts,
Expand Down Expand Up @@ -187,11 +191,13 @@ mod tests {
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{OsRng, RngCore};
use zcash_primitives::{
consensus::TestNetwork,
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::Note,
transaction::components::Amount,
util::generate_random_rseed,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
Expand Down Expand Up @@ -249,11 +255,12 @@ mod tests {

// Create a fake Note for the account
let mut rng = OsRng;
let rseed = generate_random_rseed::<TestNetwork, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
Expand Down Expand Up @@ -318,7 +325,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 2);

let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);

let tx = &txs[0];
Expand Down Expand Up @@ -350,7 +357,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 3);

let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);

let tx = &txs[0];
Expand Down Expand Up @@ -378,7 +385,7 @@ mod tests {
assert_eq!(cb.vtx.len(), 2);

let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []);
let txs = scan_block::<TestNetwork>(cb, &[], &[(&nf, account)], &mut tree, &mut []);
assert_eq!(txs.len(), 1);

let tx = &txs[0];
Expand Down
21 changes: 15 additions & 6 deletions zcash_client_sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ use zcash_primitives::zip32::ExtendedFullViewingKey;
use zcash_client_backend::constants::mainnet::{
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
};

#[cfg(not(feature = "mainnet"))]
use zcash_client_backend::constants::testnet::{
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
};

#[cfg(feature = "mainnet")]
pub use zcash_primitives::consensus::MainNetwork as Network;

#[cfg(not(feature = "mainnet"))]
pub use zcash_primitives::consensus::TestNetwork as Network;

pub mod address;
pub mod chain;
pub mod error;
Expand Down Expand Up @@ -89,7 +94,8 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error:

#[cfg(test)]
mod tests {
use ff::{Field, PrimeField};
use crate::Network;
use ff::PrimeField;
use pairing::bls12_381::Bls12;
use protobuf::Message;
use rand_core::{OsRng, RngCore};
Expand All @@ -100,10 +106,10 @@ mod tests {
};
use zcash_primitives::{
block::BlockHash,
jubjub::fs::Fs,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::{Note, PaymentAddress},
transaction::components::Amount,
util::generate_random_rseed,
zip32::ExtendedFullViewingKey,
JUBJUB,
};
Expand All @@ -120,11 +126,12 @@ mod tests {

// Create a fake Note for the account
let mut rng = OsRng;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
Expand Down Expand Up @@ -168,6 +175,7 @@ mod tests {
value: Amount,
) -> CompactBlock {
let mut rng = OsRng;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);

// Create a fake CompactBlock containing the note
let mut cspend = CompactSpend::new();
Expand All @@ -184,7 +192,7 @@ mod tests {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
Expand All @@ -208,11 +216,12 @@ mod tests {
// Create a fake Note for the change
ctx.outputs.push({
let change_addr = extfvk.default_address().unwrap().1;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: change_addr.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: change_addr.pk_d().clone(),
value: (in_value - value).into(),
r: Fs::random(&mut rng),
rseed,
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
Expand Down
28 changes: 22 additions & 6 deletions zcash_client_sqlite/src/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use ff::PrimeField;
use protobuf::parse_from_bytes;
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
use rusqlite::{types::ToSql, Connection, OptionalExtension, NO_PARAMS};
use std::path::Path;
use zcash_client_backend::{
decrypt_transaction, encoding::decode_extended_full_viewing_key,
Expand All @@ -18,7 +18,7 @@ use zcash_primitives::{
use crate::{
address::RecipientAddress,
error::{Error, ErrorKind},
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT,
Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT,
};

struct CompactBlockRow {
Expand Down Expand Up @@ -187,7 +187,7 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
let txs = {
let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect();
let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect();
scan_block(
scan_block::<Network>(
block,
&extfvks[..],
&nf_refs,
Expand Down Expand Up @@ -269,7 +269,7 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
.collect();

for output in tx.shielded_outputs {
let rcm = output.note.r.to_repr();
let rcm = output.note.rcm().to_repr();
let nf = output.note.nf(
&extfvks[output.account].fvk.vk,
output.witness.position() as u64,
Expand Down Expand Up @@ -372,7 +372,23 @@ pub fn decrypt_and_store_transaction<P: AsRef<Path>>(
.collect::<Result<Result<Option<_>, _>, _>>()??
.ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?;

let outputs = decrypt_transaction(tx, &extfvks);
// Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions.
let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?;
let height = match stmt_select_block
.query_row(&[tx.txid().0.to_vec()], |row| row.get(0))
.optional()?
{
Some(height) => height,
None => data
.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
row.get(0)
})
.optional()?
.map(|last_height: u32| last_height + 1)
.unwrap_or(SAPLING_ACTIVATION_HEIGHT as u32),
};

let outputs = decrypt_transaction::<Network>(height as u32, tx, &extfvks);

if outputs.is_empty() {
// Nothing to see here
Expand Down Expand Up @@ -457,7 +473,7 @@ pub fn decrypt_and_store_transaction<P: AsRef<Path>>(
])?;
}
} else {
let rcm = output.note.r.to_repr();
let rcm = output.note.rcm().to_repr();

// Try updating an existing received note.
if stmt_update_received_note.execute(&[
Expand Down
Loading