diff --git a/Cargo.toml b/Cargo.toml index 0e7f64ea6..3ad6adfb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ plotters = { version = "0.3.0", optional = true } [dev-dependencies] bridgetree = "0.3" +half = ">= 1.8, < 2.3" criterion = "0.3" halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "zsa1", features = ["test-dependencies"] } hex = "0.4" diff --git a/src/circuit.rs b/src/circuit.rs index 99a35b1d9..a8164c7bc 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -224,12 +224,13 @@ impl plonk::Circuit for Circuit { // Constrain split_flag to be boolean // Constrain v_old * (1 - split_flag) - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). - // Constrain v_old = 0 or calculated root = anchor (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + // Constrain (v_old = 0 and split_flag = 0) or (calculated root = anchor) (https://p.z.cash/ZKS:action-merkle-path-validity?partial). // Constrain v_old = 0 or enable_spends = 1 (https://p.z.cash/ZKS:action-enable-spend). // Constrain v_new = 0 or enable_outputs = 1 (https://p.z.cash/ZKS:action-enable-output). // Constrain is_native_asset to be boolean // Constraint if is_native_asset = 1 then asset = native_asset else asset != native_asset // Constraint if split_flag = 0 then psi_old = psi_nf + // Constraint if split_flag = 1, then is_native_asset = 0 let q_orchard = meta.selector(); meta.create_gate("Orchard circuit checks", |meta| { let q_orchard = meta.query_selector(q_orchard); @@ -276,9 +277,13 @@ impl plonk::Circuit for Circuit { - v_new.clone() - magnitude * sign, ), + // We already checked that + // * split_flag is boolean (just above), and + // * v_old is a 64 bit integer (in the note commitment evaluation). + // So, split_flag + v_old = 0 only when (split_flag = 0 and v_old = 0), no overflow can occur. ( - "v_old = 0 or root = anchor", - v_old.clone() * (root - anchor), + "(v_old = 0 and split_flag = 0) or (root = anchor)", + (v_old.clone() + split_flag.clone()) * (root - anchor), ), ( "v_old = 0 or enable_spends = 1", @@ -307,13 +312,17 @@ impl plonk::Circuit for Circuit { // is not equal to zero, we will prove that it is invertible. ( "(is_native_asset = 0) => (asset != native_asset)", - (one.clone() - is_native_asset) + (one.clone() - is_native_asset.clone()) * (diff_asset_x * diff_asset_x_inv - one.clone()) * (diff_asset_y * diff_asset_y_inv - one.clone()), ), ( "(split_flag = 0) => (psi_old = psi_nf)", - (one - split_flag) * (psi_old - psi_nf), + (one - split_flag.clone()) * (psi_old - psi_nf), + ), + ( + "(split_flag = 1) => (is_native_asset = 0)", + split_flag * is_native_asset, ), ], ) @@ -1550,9 +1559,11 @@ mod tests { let (circuit, instance) = generate_circuit_instance(is_native_asset, split_flag, &mut rng); - check_proof_of_orchard_circuit(&circuit, &instance, true); + let should_pass = !(matches!((is_native_asset, split_flag), (true, true))); + + check_proof_of_orchard_circuit(&circuit, &instance, should_pass); - // Set cv_net to zero + // Set cv_net to be zero // The proof should fail let instance_wrong_cv_net = Instance { anchor: instance.anchor, @@ -1565,7 +1576,7 @@ mod tests { }; check_proof_of_orchard_circuit(&circuit, &instance_wrong_cv_net, false); - // Set rk_pub to dummy VerificationKey + // Set rk_pub to be a dummy VerificationKey // The proof should fail let instance_wrong_rk = Instance { anchor: instance.anchor, @@ -1578,7 +1589,7 @@ mod tests { }; check_proof_of_orchard_circuit(&circuit, &instance_wrong_rk, false); - // Set cm_old to random NoteCommitment + // Set cm_old to be a random NoteCommitment // The proof should fail let circuit_wrong_cm_old = Circuit { path: circuit.path, @@ -1606,7 +1617,7 @@ mod tests { }; check_proof_of_orchard_circuit(&circuit_wrong_cm_old, &instance, false); - // Set cmx_pub to random NoteCommitment + // Set cmx_pub to be a random NoteCommitment // The proof should fail let instance_wrong_cmx_pub = Instance { anchor: instance.anchor, @@ -1619,19 +1630,47 @@ mod tests { }; check_proof_of_orchard_circuit(&circuit, &instance_wrong_cmx_pub, false); - // If split_flag=0, set nf_old_pub to random Nullifier + // Set nf_old_pub to be a random Nullifier + // The proof should fail + let instance_wrong_nf_old_pub = Instance { + anchor: instance.anchor, + cv_net: instance.cv_net.clone(), + nf_old: Nullifier::dummy(&mut rng), + 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_nf_old_pub, false); + + // If split_flag = 0 , set psi_nf to be a random Pallas base element // 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, + let circuit_wrong_psi_nf = 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: circuit.cm_old.clone(), + psi_nf: Value::known(pallas::Base::random(&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, &instance_wrong_nf_old_pub, false); + check_proof_of_orchard_circuit(&circuit_wrong_psi_nf, &instance, false); } } } diff --git a/src/circuit_description b/src/circuit_description index 8078e15f1..863c4e1e2 100644 --- a/src/circuit_description +++ b/src/circuit_description @@ -260,13 +260,22 @@ PinnedVerificationKey { ), ), Product( - Advice { - query_index: 0, - column_index: 0, - rotation: Rotation( - 0, - ), - }, + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), Sum( Advice { query_index: 4, @@ -913,6 +922,79 @@ PinnedVerificationKey { ), ), ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), Product( Product( Product( diff --git a/src/circuit_proof_test_case.bin b/src/circuit_proof_test_case.bin index 695d163d9..5a8711554 100644 Binary files a/src/circuit_proof_test_case.bin and b/src/circuit_proof_test_case.bin differ