aztec-nr - noir_aztec::history::nullifier

Function assert_nullifier_did_not_exist_by

pub fn assert_nullifier_did_not_exist_by(
    block_header: BlockHeader,
    siloed_nullifier: Field,
)

Asserts that a nullifier did not exist by the time a block was mined.

This function takes a siloed nullifier, i.e. the value that is actually stored in the tree. Use crate::protocol::hash::compute_siloed_nullifier to convert an inner nullifier (what PrivateContext::push_nullifier takes) into a siloed one.

In order to prove that a nullifier did exist by block_header, use assert_nullifier_existed_by.

Nullifier Non-Existence

Proving nullifier non-existence is always nuanced, as it is not possible to privately prove that a nullifier does not exist by the time a transaction is executed. What this function does instead is assert that once all transactions from block_header were executed, the nullifier was not in the tree.

If you must prove that a nullifier does not exist by the time a transaction is executed, there only two ways to do this: by actually emitting the nullifier via PrivateContext::push_nullifier (which can of course can be done once), or by calling a public contract function that calls PublicContext::nullifier_exists_unsafe (which leaks that this nullifier is being checked):

#[external("public")]
#[only_self]
fn _assert_nullifier_does_not_exist(unsileod_nullifier: Field, contract_address: AztecAddress) {
    assert(!self.context.nullifier_exists_unsafe(unsiloed_nullifier, contract_address));
}

Cost

This function performs a full merkle tree inclusion proof, which is in the order of 4k gates.