mirror of https://github.com/zcash/orchard.git
Merge pull request #301 from zcash/non-consensus-changes-on-branchid-c4cd541e
Merge non-consensus changes
This commit is contained in:
commit
43d38f5b97
|
@ -9,13 +9,13 @@ use std::io;
|
||||||
use blake2b_simd::Hash as Blake2bHash;
|
use blake2b_simd::Hash as Blake2bHash;
|
||||||
use memuse::DynamicUsage;
|
use memuse::DynamicUsage;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use zcash_note_encryption::try_note_decryption;
|
use zcash_note_encryption::{try_note_decryption, try_output_recovery_with_ovk};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
address::Address,
|
address::Address,
|
||||||
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
|
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
|
||||||
circuit::{Instance, Proof, VerifyingKey},
|
circuit::{Instance, Proof, VerifyingKey},
|
||||||
keys::IncomingViewingKey,
|
keys::{IncomingViewingKey, OutgoingViewingKey},
|
||||||
note::{ExtractedNoteCommitment, Note, Nullifier, TransmittedNoteCiphertext},
|
note::{ExtractedNoteCommitment, Note, Nullifier, TransmittedNoteCiphertext},
|
||||||
note_encryption::OrchardDomain,
|
note_encryption::OrchardDomain,
|
||||||
primitives::redpallas::{self, Binding, SpendAuth},
|
primitives::redpallas::{self, Binding, SpendAuth},
|
||||||
|
@ -364,10 +364,11 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform trial decryption of each action in the bundle with each of the
|
/// Performs trial decryption of each action in the bundle with each of the
|
||||||
/// specified incoming viewing keys, and return the decrypted note contents
|
/// specified incoming viewing keys, and returns a vector of each decrypted
|
||||||
/// along with the index of the action from which it was derived.
|
/// note plaintext contents along with the index of the action from which it
|
||||||
pub fn decrypt_outputs_for_keys(
|
/// was derived.
|
||||||
|
pub fn decrypt_outputs_with_keys(
|
||||||
&self,
|
&self,
|
||||||
keys: &[IncomingViewingKey],
|
keys: &[IncomingViewingKey],
|
||||||
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
|
) -> Vec<(usize, IncomingViewingKey, Note, Address, [u8; 512])> {
|
||||||
|
@ -384,8 +385,9 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform trial decryption of each action at `action_idx` in the bundle with the
|
/// Performs trial decryption of the action at `action_idx` in the bundle with the
|
||||||
/// specified incoming viewing key, and return the decrypted note contents.
|
/// specified incoming viewing key, and returns the decrypted note plaintext
|
||||||
|
/// contents if successful.
|
||||||
pub fn decrypt_output_with_key(
|
pub fn decrypt_output_with_key(
|
||||||
&self,
|
&self,
|
||||||
action_idx: usize,
|
action_idx: usize,
|
||||||
|
@ -396,6 +398,53 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
try_note_decryption(&domain, key, action)
|
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> {
|
impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {
|
||||||
|
|
198
src/circuit.rs
198
src/circuit.rs
|
@ -890,7 +890,7 @@ mod tests {
|
||||||
use group::GroupEncoding;
|
use group::GroupEncoding;
|
||||||
use halo2_proofs::dev::MockProver;
|
use halo2_proofs::dev::MockProver;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
use rand::rngs::OsRng;
|
use rand::{rngs::OsRng, RngCore};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
|
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
|
||||||
|
@ -901,65 +901,68 @@ mod tests {
|
||||||
value::{ValueCommitTrapdoor, ValueCommitment},
|
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
|
// TODO: recast as a proptest
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip() {
|
fn round_trip() {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
|
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
|
||||||
.map(|()| {
|
.map(|()| generate_circuit_instance(&mut rng))
|
||||||
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,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
let vk = VerifyingKey::build();
|
let vk = VerifyingKey::build();
|
||||||
|
@ -1009,6 +1012,93 @@ mod tests {
|
||||||
assert_eq!(proof.0.len(), expected_proof_size);
|
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")]
|
#[cfg(feature = "dev-graph")]
|
||||||
#[test]
|
#[test]
|
||||||
fn print_action_circuit() {
|
fn print_action_circuit() {
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue