Circuit: Add tests for orchard circuit (#63)

Add some positive and negative tests for Orchard circuit
This commit is contained in:
Constance Beguier 2023-06-01 10:15:24 +02:00 committed by GitHub
parent 8e71fffc51
commit bedc732d6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 234 additions and 7 deletions

View File

@ -1188,20 +1188,24 @@ mod tests {
use core::iter;
use ff::Field;
use group::{Curve, Group, GroupEncoding};
use halo2_proofs::{circuit::Value, dev::MockProver};
use pasta_curves::pallas;
use rand::{rngs::OsRng, RngCore};
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
use crate::note::AssetBase;
use crate::builder::SpendInfo;
use crate::note::commitment::NoteCommitTrapdoor;
use crate::note::{AssetBase, Nullifier};
use crate::primitives::redpallas::VerificationKey;
use crate::{
keys::SpendValidatingKey,
note::Note,
keys::{FullViewingKey, Scope, SpendValidatingKey, SpendingKey},
note::{Note, NoteCommitment},
tree::MerklePath,
value::{ValueCommitTrapdoor, ValueCommitment},
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
};
fn generate_circuit_instance<R: RngCore>(mut rng: R) -> (Circuit, Instance) {
fn generate_dummy_circuit_instance<R: RngCore>(mut rng: R) -> (Circuit, Instance) {
let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetBase::native());
let sender_address = spent_note.recipient();
@ -1264,7 +1268,7 @@ mod tests {
let mut rng = OsRng;
let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(())
.map(|()| generate_circuit_instance(&mut rng))
.map(|()| generate_dummy_circuit_instance(&mut rng))
.unzip();
let vk = VerifyingKey::build();
@ -1378,7 +1382,7 @@ mod tests {
let create_proof = || -> std::io::Result<()> {
let mut rng = OsRng;
let (circuit, instance) = generate_circuit_instance(OsRng);
let (circuit, instance) = generate_dummy_circuit_instance(OsRng);
let instances = &[instance.clone()];
let pk = ProvingKey::build();
@ -1441,4 +1445,227 @@ mod tests {
.render(K, &circuit, &root)
.unwrap();
}
fn check_proof_of_orchard_circuit(circuit: &Circuit, instance: &Instance, should_pass: bool) {
let proof_verify = MockProver::run(
K,
circuit,
instance
.to_halo2_instance()
.iter()
.map(|p| p.to_vec())
.collect(),
)
.unwrap()
.verify();
if should_pass {
assert!(proof_verify.is_ok());
} else {
assert!(proof_verify.is_err());
}
}
fn generate_circuit_instance<R: RngCore>(
is_native_asset: bool,
split_flag: bool,
mut rng: R,
) -> (Circuit, Instance) {
// Create asset
let asset_base = if is_native_asset {
AssetBase::native()
} else {
AssetBase::random(&mut rng)
};
// Create spent_note
let (spent_note_fvk, spent_note) = {
let sk = SpendingKey::random(&mut rng);
let fvk: FullViewingKey = (&sk).into();
let sender_address = fvk.address_at(0u32, Scope::External);
let rho_old = Nullifier::dummy(&mut rng);
let spent_note = Note::new(
sender_address,
NoteValue::from_raw(40),
asset_base,
rho_old,
&mut rng,
);
(fvk, spent_note)
};
let output_value = NoteValue::from_raw(10);
let (dummy_sk, fvk, scope, nf_old, v_net) = if split_flag {
let sk = SpendingKey::random(&mut rng);
let fvk: FullViewingKey = (&sk).into();
(
Some(sk),
fvk.clone(),
Scope::External,
spent_note.nullifier(&fvk),
// Split notes do not contribute to v_net.
// Therefore, if split_flag is true, v_net = - output_value
NoteValue::zero() - output_value,
)
} else {
(
None,
spent_note_fvk.clone(),
spent_note_fvk
.scope_for_address(&spent_note.recipient())
.unwrap(),
spent_note.nullifier(&spent_note_fvk),
spent_note.value() - output_value,
)
};
let ak: SpendValidatingKey = fvk.clone().into();
let alpha = pallas::Scalar::random(&mut rng);
let rk = ak.randomize(&alpha);
let output_note = {
let sk = SpendingKey::random(&mut rng);
let fvk: FullViewingKey = (&sk).into();
let sender_address = fvk.address_at(0u32, Scope::External);
Note::new(sender_address, output_value, asset_base, nf_old, &mut rng)
};
let cmx = output_note.commitment().into();
let rcv = ValueCommitTrapdoor::random(&mut rng);
let cv_net = ValueCommitment::derive(v_net, rcv, asset_base);
let path = MerklePath::dummy(&mut rng);
let anchor = path.root(spent_note.commitment().into());
let spend_info = SpendInfo {
dummy_sk,
fvk,
scope,
note: spent_note,
merkle_path: path,
split_flag,
};
(
Circuit::from_action_context_unchecked(spend_info, output_note, alpha, rcv),
Instance {
anchor,
cv_net,
nf_old,
rk,
cmx,
enable_spend: true,
enable_output: true,
},
)
}
fn random_note_commitment(mut rng: impl RngCore) -> NoteCommitment {
NoteCommitment::derive(
pallas::Point::random(&mut rng).to_affine().to_bytes(),
pallas::Point::random(&mut rng).to_affine().to_bytes(),
NoteValue::from_raw(rng.next_u64()),
AssetBase::random(&mut rng),
pallas::Base::random(&mut rng),
pallas::Base::random(&mut rng),
NoteCommitTrapdoor(pallas::Scalar::random(&mut rng)),
)
.unwrap()
}
#[test]
fn orchard_circuit_negative_test() {
let mut rng = OsRng;
for is_native_asset in [true, false] {
for split_flag in [true, false] {
let (circuit, instance) =
generate_circuit_instance(is_native_asset, split_flag, &mut rng);
check_proof_of_orchard_circuit(&circuit, &instance, true);
// Set cv_net to zero
// The proof should fail
let instance_wrong_cv_net = Instance {
anchor: instance.anchor,
cv_net: ValueCommitment::from_bytes(&[0u8; 32]).unwrap(),
nf_old: instance.nf_old,
rk: instance.rk.clone(),
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_cv_net, false);
// Set rk_pub to dummy VerificationKey
// The proof should fail
let instance_wrong_rk = Instance {
anchor: instance.anchor,
cv_net: instance.cv_net.clone(),
nf_old: instance.nf_old,
rk: VerificationKey::dummy(),
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_rk, false);
// Set cm_old to random NoteCommitment
// The proof should fail
let circuit_wrong_cm_old = Circuit {
path: circuit.path,
pos: circuit.pos,
g_d_old: circuit.g_d_old,
pk_d_old: circuit.pk_d_old,
v_old: circuit.v_old,
rho_old: circuit.rho_old,
psi_old: circuit.psi_old,
rcm_old: circuit.rcm_old.clone(),
cm_old: Value::known(random_note_commitment(&mut rng)),
alpha: circuit.alpha,
ak: circuit.ak.clone(),
nk: circuit.nk,
rivk: circuit.rivk,
g_d_new: circuit.g_d_new,
pk_d_new: circuit.pk_d_new,
v_new: circuit.v_new,
psi_new: circuit.psi_new,
rcm_new: circuit.rcm_new.clone(),
rcv: circuit.rcv,
asset: circuit.asset,
split_flag: circuit.split_flag,
};
check_proof_of_orchard_circuit(&circuit_wrong_cm_old, &instance, false);
// Set cmx_pub to random NoteCommitment
// The proof should fail
let instance_wrong_cmx_pub = Instance {
anchor: instance.anchor,
cv_net: instance.cv_net.clone(),
nf_old: instance.nf_old,
rk: instance.rk.clone(),
cmx: random_note_commitment(&mut rng).into(),
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_cmx_pub, false);
// If split_flag=0, set nf_old_pub to random Nullifier
// The proof should fail
if !split_flag {
let instance_wrong_nf_old_pub = Instance {
anchor: instance.anchor,
cv_net: instance.cv_net,
nf_old: Nullifier::dummy(&mut rng),
rk: instance.rk,
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_nf_old_pub, false);
}
}
}
}
}