From 795fb78d2dd873dbaa42eb311d42f88875f9ffe4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 16 Mar 2022 19:47:00 +0000 Subject: [PATCH] Add serialized proof test case Co-authored-by: Sean Bowe --- src/circuit.rs | 196 +++++++++++++++++++++++--------- src/circuit_proof_test_case.bin | Bin 0 -> 5154 bytes 2 files changed, 142 insertions(+), 54 deletions(-) create mode 100644 src/circuit_proof_test_case.bin diff --git a/src/circuit.rs b/src/circuit.rs index e63917f5..4501cab2 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -941,7 +941,7 @@ mod tests { use group::GroupEncoding; use halo2::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}; @@ -952,65 +952,68 @@ mod tests { value::{ValueCommitTrapdoor, ValueCommitment}, }; + fn generate_circuit_instance(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(); @@ -1059,6 +1062,91 @@ 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( + 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(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 (circuit, instance) = generate_circuit_instance(OsRng); + let instances = &[instance.clone()]; + + let pk = ProvingKey::build(); + let proof = Proof::create(&pk, &[circuit], instances).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() { diff --git a/src/circuit_proof_test_case.bin b/src/circuit_proof_test_case.bin new file mode 100644 index 0000000000000000000000000000000000000000..79311047298386d690656a7f7d24ccd6d8a5f4fd GIT binary patch literal 5154 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 Q;A$1_QB~9n)XhksHeu7?!~g&Q literal 0 HcmV?d00001