Circuit: Add enable_zsa flag (#79)

When enable_zsa flag is set to false, it is not possible to perform ZSA transactions (the circuit will fail).

Fix the version of reddsa (=0.5.0) because recent versions required rust version 1.65 or newer
Fix the version of tempfile (=3.5.0) because recent versions required rust version 1.63 or newer
Limit the version of flate2 (<1.0.27) because recent versions raise some clippy issues
This commit is contained in:
Constance Beguier 2023-08-31 09:23:44 +02:00 committed by GitHub
parent 081513b363
commit 139ecca079
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 567 additions and 408 deletions

View File

@ -35,9 +35,10 @@ hex = "0.4"
lazy_static = "1"
memuse = { version = "0.2.1", features = ["nonempty"] }
pasta_curves = "0.5"
tempfile = "= 3.5.0" # Last version required rust 1.63
proptest = { version = "1.0.0", optional = true }
rand = "0.8"
reddsa = "0.5"
reddsa = "=0.5.0" # Last version required rust 1.65
nonempty = "0.7"
serde = { version = "1.0", features = ["derive"] }
subtle = "2.3"
@ -49,6 +50,7 @@ tracing = "0.1"
# Developer tooling dependencies
image = { version = ">= 0.24, < 0.24.5", optional = true } # 0.24.5 has MSRV 1.61
flate2 = ">= 1.0, <1.0.27" # Clippy issues in last version
plotters = { version = "0.3.0", optional = true }
[dev-dependencies]

View File

@ -28,7 +28,7 @@ fn criterion_benchmark(c: &mut Criterion) {
let create_bundle = |num_recipients| {
let mut builder = Builder::new(
Flags::from_parts(true, true),
Flags::from_parts(true, true, false),
Anchor::from_bytes([0; 32]).unwrap(),
);
for _ in 0..num_recipients {

View File

@ -47,7 +47,7 @@ fn bench_note_decryption(c: &mut Criterion) {
let bundle = {
let mut builder = Builder::new(
Flags::from_parts(true, true),
Flags::from_parts(true, true, false),
Anchor::from_bytes([0; 32]).unwrap(),
);
// The builder pads to two actions, and shuffles their order. Add two recipients

View File

@ -947,7 +947,7 @@ pub mod testing {
/// Create a bundle from the set of arbitrary bundle inputs.
fn into_bundle<V: TryFrom<i64> + Copy + Into<i64>>(mut self) -> Bundle<Authorized, V> {
let fvk = FullViewingKey::from(&self.sk);
let flags = Flags::from_parts(true, true);
let flags = Flags::from_parts(true, true, true);
let mut builder = Builder::new(flags, self.anchor);
for (note, path) in self.notes.into_iter() {
@ -1068,7 +1068,7 @@ mod tests {
let recipient = fvk.address_at(0u32, Scope::External);
let mut builder = Builder::new(
Flags::from_parts(true, true),
Flags::from_parts(true, true, false),
EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(),
);

View File

@ -38,6 +38,7 @@ impl<T> Action<T> {
cmx: *self.cmx(),
enable_spend: flags.spends_enabled,
enable_output: flags.outputs_enabled,
enable_zsa: flags.zsa_enabled,
}
}
}
@ -57,18 +58,25 @@ pub struct Flags {
/// guaranteed to be dummy notes. If `true`, the created notes may be either real or
/// dummy notes.
outputs_enabled: bool,
/// Flag denoting whether ZSA transaction is enabled.
///
/// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be notes with native asset.
zsa_enabled: bool,
}
const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001;
const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010;
const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED);
const FLAG_ZSA_ENABLED: u8 = 0b0000_0100;
const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_ZSA_ENABLED);
impl Flags {
/// Construct a set of flags from its constituent parts
pub fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self {
pub fn from_parts(spends_enabled: bool, outputs_enabled: bool, zsa_enabled: bool) -> Self {
Flags {
spends_enabled,
outputs_enabled,
zsa_enabled,
}
}
@ -90,6 +98,14 @@ impl Flags {
self.outputs_enabled
}
/// Flag denoting whether ZSA transaction is enabled.
///
/// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are
/// guaranteed to be notes with native asset.
pub fn zsa_enabled(&self) -> bool {
self.zsa_enabled
}
/// Serialize flags to a byte as defined in [Zcash Protocol Spec § 7.1: Transaction
/// Encoding And Consensus][txencoding].
///
@ -102,6 +118,9 @@ impl Flags {
if self.outputs_enabled {
value |= FLAG_OUTPUTS_ENABLED;
}
if self.zsa_enabled {
value |= FLAG_ZSA_ENABLED;
}
value
}
@ -116,6 +135,7 @@ impl Flags {
Some(Self::from_parts(
value & FLAG_SPENDS_ENABLED != 0,
value & FLAG_OUTPUTS_ENABLED != 0,
value & FLAG_ZSA_ENABLED != 0,
))
} else {
None
@ -607,8 +627,8 @@ pub mod testing {
prop_compose! {
/// Create an arbitrary set of flags.
pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY) -> Flags {
Flags::from_parts(spends_enabled, outputs_enabled)
pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY, zsa_enabled in prop::bool::ANY) -> Flags {
Flags::from_parts(spends_enabled, outputs_enabled, zsa_enabled)
}
}

View File

@ -27,6 +27,7 @@ use self::{
};
use crate::{
builder::SpendInfo,
bundle::Flags,
circuit::gadget::mux_chip::{MuxChip, MuxConfig},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
@ -79,6 +80,7 @@ const RK_Y: usize = 5;
const CMX: usize = 6;
const ENABLE_SPEND: usize = 7;
const ENABLE_OUTPUT: usize = 8;
const ENABLE_ZSA: usize = 9;
/// Configuration needed to use the Orchard Action circuit.
#[derive(Clone, Debug)]
@ -231,6 +233,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// 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
// Constraint if enable_zsa = 0, then is_native_asset = 1
let q_orchard = meta.selector();
meta.create_gate("Orchard circuit checks", |meta| {
let q_orchard = meta.query_selector(q_orchard);
@ -267,6 +270,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let psi_old = meta.query_advice(advices[4], Rotation::next());
let psi_nf = meta.query_advice(advices[5], Rotation::next());
let enable_zsa = meta.query_advice(advices[6], Rotation::next());
Constraints::with_selector(
q_orchard,
[
@ -318,11 +323,15 @@ impl plonk::Circuit<pallas::Base> for Circuit {
),
(
"(split_flag = 0) => (psi_old = psi_nf)",
(one - split_flag.clone()) * (psi_old - psi_nf),
(one.clone() - split_flag.clone()) * (psi_old - psi_nf),
),
(
"(split_flag = 1) => (is_native_asset = 0)",
split_flag * is_native_asset,
split_flag * is_native_asset.clone(),
),
(
"(enable_zsa = 0) => (is_native_asset = 1)",
(one.clone() - enable_zsa) * (one - is_native_asset),
),
],
)
@ -942,6 +951,14 @@ impl plonk::Circuit<pallas::Base> for Circuit {
psi_old.copy_advice(|| "psi_old", &mut region, config.advices[4], 1)?;
psi_nf.copy_advice(|| "psi_nf", &mut region, config.advices[5], 1)?;
region.assign_advice_from_instance(
|| "enable zsa",
config.primary,
ENABLE_ZSA,
config.advices[6],
1,
)?;
config.q_orchard.enable(&mut region, 0)
},
)?;
@ -999,6 +1016,7 @@ pub struct Instance {
pub(crate) cmx: ExtractedNoteCommitment,
pub(crate) enable_spend: bool,
pub(crate) enable_output: bool,
pub(crate) enable_zsa: bool,
}
impl Instance {
@ -1015,8 +1033,7 @@ impl Instance {
nf_old: Nullifier,
rk: VerificationKey<SpendAuth>,
cmx: ExtractedNoteCommitment,
enable_spend: bool,
enable_output: bool,
flags: Flags,
) -> Self {
Instance {
anchor,
@ -1024,13 +1041,14 @@ impl Instance {
nf_old,
rk,
cmx,
enable_spend,
enable_output,
enable_spend: flags.spends_enabled(),
enable_output: flags.outputs_enabled(),
enable_zsa: flags.zsa_enabled(),
}
}
fn to_halo2_instance(&self) -> [[vesta::Scalar; 9]; 1] {
let mut instance = [vesta::Scalar::zero(); 9];
fn to_halo2_instance(&self) -> [[vesta::Scalar; 10]; 1] {
let mut instance = [vesta::Scalar::zero(); 10];
instance[ANCHOR] = self.anchor.inner();
instance[CV_NET_X] = self.cv_net.x();
@ -1048,6 +1066,7 @@ impl Instance {
instance[CMX] = self.cmx.inner();
instance[ENABLE_SPEND] = vesta::Scalar::from(u64::from(self.enable_spend));
instance[ENABLE_OUTPUT] = vesta::Scalar::from(u64::from(self.enable_output));
instance[ENABLE_ZSA] = vesta::Scalar::from(u64::from(self.enable_zsa));
[instance]
}
@ -1167,6 +1186,7 @@ mod tests {
use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K};
use crate::builder::SpendInfo;
use crate::bundle::Flags;
use crate::note::commitment::NoteCommitTrapdoor;
use crate::note::{AssetBase, Nullifier};
use crate::primitives::redpallas::VerificationKey;
@ -1234,6 +1254,7 @@ mod tests {
cmx,
enable_spend: true,
enable_output: true,
enable_zsa: false,
},
)
}
@ -1314,6 +1335,7 @@ mod tests {
w.write_all(&[
if instance.enable_spend { 1 } else { 0 },
if instance.enable_output { 1 } else { 0 },
if instance.enable_zsa { 1 } else { 0 },
])?;
w.write_all(proof.as_ref())?;
@ -1344,8 +1366,15 @@ mod tests {
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 enable_zsa = read_bool(&mut r);
let instance = Instance::from_parts(
anchor,
cv_net,
nf_old,
rk,
cmx,
Flags::from_parts(enable_spend, enable_output, enable_zsa),
);
let mut proof_bytes = vec![];
r.read_to_end(&mut proof_bytes)?;
@ -1533,6 +1562,7 @@ mod tests {
cmx,
enable_spend: true,
enable_output: true,
enable_zsa: true,
},
)
}
@ -1573,6 +1603,7 @@ mod tests {
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
enable_zsa: instance.enable_zsa,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_cv_net, false);
@ -1586,6 +1617,7 @@ mod tests {
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
enable_zsa: instance.enable_zsa,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_rk, false);
@ -1627,6 +1659,7 @@ mod tests {
cmx: random_note_commitment(&mut rng).into(),
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
enable_zsa: instance.enable_zsa,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_cmx_pub, false);
@ -1640,6 +1673,7 @@ mod tests {
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
enable_zsa: instance.enable_zsa,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_nf_old_pub, false);
@ -1672,6 +1706,22 @@ mod tests {
};
check_proof_of_orchard_circuit(&circuit_wrong_psi_nf, &instance, false);
}
// If asset is not equal to the native asset, set enable_zsa = 0
// The proof should fail
if !is_native_asset {
let instance_wrong_enable_zsa = Instance {
anchor: instance.anchor,
cv_net: instance.cv_net.clone(),
nf_old: instance.nf_old,
rk: instance.rk.clone(),
cmx: instance.cmx,
enable_spend: instance.enable_spend,
enable_output: instance.enable_output,
enable_zsa: false,
};
check_proof_of_orchard_circuit(&circuit, &instance_wrong_enable_zsa, false);
}
}
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -62,7 +62,7 @@ fn bundle_chain() {
// Use the empty tree.
let anchor = MerkleHashOrchard::empty_root(32.into()).into();
let mut builder = Builder::new(Flags::from_parts(false, true), anchor);
let mut builder = Builder::new(Flags::from_parts(false, true, false), anchor);
assert_eq!(
builder.add_recipient(
None,
@ -96,7 +96,7 @@ fn bundle_chain() {
let (merkle_path, anchor) = build_merkle_path(&note);
let mut builder = Builder::new(Flags::from_parts(true, true), anchor);
let mut builder = Builder::new(Flags::from_parts(true, true, false), anchor);
assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(()));
assert_eq!(
builder.add_recipient(

View File

@ -189,7 +189,7 @@ fn create_native_note(keys: &Keychain) -> Note {
// Use the empty tree.
let anchor = MerkleHashOrchard::empty_root(32.into()).into();
let mut builder = Builder::new(Flags::from_parts(false, true), anchor);
let mut builder = Builder::new(Flags::from_parts(false, true, false), anchor);
assert_eq!(
builder.add_recipient(
None,
@ -244,7 +244,7 @@ fn build_and_verify_bundle(
) -> Result<(), String> {
let rng = OsRng;
let shielded_bundle: Bundle<_, i64> = {
let mut builder = Builder::new(Flags::from_parts(true, true), anchor);
let mut builder = Builder::new(Flags::from_parts(true, true, true), anchor);
spends
.iter()