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: Allow nullifier proofs in public #4892

Merged
merged 1 commit into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
Loading