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
18 changes: 6 additions & 12 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,26 +167,20 @@ impl SpendInfo {
}
}

/// Creates a split spend, which is identical to origin normal spend except that we use a random
/// fvk to generate a different nullifier. In addition, the split_flag is raised.
/// Creates a split spend, which is identical to origin normal spend except that
/// `rseed_split_note` contains a random seed. In addition, the split_flag is raised.
///
/// Defined in [Transfer and Burn of Zcash Shielded Assets ZIP-0226 § Split Notes (DRAFT PR)][TransferZSA].
///
/// [TransferZSA]: https://qed-it.github.io/zips/zip-0226.html#split-notes
fn create_split_spend(&self, rng: &mut impl RngCore) -> Self {
let note = self.note;
let merkle_path = self.merkle_path.clone();

let sk = SpendingKey::random(rng);
let fvk: FullViewingKey = (&sk).into();

SpendInfo {
dummy_sk: Some(sk),
fvk,
dummy_sk: None,
fvk: self.fvk.clone(),
// We use external scope to avoid unnecessary derivations
scope: Scope::External,
note,
merkle_path,
note: self.note.create_split_note(rng),
merkle_path: self.merkle_path.clone(),
split_flag: true,
}
}
Expand Down
1 change: 0 additions & 1 deletion src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ pub struct Bundle<T: Authorization, V> {
/// This is the sum of Orchard spends minus the sum of Orchard outputs.
value_balance: V,
/// Assets intended for burning
/// TODO We need to add a consensus check to make sure that it is impossible to burn ZEC.
burn: Vec<(AssetBase, V)>,
/// The root of the Orchard commitment tree that this bundle commits to.
anchor: Anchor,
Expand Down
192 changes: 80 additions & 112 deletions src/circuit.rs

Large diffs are not rendered by default.

60 changes: 54 additions & 6 deletions src/circuit/gadget.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Gadgets used in the Orchard circuit.

use ff::Field;
use group::Curve;
use pasta_curves::arithmetic::CurveExt;
use pasta_curves::pallas;

use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip};
use crate::circuit::gadget::mux_chip::{MuxChip, MuxInstructions};
use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains};
use crate::note::AssetBase;
use halo2_gadgets::{
ecc::{chip::EccChip, EccInstructions, FixedPointBaseField, Point, X},
ecc::{chip::EccChip, chip::EccPoint, EccInstructions, FixedPointBaseField, Point, X},
poseidon::{
primitives::{self as poseidon, ConstantLength},
Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip,
Expand Down Expand Up @@ -128,6 +131,28 @@ where
)
}

/// Witnesses split_flag.
pub(in crate::circuit) fn assign_split_flag<F: Field>(
layouter: impl Layouter<F>,
column: Column<Advice>,
split_flag: Value<bool>,
) -> Result<AssignedCell<pasta_curves::Fp, F>, plonk::Error>
where
Assigned<F>: for<'v> From<&'v pasta_curves::Fp>,
{
assign_free_advice(
layouter,
column,
split_flag.map(|split_flag| {
if split_flag {
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 All @@ -138,17 +163,20 @@ pub(in crate::circuit) fn derive_nullifier<
EccChip: EccInstructions<
pallas::Affine,
FixedPoints = OrchardFixedBases,
Point = EccPoint,
Var = AssignedCell<pallas::Base, pallas::Base>,
>,
>(
mut layouter: impl Layouter<pallas::Base>,
poseidon_chip: PoseidonChip,
add_chip: AddChip,
ecc_chip: EccChip,
mux_chip: MuxChip,
rho: AssignedCell<pallas::Base, pallas::Base>,
psi: &AssignedCell<pallas::Base, pallas::Base>,
cm: &Point<pallas::Affine, EccChip>,
nk: AssignedCell<pallas::Base, pallas::Base>,
split_flag: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<X<pallas::Affine, EccChip>, plonk::Error> {
// hash = poseidon_hash(nk, rho)
let hash = {
Expand All @@ -173,17 +201,37 @@ pub(in crate::circuit) fn derive_nullifier<
// `product` = [poseidon_hash(nk, rho) + psi] NullifierK.
//
let product = {
let nullifier_k = FixedPointBaseField::from_inner(ecc_chip, NullifierK);
let nullifier_k = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
nullifier_k.mul(
layouter.namespace(|| "[poseidon_output + psi] NullifierK"),
scalar,
)?
};

// Add cm to multiplied fixed base to get nf
// cm + [poseidon_output + psi] NullifierK
cm.add(layouter.namespace(|| "nf"), &product)
.map(|res| res.extract_p())
// Add cm to multiplied fixed base
// nf = cm + [poseidon_output + psi] NullifierK
let nf = cm.add(layouter.namespace(|| "nf"), &product)?;

// Add NullifierL to nf
// split_note_nf = NullifierL + nf
let nullifier_l = Point::new_from_constant(
ecc_chip.clone(),
layouter.namespace(|| "witness NullifierL constant"),
pallas::Point::hash_to_curve("z.cash:Orchard")(b"L").to_affine(),
)?;
let split_note_nf = nullifier_l.add(layouter.namespace(|| "split_note_nf"), &nf)?;

// Select the desired nullifier according to split_flag
Ok(Point::from_inner(
ecc_chip,
mux_chip.mux(
layouter.namespace(|| "mux on nf"),
&split_flag,
nf.inner(),
split_note_nf.inner(),
)?,
)
.extract_p())
}

pub(in crate::circuit) use crate::circuit::commit_ivk::gadgets::commit_ivk;
Expand Down
Loading