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
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
feat: Allow nullifier historic proofs in public
spalladino committed Mar 1, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit b3a6e31eac5d5b3ca3dcc63c5deae681f7b4c241
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 {
@@ -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
@@ -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);
@@ -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 {
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
@@ -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);
Original file line number Diff line number Diff line change
@@ -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}
};

@@ -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(
Original file line number Diff line number Diff line change
@@ -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
) {
Original file line number Diff line number Diff line change
@@ -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();
4 changes: 4 additions & 0 deletions yarn-project/pxe/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
@@ -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,
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
@@ -3,6 +3,7 @@ import {
ExtendedContractData,
L1ToL2MessageSource,
MerkleTreeId,
NullifierMembershipWitness,
Tx,
UnencryptedL2Log,
} from '@aztec/circuit-types';
@@ -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';
@@ -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>> {
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';
@@ -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.
@@ -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';
@@ -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);
}
}