Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2bf081f
Simplify circuit note commiment test
ConstanceBeguier Apr 14, 2023
d7faec1
Update Cargo.toml
ConstanceBeguier Apr 25, 2023
ab1cdff
Add ZSA note commitment constants
ConstanceBeguier Apr 17, 2023
b19f012
Split note commitment into hash and blind
ConstanceBeguier Apr 17, 2023
adfbadd
Add Mux chip
ConstanceBeguier Apr 19, 2023
bc34b39
Add is_native_asset
ConstanceBeguier Apr 25, 2023
ba70c15
Use R_ZEC for ZSA note commitment
ConstanceBeguier Apr 20, 2023
3a1b71c
Use is_native_asset to evaluate the note commitment with the correct …
ConstanceBeguier Apr 25, 2023
8cb0950
Add asset into hash for ZSA note commitment
ConstanceBeguier Apr 25, 2023
da0e076
Add test on ZSA note commitment
ConstanceBeguier Apr 26, 2023
90e5e00
Update constraints for note commitment
ConstanceBeguier Apr 26, 2023
e809f7f
Check that is_native_asset is boolean
ConstanceBeguier Apr 26, 2023
7cb5250
Update some comments
ConstanceBeguier Apr 26, 2023
b71430a
Fix constraint is_native_asset * (asset - native_asset) = 0
ConstanceBeguier Apr 27, 2023
201b049
Update comments
ConstanceBeguier Apr 27, 2023
87112af
Set verify_proof to true in tests/zsa.rs
ConstanceBeguier Apr 27, 2023
4ed6c64
Add a comment on a possible optimization
ConstanceBeguier Apr 27, 2023
add2023
Update some comments in the MUX chip
ConstanceBeguier May 2, 2023
289213d
update comments
ConstanceBeguier May 4, 2023
e2addc6
Fix constraints on is_native_asset
ConstanceBeguier May 4, 2023
8cad322
Fix is_native_asset constraints
ConstanceBeguier May 4, 2023
a3cc310
Update diff_asset_x_inv and diff_asset_y_inv
ConstanceBeguier May 9, 2023
4dda8eb
Fix clippy warnings
ConstanceBeguier May 9, 2023
a98b2c2
Update some comments
ConstanceBeguier May 16, 2023
2637883
Add AssetBase::random function
ConstanceBeguier May 16, 2023
ebd7b64
Add a function assign_is_native_asset
ConstanceBeguier May 16, 2023
7793bf5
Update comment for H decomposition
ConstanceBeguier May 22, 2023
22ad693
Rename h_2 by h_2_zsa
ConstanceBeguier May 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ debug = true
[patch.crates-io]
zcash_note_encryption = { git = "https://github.com/QED-it/librustzcash.git", rev = "07c377ddedf71ab7c7a266d284b054a2dafc2ed4" }
bridgetree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "ea1686e8f8f6c1e41aa97251a7eb4fadfd33df47" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "ea1686e8f8f6c1e41aa97251a7eb4fadfd33df47" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "ea1686e8f8f6c1e41aa97251a7eb4fadfd33df47" }
165 changes: 148 additions & 17 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use core::fmt;

use ff::Field;
use group::{Curve, GroupEncoding};
use halo2_proofs::{
circuit::{floor_planner, Layouter, Value},
Expand All @@ -20,12 +21,13 @@ use self::{
commit_ivk::{CommitIvkChip, CommitIvkConfig},
gadget::{
add_chip::{AddChip, AddConfig},
assign_free_advice,
assign_free_advice, assign_is_native_asset,
},
note_commit::{NoteCommitChip, NoteCommitConfig},
};
use crate::{
builder::SpendInfo,
circuit::gadget::mux_chip::{MuxChip, MuxConfig},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
MERKLE_DEPTH_ORCHARD,
Expand Down Expand Up @@ -65,7 +67,7 @@ mod note_commit;
mod value_commit_orchard;

/// Size of the Orchard circuit.
const K: u32 = 11;
const K: u32 = 12;

// Absolute offsets for public inputs.
const ANCHOR: usize = 0;
Expand Down Expand Up @@ -96,6 +98,7 @@ pub struct Config {
commit_ivk_config: CommitIvkConfig,
old_note_commit_config: NoteCommitConfig,
new_note_commit_config: NoteCommitConfig,
mux_config: MuxConfig,
}

/// The Orchard Action circuit.
Expand Down Expand Up @@ -220,6 +223,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// 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 split_flag = 1 or nf_old = nf_old_pub
// Constrain is_native_asset to be boolean
// Constraint if is_native_asset = 1 then asset = native_asset else asset != native_asset
let q_orchard = meta.selector();
meta.create_gate("Orchard circuit checks", |meta| {
let q_orchard = meta.query_selector(q_orchard);
Expand All @@ -239,8 +244,23 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let nf_old = meta.query_advice(advices[9], Rotation::cur());
let nf_old_pub = meta.query_advice(advices[0], Rotation::next());

let is_native_asset = meta.query_advice(advices[1], Rotation::next());
let asset_x = meta.query_advice(advices[2], Rotation::next());
let asset_y = meta.query_advice(advices[3], Rotation::next());
let diff_asset_x_inv = meta.query_advice(advices[4], Rotation::next());
let diff_asset_y_inv = meta.query_advice(advices[5], Rotation::next());

let one = Expression::Constant(pallas::Base::one());

let native_asset = AssetBase::native()
.cv_base()
.to_affine()
.coordinates()
.unwrap();

let diff_asset_x = asset_x - Expression::Constant(*native_asset.x());
let diff_asset_y = asset_y - Expression::Constant(*native_asset.y());

Constraints::with_selector(
q_orchard,
[
Expand All @@ -265,7 +285,30 @@ impl plonk::Circuit<pallas::Base> for Circuit {
),
(
"split_flag = 1 or nf_old = nf_old_pub",
(one - split_flag) * (nf_old - nf_old_pub),
(one.clone() - split_flag) * (nf_old - nf_old_pub),
),
(
"bool_check is_native_asset",
bool_check(is_native_asset.clone()),
),
(
"(is_native_asset = 1) => (asset_x = native_asset_x)",
is_native_asset.clone() * diff_asset_x.clone(),
),
(
"(is_native_asset = 1) => (asset_y = native_asset_y)",
is_native_asset.clone() * diff_asset_y.clone(),
),
// To prove that `asset` is not equal to `native_asset`, we will prove that at
// least one of `x(asset) - x(native_asset)` or `y(asset) - y(native_asset)` is
// not equal to zero.
// To prove that `x(asset) - x(native_asset)` (resp `y(asset) - y(native_asset)`)
// is not equal to zero, we will prove that it is invertible.
(
"(is_native_asset = 0) => (asset != native_asset)",
(one.clone() - is_native_asset)
* (diff_asset_x * diff_asset_x_inv - one.clone())
* (diff_asset_y * diff_asset_y_inv - one),
),
],
)
Expand Down Expand Up @@ -383,6 +426,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let new_note_commit_config =
NoteCommitChip::configure(meta, advices, sinsemilla_config_2.clone());

let mux_config = MuxChip::configure(meta, advices[0], advices[1], advices[2], advices[3]);

Config {
primary,
q_orchard,
Expand All @@ -397,6 +442,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
commit_ivk_config,
old_note_commit_config,
new_note_commit_config,
mux_config,
}
}

Expand All @@ -413,7 +459,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let ecc_chip = config.ecc_chip();

// Witness private inputs that are used across multiple checks.
let (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) = {
let (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new, asset) = {
// Witness psi_old
let psi_old = assign_free_advice(
layouter.namespace(|| "witness psi_old"),
Expand Down Expand Up @@ -471,9 +517,27 @@ impl plonk::Circuit<pallas::Base> for Circuit {
self.v_new,
)?;

(psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new)
// Witness asset
let asset = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness asset"),
self.asset.map(|asset| asset.cv_base().to_affine()),
)?;

(
psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new, asset,
)
};

// Witness is_native_asset which is equal to
// 1 if asset is equal to native asset, and
// 0 if asset is not equal to native asset.
let is_native_asset = assign_is_native_asset(
layouter.namespace(|| "witness is_native_asset"),
config.advices[0],
self.asset,
)?;

// Merkle path validity check (https://p.z.cash/ZKS:action-merkle-path-validity?partial).
let root = {
let path = self
Expand Down Expand Up @@ -537,19 +601,13 @@ impl plonk::Circuit<pallas::Base> for Circuit {
self.rcv.as_ref().map(|rcv| rcv.inner()),
)?;

let asset = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness asset"),
self.asset.map(|asset| asset.cv_base().to_affine()),
)?;

let cv_net = gadget::value_commit_orchard(
layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net_magnitude_sign)"),
config.sinsemilla_chip_1(),
ecc_chip.clone(),
v_net_magnitude_sign.clone(),
rcv,
asset,
asset.clone(),
)?;

// Constrain cv_net to equal public input
Expand Down Expand Up @@ -658,12 +716,15 @@ impl plonk::Circuit<pallas::Base> for Circuit {
config.sinsemilla_chip_1(),
config.ecc_chip(),
config.note_commit_chip_old(),
config.mux_chip(),
g_d_old.inner(),
pk_d_old.inner(),
v_old.clone(),
rho_old,
psi_old,
asset.inner(),
rcm_old,
is_native_asset.clone(),
)?;

// Constrain derived cm_old to equal witnessed cm_old
Expand Down Expand Up @@ -716,12 +777,15 @@ impl plonk::Circuit<pallas::Base> for Circuit {
config.sinsemilla_chip_2(),
config.ecc_chip(),
config.note_commit_chip_new(),
config.mux_chip(),
g_d_new.inner(),
pk_d_new.inner(),
v_new.clone(),
rho_new,
psi_new,
asset.inner(),
rcm_new,
is_native_asset.clone(),
)?;

let cmx = cm_new.extract_p();
Expand Down Expand Up @@ -795,6 +859,72 @@ impl plonk::Circuit<pallas::Base> for Circuit {
1,
)?;

is_native_asset.copy_advice(
|| "is_native_asset",
&mut region,
config.advices[1],
1,
)?;
asset
.inner()
.x()
.copy_advice(|| "asset_x", &mut region, config.advices[2], 1)?;
asset
.inner()
.y()
.copy_advice(|| "asset_y", &mut region, config.advices[3], 1)?;

// `diff_asset_x_inv` and `diff_asset_y_inv` will be used to prove that
// if is_native_asset = 0, then asset != native_asset.
region.assign_advice(
|| "diff_asset_x_inv",
config.advices[4],
1,
|| {
self.asset.map(|asset| {
let asset_x = *asset.cv_base().to_affine().coordinates().unwrap().x();
let native_asset_x = *AssetBase::native()
.cv_base()
.to_affine()
.coordinates()
.unwrap()
.x();

let diff_asset_x = asset_x - native_asset_x;

if diff_asset_x == pallas::Base::zero() {
pallas::Base::zero()
} else {
diff_asset_x.invert().unwrap()
}
})
},
)?;
region.assign_advice(
|| "diff_asset_y_inv",
config.advices[5],
1,
|| {
self.asset.map(|asset| {
let asset_y = *asset.cv_base().to_affine().coordinates().unwrap().y();
let native_asset_y = *AssetBase::native()
.cv_base()
.to_affine()
.coordinates()
.unwrap()
.y();

let diff_asset_y = asset_y - native_asset_y;

if diff_asset_y == pallas::Base::zero() {
pallas::Base::zero()
} else {
diff_asset_y.invert().unwrap()
}
})
},
)?;

config.q_orchard.enable(&mut region, 0)
},
)?;
Expand Down Expand Up @@ -1111,8 +1241,8 @@ mod tests {
K,
&circuits[0],
);
assert_eq!(usize::from(circuit_cost.proof_size(1)), 5024);
assert_eq!(usize::from(circuit_cost.proof_size(2)), 7296);
assert_eq!(usize::from(circuit_cost.proof_size(1)), 5088);
assert_eq!(usize::from(circuit_cost.proof_size(2)), 7360);
usize::from(circuit_cost.proof_size(instances.len()))
};

Expand Down Expand Up @@ -1141,6 +1271,7 @@ mod tests {

#[test]
fn serialized_proof_test_case() {
use std::fs;
use std::io::{Read, Write};

let vk = VerifyingKey::build();
Expand Down Expand Up @@ -1209,18 +1340,18 @@ mod tests {
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")?;
let file = std::fs::File::create("src/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");
let test_case_bytes = fs::read("src/circuit_proof_test_case.bin").unwrap();
read_test_case(&test_case_bytes[..]).expect("proof must be valid")
};
assert_eq!(proof.0.len(), 5024);
assert_eq!(proof.0.len(), 5088);

assert!(proof.verify(&vk, &[instance]).is_ok());
}
Expand Down
28 changes: 28 additions & 0 deletions src/circuit/gadget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use pasta_curves::pallas;

use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip};
use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
use crate::note::AssetBase;
use halo2_gadgets::{
ecc::{chip::EccChip, EccInstructions, FixedPointBaseField, Point, X},
poseidon::{
Expand All @@ -19,6 +20,7 @@ use halo2_proofs::{
};

pub(in crate::circuit) mod add_chip;
pub(in crate::circuit) mod mux_chip;

impl super::Config {
pub(super) fn add_chip(&self) -> add_chip::AddChip {
Expand Down Expand Up @@ -68,6 +70,10 @@ impl super::Config {
pub(super) fn note_commit_chip_old(&self) -> NoteCommitChip {
NoteCommitChip::construct(self.old_note_commit_config.clone())
}

pub(super) fn mux_chip(&self) -> mux_chip::MuxChip {
mux_chip::MuxChip::construct(self.mux_config.clone())
}
}

/// An instruction set for adding two circuit words (field elements).
Expand Down Expand Up @@ -100,6 +106,28 @@ where
)
}

/// Witnesses is_native_asset.
pub(in crate::circuit) fn assign_is_native_asset<F: Field>(
layouter: impl Layouter<F>,
column: Column<Advice>,
asset: Value<AssetBase>,
) -> Result<AssignedCell<pasta_curves::Fp, F>, plonk::Error>
where
Assigned<F>: for<'v> From<&'v pasta_curves::Fp>,
{
assign_free_advice(
layouter,
column,
asset.map(|asset| {
if bool::from(asset.is_native()) {
pallas::Base::one()
} else {
pallas::Base::zero()
}
}),
)
}

/// `DeriveNullifier` from [Section 4.16: Note Commitments and Nullifiers].
///
/// [Section 4.16: Note Commitments and Nullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers
Expand Down
Loading