Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: TXE detects duplicate nullifiers #10764

Merged
merged 10 commits into from
Dec 17, 2024
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod test;
use dep::aztec::macros::aztec;

#[aztec]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::test::utils;
use dep::aztec::oracle::{execution::get_block_number, storage::storage_read};
use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map;

use crate::EasyPrivateVoting;

#[test]
unconstrained fn test_initializer() {
let (_, voting_contract_address, admin) = utils::setup();

let block_number = get_block_number();
let admin_slot = EasyPrivateVoting::storage_layout().admin.slot;
let admin_storage_value = storage_read(voting_contract_address, admin_slot, block_number);
assert(admin_storage_value == admin, "Vote ended should be false");
}

#[test]
unconstrained fn test_check_vote_status() {
let (_, voting_contract_address, _) = utils::setup();

let vote_ended_expected: bool = false;

let block_number = get_block_number();
let status_slot = EasyPrivateVoting::storage_layout().vote_ended.slot;
let vote_ended_read: bool = storage_read(voting_contract_address, status_slot, block_number);
assert(vote_ended_expected == vote_ended_read, "Vote ended should be false");
}

#[test]
unconstrained fn test_end_vote() {
let (env, voting_contract_address, admin) = utils::setup();

env.impersonate(admin);
EasyPrivateVoting::at(voting_contract_address).end_vote().call(&mut env.public());

let vote_ended_expected = true;

let block_number = get_block_number();
let status_slot = EasyPrivateVoting::storage_layout().vote_ended.slot;
let vote_ended_read: bool = storage_read(voting_contract_address, status_slot, block_number);
assert(vote_ended_expected == vote_ended_read, "Vote ended should be true");
}

#[test(should_fail)]
unconstrained fn test_fail_end_vote_by_non_admin() {
let (env, voting_contract_address, _) = utils::setup();
let alice = env.create_account();

env.impersonate(alice);
EasyPrivateVoting::at(voting_contract_address).end_vote().call(&mut env.public());
}

#[test]
unconstrained fn test_cast_vote() {
let (env, voting_contract_address, _) = utils::setup();
let alice = 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());

// Read vote count from storage
let block_number = get_block_number();
let tally_slot = EasyPrivateVoting::storage_layout().tally.slot;
let candidate_tally_slot = derive_storage_slot_in_map(tally_slot, candidate);
let vote_count: u32 = storage_read(voting_contract_address, candidate_tally_slot, block_number);

assert(vote_count == 1, "vote tally should be incremented");
}

#[test]
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 candidate = 101;

env.impersonate(alice);
env.advance_block_by(1);
EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private());

env.impersonate(bob);
env.advance_block_by(1);
EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private());

// Read vote count from storage
let block_number = get_block_number();
let tally_slot = EasyPrivateVoting::storage_layout().tally.slot;
let candidate_tally_slot = derive_storage_slot_in_map(tally_slot, candidate);
let vote_count: u32 = storage_read(voting_contract_address, candidate_tally_slot, block_number);

assert(vote_count == 2, "vote tally should be 2");
}

#[test(should_fail)]
unconstrained fn test_fail_vote_twice() {
let (env, voting_contract_address, _) = utils::setup();
let alice = 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());

// Vote again as alice
env.advance_block_by(1);
EasyPrivateVoting::at(voting_contract_address).cast_vote(candidate).call(&mut env.private());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod first;
mod utils;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use dep::aztec::{
note::{note_getter::{MAX_NOTES_PER_PAGE, view_notes}, note_viewer_options::NoteViewerOptions},
prelude::AztecAddress,
protocol_types::storage::map::derive_storage_slot_in_map,
test::helpers::test_environment::TestEnvironment,
};

use crate::EasyPrivateVoting;

pub fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress) {
let mut env = TestEnvironment::new();

let admin = env.create_account();

let initializer_call_interface = EasyPrivateVoting::interface().constructor(admin);
let voting_contract = env.deploy_self("EasyPrivateVoting").with_public_void_initializer(
initializer_call_interface,
);
// std::println(voting_contract);
(&mut env, voting_contract.to_address(), admin)
}
22 changes: 17 additions & 5 deletions yarn-project/txe/src/oracle/txe_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,19 @@ export class TXE implements TypedOracle {

async addSiloedNullifiers(siloedNullifiers: Fr[]) {
const db = await this.trees.getLatest();
await db.batchInsert(
MerkleTreeId.NULLIFIER_TREE,
siloedNullifiers.map(n => n.toBuffer()),
NULLIFIER_SUBTREE_HEIGHT,
);
try {
await db.batchInsert(
MerkleTreeId.NULLIFIER_TREE,
siloedNullifiers.map(n => n.toBuffer()),
NULLIFIER_SUBTREE_HEIGHT,
);
} catch (err: any) {
if (err.message === 'Nullifiers are create only') {
throw new Error(`Rejecting tx for emitting duplicate nullifiers`);
} else {
throw err;
}
}
}

async addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) {
Expand Down Expand Up @@ -709,6 +717,10 @@ export class TXE implements TypedOracle {
targetContractAddress,
publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log),
);
await this.addNullifiers(
targetContractAddress,
publicInputs.nullifiers.filter(nullifier => !nullifier.isEmpty()).map(nullifier => nullifier.value),
);

this.setContractAddress(currentContractAddress);
this.setMsgSender(currentMessageSender);
Expand Down
Loading