Merge pull request #301 from zcash/non-consensus-changes-on-branchid-c4cd541e

Merge non-consensus changes
This commit is contained in:
str4d 2022-03-22 19:42:35 +00:00 committed by GitHub
commit 43d38f5b97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 201 additions and 62 deletions

View File

@ -9,13 +9,13 @@ use std::io;
use blake2b_simd::Hash as Blake2bHash;
use memuse::DynamicUsage;
use nonempty::NonEmpty;
use zcash_note_encryption::try_note_decryption;
use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk};
use crate::{
address::Address,
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
circuit::{Instance, Proof, VerifyingKey},
keys::IncomingViewingKey,
keys::{IncomingViewingKey, OutgoingViewingKey},
note::{ExtractedNoteCommitment, Note, Nullifier, TransmittedNoteCiphertext},
note_encryption::OrchardDomain,
primitives::redpallas::{self, Binding, SpendAuth},
@ -364,10 +364,11 @@ impl<T: Authorization, V> Bundle<T, V> {
.collect()
}
/// Perform trial decryption of each action in the bundle with each of the
/// specified incoming viewing keys, and return the decrypted note contents
/// along with the index of the action from which it was derived.
pub fn decrypt_outputs_for_keys(
/// Performs trial decryption of each action in the bundle with each of the
/// specified incoming viewing keys, and returns a vector of each decrypted
/// note plaintext contents along with the index of the action from which it
/// was derived.
pub fn decrypt_outputs_with_keys(
&self,
keys: &[IncomingViewingKey],
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
@ -384,8 +385,9 @@ impl<T: Authorization, V> Bundle<T, V> {
.collect()
}
/// Perform trial decryption of each action at `action_idx` in the bundle with the
/// specified incoming viewing key, and return the decrypted note contents.
/// Performs trial decryption of the action at `action_idx` in the bundle with the
/// specified incoming viewing key, and returns the decrypted note plaintext
/// contents if successful.
pub fn decrypt_output_with_key(
&self,
action_idx: usize,
@ -396,6 +398,53 @@ impl<T: Authorization, V> Bundle<T, V> {
try_note_decryption(&domain, key, action)
})
}
/// Performs trial decryption of each action in the bundle with each of the
/// specified outgoing viewing keys, and returns a vector of each decrypted
/// note plaintext contents along with the index of the action from which it
/// was derived.
pub fn recover_outputs_with_ovks(
&self,
keys: &[OutgoingViewingKey],
) -> Vec<(usize, OutgoingViewingKey, Note, Address, [u8; 512])> {
self.actions
.iter()
.enumerate()
.filter_map(|(idx, action)| {
let domain = OrchardDomain::for_action(action);
keys.iter().find_map(move |key| {
try_output_recovery_with_ovk(
&domain,
key,
action,
action.cv_net(),
&action.encrypted_note().out_ciphertext,
)
.map(|(n, a, m)| (idx, key.clone(), n, a, m))
})
})
.collect()
}
/// Attempts to decrypt the action at the specified index with the specified
/// outgoing viewing key, and returns the decrypted note plaintext contents
/// if successful.
pub fn recover_output_with_ovk(
&self,
action_idx: usize,
key: &OutgoingViewingKey,
) -> Option<(Note, Address, [u8; 512])> {
self.actions.get(action_idx).and_then(move |action| {
let domain = OrchardDomain::for_action(action);
try_output_recovery_with_ovk(
&domain,
key,
action,
action.cv_net(),
&action.encrypted_note().out_ciphertext,
)
})
}
}
impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {

View File

@ -890,7 +890,7 @@ mod tests {
use group::GroupEncoding;
use halo2_proofs::dev::MockProver;
use pasta_curves::pallas;
use rand::rngs::OsRng;
use rand::{rngs::OsRng, RngCore};
use std::iter;
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
@ -901,65 +901,68 @@ mod tests {
value::{ValueCommitTrapdoor, ValueCommitment},
};
fn generate_circuit_instance<R: RngCore>(mut rng: R) -> (Circuit, Instance) {
let (_, fvk, spent_note) = Note::dummy(&mut rng, None);
let sender_address = spent_note.recipient();
let nk = *fvk.nk();
let rivk = *fvk.rivk();
let nf_old = spent_note.nullifier(&fvk);
let ak: SpendValidatingKey = fvk.into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old));
let cmx = output_note.commitment().into();
let value = spent_note.value() - output_note.value();
let rcv = ValueCommitTrapdoor::random(&mut rng);
let cv_net = ValueCommitment::derive(value.unwrap(), rcv.clone());
let path = MerklePath::dummy(&mut rng);
let anchor = path.root(spent_note.commitment().into());
(
Circuit {
path: Some(path.auth_path()),
pos: Some(path.position()),
g_d_old: Some(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()),
v_old: Some(spent_note.value()),
rho_old: Some(spent_note.rho()),
psi_old: Some(spent_note.rseed().psi(&spent_note.rho())),
rcm_old: Some(spent_note.rseed().rcm(&spent_note.rho())),
cm_old: Some(spent_note.commitment()),
alpha: Some(alpha),
ak: Some(ak),
nk: Some(nk),
rivk: Some(rivk),
g_d_new_star: Some((*output_note.recipient().g_d()).to_bytes()),
pk_d_new_star: Some(output_note.recipient().pk_d().to_bytes()),
v_new: Some(output_note.value()),
psi_new: Some(output_note.rseed().psi(&output_note.rho())),
rcm_new: Some(output_note.rseed().rcm(&output_note.rho())),
rcv: Some(rcv),
},
Instance {
anchor,
cv_net,
nf_old,
rk,
cmx,
enable_spend: true,
enable_output: true,
},
)
}
// TODO: recast as a proptest
#[test]
fn round_trip() {
let mut rng = OsRng;
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
.map(|()| {
let (_, fvk, spent_note) = Note::dummy(&mut rng, None);
let sender_address = spent_note.recipient();
let nk = *fvk.nk();
let rivk = *fvk.rivk();
let nf_old = spent_note.nullifier(&fvk);
let ak: SpendValidatingKey = fvk.into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old));
let cmx = output_note.commitment().into();
let value = spent_note.value() - output_note.value();
let cv_net = ValueCommitment::derive(value.unwrap(), ValueCommitTrapdoor::zero());
let path = MerklePath::dummy(&mut rng);
let anchor = path.root(spent_note.commitment().into());
(
Circuit {
path: Some(path.auth_path()),
pos: Some(path.position()),
g_d_old: Some(sender_address.g_d()),
pk_d_old: Some(*sender_address.pk_d()),
v_old: Some(spent_note.value()),
rho_old: Some(spent_note.rho()),
psi_old: Some(spent_note.rseed().psi(&spent_note.rho())),
rcm_old: Some(spent_note.rseed().rcm(&spent_note.rho())),
cm_old: Some(spent_note.commitment()),
alpha: Some(alpha),
ak: Some(ak),
nk: Some(nk),
rivk: Some(rivk),
g_d_new_star: Some((*output_note.recipient().g_d()).to_bytes()),
pk_d_new_star: Some(output_note.recipient().pk_d().to_bytes()),
v_new: Some(output_note.value()),
psi_new: Some(output_note.rseed().psi(&output_note.rho())),
rcm_new: Some(output_note.rseed().rcm(&output_note.rho())),
rcv: Some(ValueCommitTrapdoor::zero()),
},
Instance {
anchor,
cv_net,
nf_old,
rk,
cmx,
enable_spend: true,
enable_output: true,
},
)
})
.map(|()| generate_circuit_instance(&mut rng))
.unzip();
let vk = VerifyingKey::build();
@ -1009,6 +1012,93 @@ mod tests {
assert_eq!(proof.0.len(), expected_proof_size);
}
#[test]
fn serialized_proof_test_case() {
use std::convert::TryInto;
use std::io::{Read, Write};
let vk = VerifyingKey::build();
fn write_test_case<W: Write>(
mut w: W,
instance: &Instance,
proof: &Proof,
) -> std::io::Result<()> {
w.write_all(&instance.anchor.to_bytes())?;
w.write_all(&instance.cv_net.to_bytes())?;
w.write_all(&instance.nf_old.to_bytes())?;
w.write_all(&<[u8; 32]>::from(instance.rk.clone()))?;
w.write_all(&instance.cmx.to_bytes())?;
w.write_all(&[
if instance.enable_spend { 1 } else { 0 },
if instance.enable_output { 1 } else { 0 },
])?;
w.write_all(proof.as_ref())?;
Ok(())
}
fn read_test_case<R: Read>(mut r: R) -> std::io::Result<(Instance, Proof)> {
let read_32_bytes = |r: &mut R| {
let mut ret = [0u8; 32];
r.read_exact(&mut ret).unwrap();
ret
};
let read_bool = |r: &mut R| {
let mut byte = [0u8; 1];
r.read_exact(&mut byte).unwrap();
match byte {
[0] => false,
[1] => true,
_ => panic!("Unexpected non-boolean byte"),
}
};
let anchor = crate::Anchor::from_bytes(read_32_bytes(&mut r)).unwrap();
let cv_net = ValueCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap();
let nf_old = crate::note::Nullifier::from_bytes(&read_32_bytes(&mut r)).unwrap();
let rk = read_32_bytes(&mut r).try_into().unwrap();
let cmx =
crate::note::ExtractedNoteCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap();
let enable_spend = read_bool(&mut r);
let enable_output = read_bool(&mut r);
let instance =
Instance::from_parts(anchor, cv_net, nf_old, rk, cmx, enable_spend, enable_output);
let mut proof_bytes = vec![];
r.read_to_end(&mut proof_bytes)?;
let proof = Proof::new(proof_bytes);
Ok((instance, proof))
}
if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() {
let create_proof = || -> std::io::Result<()> {
let mut rng = OsRng;
let (circuit, instance) = generate_circuit_instance(OsRng);
let instances = &[instance.clone()];
let pk = ProvingKey::build();
let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap();
assert!(proof.verify(&vk, instances).is_ok());
let file = std::fs::File::create("circuit_proof_test_case.bin")?;
write_test_case(file, &instance, &proof)
};
create_proof().expect("should be able to write new proof");
}
// Parse the hardcoded proof test case.
let (instance, proof) = {
let test_case_bytes = include_bytes!("circuit_proof_test_case.bin");
read_test_case(&test_case_bytes[..]).expect("proof must be valid")
};
assert_eq!(proof.0.len(), 4992);
assert!(proof.verify(&vk, &[instance]).is_ok());
}
#[cfg(feature = "dev-graph")]
#[test]
fn print_action_circuit() {

Binary file not shown.