Skip to content

Commit

Permalink
feat: Allow nullifier proofs in public (#4892)
Browse files Browse the repository at this point in the history
Allow performing nullifier proofs for the latest block in public.
Required for initialization checks.
  • Loading branch information
spalladino authored Mar 1, 2024
1 parent 823e071 commit f7a7243
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 11 deletions.
2 changes: 2 additions & 0 deletions noir-projects/aztec-nr/aztec/src/context/interface.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use dep::protocol_types::{
abis::function_selector::FunctionSelector,
address::{AztecAddress, EthAddress},
header::Header,
};

trait ContextInterface {
Expand All @@ -12,4 +13,5 @@ trait ContextInterface {
fn chain_id(self) -> Field;
fn version(self) -> Field;
fn selector(self) -> FunctionSelector;
fn get_header(self) -> Header;
}
12 changes: 6 additions & 6 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ impl ContextInterface for PrivateContext {
self.inputs.call_context.function_selector
}

// Returns the header of a block whose state is used during private execution (not the block the transaction is
// included in).
pub fn get_header(self) -> Header {
self.historical_header
}

fn push_new_note_hash(&mut self, note_hash: Field) {
let side_effect = SideEffect { value: note_hash, counter: self.side_effect_counter };
self.new_note_hashes.push(side_effect);
Expand Down Expand Up @@ -140,12 +146,6 @@ impl PrivateContext {
false
}

// Returns the header of a block whose state is used during private execution (not the block the transaction is
// included in).
pub fn get_header(self) -> Header {
self.historical_header
}

// Returns the header of an arbitrary block whose block number is less than or equal to the block number
// of historical header.
pub fn get_header_at(self, block_number: u32) -> Header {
Expand Down
4 changes: 4 additions & 0 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ impl ContextInterface for PublicContext {
self.inputs.call_context.function_selector
}

fn get_header(self) -> Header {
self.historical_header
}

fn push_new_note_hash(&mut self, note_hash: Field) {
let side_effect = SideEffect { value: note_hash, counter: self.side_effect_counter };
self.new_note_hashes.push(side_effect);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dep::std::merkle::compute_merkle_root;
use dep::protocol_types::header::Header;

use crate::{
context::PrivateContext, oracle::get_nullifier_membership_witness::get_nullifier_membership_witness,
context::{PrivateContext, ContextInterface}, oracle::get_nullifier_membership_witness::get_nullifier_membership_witness,
note::{utils::compute_siloed_nullifier, note_interface::NoteInterface}
};

Expand All @@ -25,8 +25,8 @@ fn _nullifier_inclusion(nullifier: Field, header: Header) {
// was included in the nullifier tree.
}

pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) {
_nullifier_inclusion(nullifier, context.historical_header);
pub fn prove_nullifier_inclusion<TContext>(nullifier: Field, context: TContext) where TContext: ContextInterface {
_nullifier_inclusion(nullifier, context.get_header());
}

pub fn prove_nullifier_inclusion_at(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ contract InclusionProofs {
}
}

// Proves nullifier existed at latest block
#[aztec(public)]
fn test_nullifier_inclusion_from_public(nullifier: Field) {
prove_nullifier_inclusion(nullifier, context);
}

#[aztec(private)]
fn test_public_unused_value_inclusion(block_number: u32 // The block at which we'll prove that the public value exists
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,13 @@ describe('e2e_inclusion_proofs_contract', () => {
await contract.methods.test_nullifier_inclusion(nullifier!, false, 0n).send().wait();
});

it('proves existence of a nullifier in public context', async () => {
const block = await pxe.getBlock(deploymentBlockNumber);
const nullifier = block?.body.txEffects[0].nullifiers[0];

await contract.methods.test_nullifier_inclusion_from_public(nullifier!).send().wait();
});

it('nullifier existence failure case', async () => {
// Choose random block number between first block and current block number to test archival node
const blockNumber = await getRandomBlockNumber();
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/pxe/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export class SimulatorOracle implements DBOracle {
}
}

public async getNullifierMembershipWitnessAtLatestBlock(nullifier: Fr) {
return this.getNullifierMembershipWitness(await this.getBlockNumber(), nullifier);
}

public getNullifierMembershipWitness(
blockNumber: number,
nullifier: Fr,
Expand Down
26 changes: 26 additions & 0 deletions yarn-project/sequencer-client/src/simulator/public_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ExtendedContractData,
L1ToL2MessageSource,
MerkleTreeId,
NullifierMembershipWitness,
Tx,
UnencryptedL2Log,
} from '@aztec/circuit-types';
Expand All @@ -14,6 +15,8 @@ import {
Fr,
FunctionSelector,
L1_TO_L2_MSG_TREE_HEIGHT,
NULLIFIER_TREE_HEIGHT,
NullifierLeafPreimage,
PublicDataTreeLeafPreimage,
} from '@aztec/circuits.js';
import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash';
Expand Down Expand Up @@ -217,6 +220,29 @@ export class WorldStatePublicDB implements PublicStateDB {
export class WorldStateDB implements CommitmentsDB {
constructor(private db: MerkleTreeOperations, private l1ToL2MessageSource: L1ToL2MessageSource) {}

public async getNullifierMembershipWitnessAtLatestBlock(
nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
const index = await this.db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer());
if (!index) {
return undefined;
}

const leafPreimagePromise = this.db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
const siblingPathPromise = this.db.getSiblingPath<typeof NULLIFIER_TREE_HEIGHT>(
MerkleTreeId.NULLIFIER_TREE,
BigInt(index),
);

const [leafPreimage, siblingPath] = await Promise.all([leafPreimagePromise, siblingPathPromise]);

if (!leafPreimage) {
return undefined;
}

return new NullifierMembershipWitness(BigInt(index), leafPreimage as NullifierLeafPreimage, siblingPath);
}

public async getL1ToL2MembershipWitness(
entryKey: Fr,
): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
Expand Down
10 changes: 9 additions & 1 deletion yarn-project/simulator/src/public/db.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NullifierMembershipWitness } from '@aztec/circuit-types';
import { EthAddress, FunctionSelector, L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
Expand Down Expand Up @@ -66,7 +67,7 @@ export interface PublicContractsDB {
getPortalContractAddress(address: AztecAddress): Promise<EthAddress | undefined>;
}

/** Database interface for providing access to commitment tree and l1 to l2 message tree (append only data trees). */
/** Database interface for providing access to commitment tree, l1 to l2 message tree, and nullifier tree. */
export interface CommitmentsDB {
/**
* Gets a confirmed L1 to L2 message for the given entry key.
Expand All @@ -89,4 +90,11 @@ export interface CommitmentsDB {
* @returns - The index of the nullifier. Undefined if it does not exist in the tree.
*/
getNullifierIndex(nullifier: Fr): Promise<bigint | undefined>;

/**
* Returns a nullifier membership witness for the given nullifier or undefined if not found.
* REFACTOR: Same as getL1ToL2MembershipWitness, can be combined with aztec-node method that does almost the same thing.
* @param nullifier - Nullifier we're looking for.
*/
getNullifierMembershipWitnessAtLatestBlock(nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;
}
12 changes: 11 additions & 1 deletion yarn-project/simulator/src/public/public_execution_context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionL2Logs, UnencryptedL2Log } from '@aztec/circuit-types';
import { FunctionL2Logs, NullifierMembershipWitness, UnencryptedL2Log } from '@aztec/circuit-types';
import { CallContext, FunctionData, FunctionSelector, GlobalVariables, Header } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
Expand Down Expand Up @@ -226,4 +226,14 @@ export class PublicExecutionContext extends TypedOracle {

return childExecutionResult.returnValues;
}

public async getNullifierMembershipWitness(
blockNumber: number,
nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
if (!this.header.globalVariables.blockNumber.equals(new Fr(blockNumber))) {
throw new Error(`Public execution oracle can only access nullifier membership witnesses for the current block`);
}
return await this.commitmentsDb.getNullifierMembershipWitnessAtLatestBlock(nullifier);
}
}

0 comments on commit f7a7243

Please sign in to comment.