Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion benches/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion benches/note_decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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(),
);

Expand Down
28 changes: 24 additions & 4 deletions src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
}
Expand All @@ -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,
}
}

Expand All @@ -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].
///
Expand All @@ -102,6 +118,9 @@ impl Flags {
if self.outputs_enabled {
value |= FLAG_OUTPUTS_ENABLED;
}
if self.zsa_enabled {
value |= FLAG_ZSA_ENABLED;
}
value
}

Expand All @@ -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
Expand Down Expand Up @@ -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)
}
}

Expand Down
70 changes: 60 additions & 10 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use self::{
};
use crate::{
builder::SpendInfo,
bundle::Flags,
circuit::gadget::mux_chip::{MuxChip, MuxConfig},
constants::{
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
[
Expand Down Expand Up @@ -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),
),
],
)
Expand Down Expand Up @@ -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)
},
)?;
Expand Down Expand Up @@ -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 {
Expand All @@ -1015,22 +1033,22 @@ impl Instance {
nf_old: Nullifier,
rk: VerificationKey<SpendAuth>,
cmx: ExtractedNoteCommitment,
enable_spend: bool,
enable_output: bool,
flags: Flags,
) -> Self {
Instance {
anchor,
cv_net,
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();
Expand All @@ -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]
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1234,6 +1254,7 @@ mod tests {
cmx,
enable_spend: true,
enable_output: true,
enable_zsa: false,
},
)
}
Expand Down Expand Up @@ -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())?;
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -1533,6 +1562,7 @@ mod tests {
cmx,
enable_spend: true,
enable_output: true,
enable_zsa: true,
},
)
}
Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
}
}
}
Expand Down
Loading