From d6631b0f2e98db5fc0d6e392d6bd4f34800c2084 Mon Sep 17 00:00:00 2001 From: josh crites Date: Wed, 13 Nov 2024 15:57:19 -0500 Subject: [PATCH 1/3] expected --- Nargo.toml | 1 + src/main.nr | 25 +++++++++++++++++++++++-- src/test/first.nr | 27 ++++++++++++++++----------- src/test/index.test.ts | 21 ++++++++++++++++----- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/Nargo.toml b/Nargo.toml index e6ba684..06e4e50 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -6,3 +6,4 @@ compiler_version = ">=0.18.0" [dependencies] aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "aztec-packages-v0.62.0", directory = "noir-projects/aztec-nr/aztec" } +value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.62.0", directory="noir-projects/aztec-nr/value-note" } \ No newline at end of file diff --git a/src/main.nr b/src/main.nr index 3001e9a..7d534c1 100644 --- a/src/main.nr +++ b/src/main.nr @@ -4,16 +4,20 @@ use dep::aztec::macros::aztec; #[aztec] contract EasyPrivateVoting { use dep::aztec::{ + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, keys::getters::get_public_keys, macros::{functions::{initializer, internal, private, public}, storage::storage}, }; - use dep::aztec::prelude::{AztecAddress, Map, PublicMutable, SharedImmutable}; + use dep::aztec::prelude::{AztecAddress, Map, PrivateSet, PublicMutable, SharedImmutable}; + use dep::value_note::value_note::ValueNote; + #[storage] struct Storage { admin: PublicMutable, // admin can end vote tally: Map, Context>, // we will store candidate as key and number of votes as value vote_ended: PublicMutable, // vote_ended is boolean active_at_block: SharedImmutable, // when people can start voting + test: PrivateSet, } #[public] @@ -27,7 +31,7 @@ contract EasyPrivateVoting { #[private] // annotation to mark function as private and expose private context - fn cast_vote(candidate: Field) { + fn cast_vote(candidate: Field, bob: AztecAddress) { let msg_sender_npk_m_hash = get_public_keys(context.msg_sender()).npk_m.hash(); let secret = context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function @@ -36,6 +40,23 @@ contract EasyPrivateVoting { EasyPrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue( &mut context, ); + // + let mut value_note = ValueNote::new(candidate, context.msg_sender()); + let owner_ovpk = get_public_keys(context.msg_sender()).ovpk_m; + + storage.test.insert(&mut value_note).emit(encode_and_encrypt_note( + &mut context, + owner_ovpk, + context.msg_sender(), + context.msg_sender(), + )); + + storage.test.insert(&mut value_note).emit(encode_and_encrypt_note( + &mut context, + owner_ovpk, + bob, + context.msg_sender(), + )); } #[public] diff --git a/src/test/first.nr b/src/test/first.nr index 18b2a0b..4467770 100644 --- a/src/test/first.nr +++ b/src/test/first.nr @@ -1,12 +1,15 @@ use crate::test::utils; -use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; -use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; +use dep::aztec::hash::compute_secret_hash; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; -use dep::aztec::hash::compute_secret_hash; +use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; +use dep::aztec::test::helpers::{cheatcodes, test_environment::TestEnvironment}; -use dep::aztec::{oracle::{execution::{get_block_number, get_contract_address}, storage::storage_read}}; +use dep::aztec::oracle::{ + execution::{get_block_number, get_contract_address}, + storage::storage_read, +}; use crate::EasyPrivateVoting; @@ -60,11 +63,12 @@ unconstrained fn test_fail_end_vote_by_non_admin() { unconstrained fn test_cast_vote() { let (env, voting_contract_address, _) = utils::setup(); let alice = env.create_account(); + let bob = env.create_account(); env.impersonate(alice); let candidate = 1; env.advance_block_by(6); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); // Read vote count from storage let block_number = get_block_number(); @@ -82,14 +86,14 @@ unconstrained fn test_cast_vote_with_separate_accounts() { let bob = env.create_account(); let candidate = 101; - + env.impersonate(alice); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); env.impersonate(bob); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); // Read vote count from storage let block_number = get_block_number(); @@ -104,14 +108,15 @@ unconstrained fn test_cast_vote_with_separate_accounts() { unconstrained fn test_fail_vote_twice() { let (env, voting_contract_address, _) = utils::setup(); let alice = env.create_account(); + let bob = env.create_account(); let candidate = 101; - + env.impersonate(alice); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); // Vote again as alice env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); } diff --git a/src/test/index.test.ts b/src/test/index.test.ts index 42bade6..5b4a8ac 100644 --- a/src/test/index.test.ts +++ b/src/test/index.test.ts @@ -22,6 +22,7 @@ describe("Voting", () => { pxe = await setupSandbox(); wallets = await getInitialTestAccountsWallets(pxe); + accounts = wallets.map(w => w.getCompleteAddress()) }) @@ -63,20 +64,30 @@ describe("Voting", () => { it("It casts a vote", async () => { const candidate = new Fr(1) + const [deployerWallet, bobWallet] = wallets; // using first account as deployer and second as bob + + const contract = await EasyPrivateVotingContract.deploy(deployerWallet, deployerWallet.getAddress()).send().deployed(); + const receipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().wait({ debug: true }); + + console.log(receipt); + const { visibleIncomingNotes, visibleOutgoingNotes } = receipt.debugInfo! + console.log("Visible Incoming Notes:"); + console.log(visibleIncomingNotes); + console.log("Visible Outgoing Notes:"); + console.log(visibleOutgoingNotes); - const contract = await EasyPrivateVotingContract.deploy(wallets[0], accounts[0].address).send().deployed(); - const tx = await contract.methods.cast_vote(candidate).send().wait(); let count = await contract.methods.get_vote(candidate).simulate(); expect(count).toBe(1n); }, 300_000) it("It should fail when trying to vote twice", async () => { const candidate = new Fr(1) + const [deployerWallet, bobWallet] = wallets; // using first account as deployer and second as bob - const contract = await EasyPrivateVotingContract.deploy(wallets[0], accounts[0].address).send().deployed(); - await contract.methods.cast_vote(candidate).send().wait(); + const contract = await EasyPrivateVotingContract.deploy(deployerWallet, deployerWallet.getAddress()).send().deployed(); + await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().wait(); - const secondVoteReceipt = await contract.methods.cast_vote(candidate).send().getReceipt(); + const secondVoteReceipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().getReceipt(); expect(secondVoteReceipt).toEqual( expect.objectContaining({ status: TxStatus.DROPPED, From 24c931dcd3d82e186efa4f796f3dba5b77d559d5 Mon Sep 17 00:00:00 2001 From: josh crites Date: Fri, 15 Nov 2024 10:13:27 -0500 Subject: [PATCH 2/3] emit 3 notes --- src/main.nr | 16 +++++++--------- src/test/first.nr | 24 +++++++++++++++++++----- src/test/index.test.ts | 10 +++++----- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/main.nr b/src/main.nr index 7d534c1..42f2163 100644 --- a/src/main.nr +++ b/src/main.nr @@ -17,7 +17,7 @@ contract EasyPrivateVoting { tally: Map, Context>, // we will store candidate as key and number of votes as value vote_ended: PublicMutable, // vote_ended is boolean active_at_block: SharedImmutable, // when people can start voting - test: PrivateSet, + set: PrivateSet, } #[public] @@ -31,7 +31,7 @@ contract EasyPrivateVoting { #[private] // annotation to mark function as private and expose private context - fn cast_vote(candidate: Field, bob: AztecAddress) { + fn cast_vote(candidate: Field, bob: AztecAddress, charlie: AztecAddress) { let msg_sender_npk_m_hash = get_public_keys(context.msg_sender()).npk_m.hash(); let secret = context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function @@ -44,19 +44,17 @@ contract EasyPrivateVoting { let mut value_note = ValueNote::new(candidate, context.msg_sender()); let owner_ovpk = get_public_keys(context.msg_sender()).ovpk_m; - storage.test.insert(&mut value_note).emit(encode_and_encrypt_note( + let n = storage.set.insert(&mut value_note); + + n.emit(encode_and_encrypt_note( &mut context, owner_ovpk, context.msg_sender(), context.msg_sender(), )); - storage.test.insert(&mut value_note).emit(encode_and_encrypt_note( - &mut context, - owner_ovpk, - bob, - context.msg_sender(), - )); + n.emit(encode_and_encrypt_note(&mut context, owner_ovpk, bob, context.msg_sender())); + n.emit(encode_and_encrypt_note(&mut context, owner_ovpk, charlie, context.msg_sender())); } #[public] diff --git a/src/test/first.nr b/src/test/first.nr index 4467770..8fbc94f 100644 --- a/src/test/first.nr +++ b/src/test/first.nr @@ -64,11 +64,15 @@ unconstrained fn test_cast_vote() { let (env, voting_contract_address, _) = utils::setup(); let alice = env.create_account(); let bob = env.create_account(); + let charlie = env.create_account(); + env.impersonate(alice); let candidate = 1; env.advance_block_by(6); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob, charlie).call( + &mut env.private(), + ); // Read vote count from storage let block_number = get_block_number(); @@ -84,16 +88,21 @@ unconstrained fn test_cast_vote_with_separate_accounts() { let (env, voting_contract_address, _) = utils::setup(); let alice = env.create_account(); let bob = env.create_account(); + let charlie = env.create_account(); let candidate = 101; env.impersonate(alice); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob, charlie).call( + &mut env.private(), + ); env.impersonate(bob); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob, charlie).call( + &mut env.private(), + ); // Read vote count from storage let block_number = get_block_number(); @@ -109,14 +118,19 @@ unconstrained fn test_fail_vote_twice() { let (env, voting_contract_address, _) = utils::setup(); let alice = env.create_account(); let bob = env.create_account(); + let charlie = env.create_account(); let candidate = 101; env.impersonate(alice); env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob, charlie).call( + &mut env.private(), + ); // Vote again as alice env.advance_block_by(1); - EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob).call(&mut env.private()); + EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate, bob, charlie).call( + &mut env.private(), + ); } diff --git a/src/test/index.test.ts b/src/test/index.test.ts index 5b4a8ac..08ee174 100644 --- a/src/test/index.test.ts +++ b/src/test/index.test.ts @@ -64,10 +64,10 @@ describe("Voting", () => { it("It casts a vote", async () => { const candidate = new Fr(1) - const [deployerWallet, bobWallet] = wallets; // using first account as deployer and second as bob + const [deployerWallet, bobWallet, charlieWallet] = wallets; // using first account as deployer and second as bob const contract = await EasyPrivateVotingContract.deploy(deployerWallet, deployerWallet.getAddress()).send().deployed(); - const receipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().wait({ debug: true }); + const receipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress(), charlieWallet.getAddress()).send().wait({ debug: true }); console.log(receipt); const { visibleIncomingNotes, visibleOutgoingNotes } = receipt.debugInfo! @@ -82,12 +82,12 @@ describe("Voting", () => { it("It should fail when trying to vote twice", async () => { const candidate = new Fr(1) - const [deployerWallet, bobWallet] = wallets; // using first account as deployer and second as bob + const [deployerWallet, bobWallet, charlieWallet] = wallets; // using first account as deployer and second as bob const contract = await EasyPrivateVotingContract.deploy(deployerWallet, deployerWallet.getAddress()).send().deployed(); - await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().wait(); + await contract.methods.cast_vote(candidate, bobWallet.getAddress(), charlieWallet.getAddress()).send().wait(); - const secondVoteReceipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress()).send().getReceipt(); + const secondVoteReceipt = await contract.methods.cast_vote(candidate, bobWallet.getAddress(), charlieWallet.getAddress()).send().getReceipt(); expect(secondVoteReceipt).toEqual( expect.objectContaining({ status: TxStatus.DROPPED, From ec011daa3e63c9cdbb241a81e236af8e94dcb3b0 Mon Sep 17 00:00:00 2001 From: josh crites Date: Fri, 15 Nov 2024 10:27:08 -0500 Subject: [PATCH 3/3] use shared note --- src/main.nr | 9 ++++-- src/types.nr | 1 + src/types/shared_note.nr | 64 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/types.nr create mode 100644 src/types/shared_note.nr diff --git a/src/main.nr b/src/main.nr index 42f2163..bddc948 100644 --- a/src/main.nr +++ b/src/main.nr @@ -1,12 +1,15 @@ mod test; +mod types; use dep::aztec::macros::aztec; #[aztec] contract EasyPrivateVoting { + use crate::types::shared_note::{SHARED_NOTE_LEN, SharedNote}; use dep::aztec::{ encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, keys::getters::get_public_keys, macros::{functions::{initializer, internal, private, public}, storage::storage}, + oracle::random::random, }; use dep::aztec::prelude::{AztecAddress, Map, PrivateSet, PublicMutable, SharedImmutable}; use dep::value_note::value_note::ValueNote; @@ -17,7 +20,7 @@ contract EasyPrivateVoting { tally: Map, Context>, // we will store candidate as key and number of votes as value vote_ended: PublicMutable, // vote_ended is boolean active_at_block: SharedImmutable, // when people can start voting - set: PrivateSet, + set: PrivateSet, } #[public] @@ -41,10 +44,10 @@ contract EasyPrivateVoting { &mut context, ); // - let mut value_note = ValueNote::new(candidate, context.msg_sender()); + let mut shared_note = SharedNote::new(context.msg_sender(), bob, random()); let owner_ovpk = get_public_keys(context.msg_sender()).ovpk_m; - let n = storage.set.insert(&mut value_note); + let n = storage.set.insert(&mut shared_note); n.emit(encode_and_encrypt_note( &mut context, diff --git a/src/types.nr b/src/types.nr new file mode 100644 index 0000000..2ee7074 --- /dev/null +++ b/src/types.nr @@ -0,0 +1 @@ +mod shared_note; diff --git a/src/types/shared_note.nr b/src/types/shared_note.nr new file mode 100644 index 0000000..e2bf58e --- /dev/null +++ b/src/types/shared_note.nr @@ -0,0 +1,64 @@ +use dep::aztec::{ + context::PrivateContext, + macros::notes::note, + note::{ + note_header::NoteHeader, note_interface::NullifiableNote, + utils::compute_note_hash_for_nullify, + }, + prelude::AztecAddress, + protocol_types::{ + constants::GENERATOR_INDEX__NOTE_NULLIFIER, + hash::poseidon2_hash_with_separator, + traits::{Deserialize, Serialize}, + }, +}; + +/// @member alice: the user who shares the note +/// @member bob: the user who the note is shared with +/// @member shared_nullifier_key: the key used to nullify the note +#[note] +#[derive(Serialize, Deserialize)] +pub struct SharedNote { + alice: AztecAddress, // msg_sender + bob: AztecAddress, // inputted by msg_sender + shared_nullifier_key: Field, // oracle::random +} + +/// @notice Declare the length of the note. +global SHARED_NOTE_LEN: Field = 4; + +impl SharedNote { + pub fn new(alice: AztecAddress, bob: AztecAddress, shared_nullifier_key: Field) -> Self { + SharedNote { alice, bob, shared_nullifier_key, header: NoteHeader::empty() } + } +} + +impl Eq for SharedNote { + fn eq(self, other: SharedNote) -> bool { + self.alice.eq(other.alice) + & self.bob.eq(other.bob) + & self.shared_nullifier_key.eq(other.shared_nullifier_key) + } +} + +impl NullifiableNote for SharedNote { + fn compute_nullifier( + self, + context: &mut PrivateContext, + note_hash_for_nullify: Field, + ) -> Field { + // let note_hash_for_nullify = compute_note_hash_for_nullify(self); + poseidon2_hash_with_separator( + [self.shared_nullifier_key], + GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } + + unconstrained fn compute_nullifier_without_context(self) -> Field { + // let note_hash_for_nullify = compute_note_hash_for_nullify(self); + poseidon2_hash_with_separator( + [self.shared_nullifier_key], + GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } +}