Skip to content

Commit

Permalink
Merge pull request #236 from dusk-network/mocello/235_default_feature
Browse files Browse the repository at this point in the history
circuit: Add plonk dependency under default feature
  • Loading branch information
moCello authored Aug 9, 2024
2 parents e047fc6 + 4c1a600 commit 47ceb25
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 252 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/dusk_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ jobs:
uses: dusk-network/.github/.github/workflows/run-tests.yml@main
with:
test_flags: -p phoenix-circuits

test_circuits_no_default:
name: test cirucits no-default-features
uses: dusk-network/.github/.github/workflows/run-tests.yml@main
with:
test_flags: -p phoenix-circuits --no-default-features
5 changes: 5 additions & 0 deletions circuits/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Move `TxCircuit` from `transaction` module to root module [#229]
- Rename `TxInputNote` to `InputNoteInfo` [#229]
- Rename `TxOutputNote` to `OutputNoteInfo` [#229]
- Move `ff` and `rand` dependencies to dev-dependencies [#235]

### Added

Expand All @@ -31,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `Clone` and `PartialEq` derives for `TxCircuit` [#232]
- Add `PartialEq` derive for `InputNoteInfo` [#232]
- Add `PartialEq` derive for `OutputNoteInfo` [#232]
- Add `dusk-bls12_381` dependency [#235]
- Add `"plonk"` feature to add the `dusk-plonk` dependency [#235]
- Add `"plonk"` feature as default feature [#235]

## [0.2.1] - 2024-07-03

Expand Down Expand Up @@ -80,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update `poseidon-merkle` to v0.6 [#179]

<!-- ISSUES -->
[#235]: https://github.com/dusk-network/phoenix/issues/235
[#232]: https://github.com/dusk-network/phoenix/issues/232
[#229]: https://github.com/dusk-network/phoenix/issues/229
[#214]: https://github.com/dusk-network/phoenix/issues/214
Expand Down
22 changes: 16 additions & 6 deletions circuits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ exclude = [".github/workflows/dusk-ci.yml", ".gitignore"]
[dependencies]
dusk-bytes = "0.1"
phoenix-core = { version = "0.30", default-features = false, path = "../core" }
dusk-plonk = { version = "0.19", default-features = false }
dusk-bls12_381 = { version = "0.13", default-features = false }
dusk-jubjub = { version = "0.14", default-features = false }
poseidon-merkle = { version = "0.6", features = ["rkyv-impl", "zk", "size_32"] }
dusk-poseidon = { version = "0.39", features = ["zk"] }
jubjub-schnorr = { version = "0.4", features = ["zk"] }
rand = { version = "0.8", default-features = false, features = ["std_rng"] }
ff = { version = "0.13", default-features = false }
poseidon-merkle = { version = "0.6" }
dusk-poseidon = { version = "0.39" }
jubjub-schnorr = { version = "0.4" }
dusk-plonk = { version = "0.19", default-features = false, optional = true }

[dev-dependencies]
lazy_static = "1.4"
rand = { version = "0.8", default-features = false, features = ["std_rng"] }
ff = { version = "0.13", default-features = false }

[features]
default = ["plonk"]
plonk = [
"dusk-plonk",
"poseidon-merkle/zk",
"dusk-poseidon/zk",
"jubjub-schnorr/zk",
]
256 changes: 256 additions & 0 deletions circuits/src/circuit_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! The [`Circuit`] trait implementation on [`TxCircuit`].
use dusk_bls12_381::BlsScalar;
use dusk_jubjub::{GENERATOR, GENERATOR_NUMS};
use dusk_plonk::prelude::{
Circuit, Composer, Constraint, Error as PlonkError, Witness, WitnessPoint,
};
use dusk_poseidon::{Domain, HashGadget};
use jubjub_schnorr::gadgets;
use poseidon_merkle::zk::opening_gadget;

use crate::{sender_enc, InputNoteInfo, TxCircuit};

impl<const H: usize, const I: usize> Circuit for TxCircuit<H, I> {
/// Transaction gadget proving the following properties in ZK for a generic
/// `I` input-notes and [`OUTPUT_NOTES`] output-notes:
///
/// 1. Membership: every input-note is included in the Merkle tree of notes.
/// 2. Ownership: the sender holds the note secret key for every input-note.
/// 3. Nullification: the nullifier is calculated correctly.
/// 4. Minting: the value commitment for every input-note is computed
/// correctly.
/// 5. Balance integrity: the sum of the values of all input-notes is equal
/// to the sum of the values of all output-notes + the gas fee
/// + a deposit, where a deposit refers to funds being transferred to a
/// contract.
/// 6. Sender-data: Verify that the sender was encrypted correctly for each
/// output-note.
///
/// The circuit has the following public inputs:
/// - `payload_hash`
/// - `root`
/// - `[nullifier; I]`
/// - `[output_value_commitment; 2]`
/// - `max_fee`
/// - `deposit`
/// - `(npk_out_0, npk_out_1)`
/// - `(enc_A_npk_out_0, enc_B_npk_out_0)`
/// - `(enc_A_npk_out_1, enc_B_npk_out_1)`
fn circuit(&self, composer: &mut Composer) -> Result<(), PlonkError> {
// Make the payload hash a public input of the circuit
let payload_hash = composer.append_public(self.payload_hash);

// Append the root as public input
let root_pi = composer.append_public(self.root);

let mut input_notes_sum = Composer::ZERO;

// Check membership, ownership and nullification of all input notes
for input_note_info in &self.input_notes_info {
let (
note_pk,
note_pk_p,
note_type,
pos,
value,
value_blinder,
nullifier,
signature_u,
signature_r,
signature_r_p,
) = input_note_info.append_to_circuit(composer);

// Verify: 2. Ownership
gadgets::verify_signature_double(
composer,
signature_u,
signature_r,
signature_r_p,
note_pk,
note_pk_p,
payload_hash,
)?;

// Verify: 3. Nullification
let computed_nullifier = HashGadget::digest(
composer,
Domain::Other,
&[*note_pk_p.x(), *note_pk_p.y(), pos],
)[0];
composer.assert_equal(computed_nullifier, nullifier);

// Perform a range check ([0, 2^64 - 1]) on the value of the note
composer.component_range::<32>(value);

// Sum up all the input note values
let constraint = Constraint::new()
.left(1)
.a(input_notes_sum)
.right(1)
.b(value);
input_notes_sum = composer.gate_add(constraint);

// Commit to the value of the note
let pc_1 = composer.component_mul_generator(value, GENERATOR)?;
let pc_2 = composer
.component_mul_generator(value_blinder, GENERATOR_NUMS)?;
let value_commitment = composer.component_add_point(pc_1, pc_2);

// Compute the note hash
let note_hash = HashGadget::digest(
composer,
Domain::Other,
&[
note_type,
*value_commitment.x(),
*value_commitment.y(),
*note_pk.x(),
*note_pk.y(),
pos,
],
)[0];

// Verify: 1. Membership
let root = opening_gadget(
composer,
&input_note_info.merkle_opening,
note_hash,
);
composer.assert_equal(root, root_pi);
}

let mut tx_output_sum = Composer::ZERO;

// Commit to all output notes
for output_note_info in &self.output_notes_info {
// Append the witnesses to the circuit
let value = composer.append_witness(output_note_info.value);
// Append the value-commitment as public input
let expected_value_commitment =
composer.append_public_point(output_note_info.value_commitment);
let value_blinder =
composer.append_witness(output_note_info.value_blinder);

// Perform a range check ([0, 2^64 - 1]) on the value of the note
composer.component_range::<32>(value);

// Sum up all the output note values
let constraint =
Constraint::new().left(1).a(tx_output_sum).right(1).b(value);
tx_output_sum = composer.gate_add(constraint);

// Commit to the value of the note
let pc_1 = composer.component_mul_generator(value, GENERATOR)?;
let pc_2 = composer
.component_mul_generator(value_blinder, GENERATOR_NUMS)?;
let computed_value_commitment =
composer.component_add_point(pc_1, pc_2);

// Verify: 4. Minting
composer.assert_equal_point(
expected_value_commitment,
computed_value_commitment,
);
}

// Append max_fee and deposit as public inputs
let max_fee = composer.append_public(self.max_fee);
let deposit = composer.append_public(self.deposit);

// Add the deposit and the max fee to the sum of the output-values
let constraint = Constraint::new()
.left(1)
.a(tx_output_sum)
.right(1)
.b(max_fee)
.fourth(1)
.d(deposit);
tx_output_sum = composer.gate_add(constraint);

// Verify: 5. Balance integrity
composer.assert_equal(input_notes_sum, tx_output_sum);

// Verify: 6. Sender-data
// appends as public input the note-pk of both output-notes:
// `(npk_out_0, npk_out_1)`
// and the encryption of the sender-pk.A and sender-pk.B,
// encrypted first with the note-pk of one output note:
// `(enc_A_npk_out_0, enc_B_npk_out_0)
// and then with the note-pk of the other note:
// `(enc_A_npk_out_1, enc_B_npk_out_1)
sender_enc::gadget(
composer,
self.sender_pk,
self.signatures,
[
self.output_notes_info[0].note_pk,
self.output_notes_info[1].note_pk,
],
[
self.output_notes_info[0].sender_blinder,
self.output_notes_info[1].sender_blinder,
],
self.output_notes_info[0].sender_enc,
self.output_notes_info[1].sender_enc,
payload_hash,
)?;

Ok(())
}
}

impl<const H: usize> InputNoteInfo<H> {
fn append_to_circuit(
&self,
composer: &mut Composer,
) -> (
WitnessPoint,
WitnessPoint,
Witness,
Witness,
Witness,
Witness,
Witness,
Witness,
WitnessPoint,
WitnessPoint,
) {
// Append the nullifier as public-input
let nullifier = composer.append_public(self.nullifier);

let note_pk = composer
.append_point(*self.note.stealth_address().note_pk().as_ref());
let note_pk_p = composer.append_point(self.note_pk_p);

let note_type = composer
.append_witness(BlsScalar::from(self.note.note_type() as u64));
let pos = composer.append_witness(BlsScalar::from(*self.note.pos()));

let value = composer.append_witness(self.value);
let value_blinder = composer.append_witness(self.value_blinder);

let signature_u = composer.append_witness(*self.signature.u());
let signature_r = composer.append_point(self.signature.R());
let signature_r_p = composer.append_point(self.signature.R_prime());

(
note_pk,
note_pk_p,
note_type,
pos,
value,
value_blinder,
nullifier,
signature_u,
signature_r,
signature_r_p,
)
}
}
Loading

0 comments on commit 47ceb25

Please sign in to comment.