Skip to content

Commit

Permalink
feat: inclusion and non-inclusion proofs experiment (#3255)
Browse files Browse the repository at this point in the history
Fixes #2572
Fixes #2584
  • Loading branch information
benesjan committed Nov 29, 2023
1 parent 6ba9e18 commit b911e65
Show file tree
Hide file tree
Showing 38 changed files with 1,170 additions and 73 deletions.
13 changes: 13 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,17 @@ jobs:
name: "Test"
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_escrow_contract.test.ts

e2e-inclusion-proofs-contract:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_inclusion_proofs_contract.test.ts

e2e-pending-commitments-contract:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -1113,6 +1124,7 @@ workflows:
- e2e-public-to-private-messaging: *e2e_test
- e2e-account-contracts: *e2e_test
- e2e-escrow-contract: *e2e_test
- e2e-inclusion-proofs-contract: *e2e_test
- e2e-pending-commitments-contract: *e2e_test
- e2e-ordering: *e2e_test
- uniswap-trade-on-l1-from-l2: *e2e_test
Expand Down Expand Up @@ -1148,6 +1160,7 @@ workflows:
- e2e-public-to-private-messaging
- e2e-account-contracts
- e2e-escrow-contract
- e2e-inclusion-proofs-contract
- e2e-pending-commitments-contract
- e2e-ordering
- uniswap-trade-on-l1-from-l2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ template <typename NCT> struct HistoricBlockData {
nullifier_tree_root,
contract_tree_root,
l1_to_l2_messages_tree_root,
blocks_tree_root, // Note private_kernel_vk_tree_root, is not included yet as
blocks_tree_root, // TODO(#3441) Note private_kernel_vk_tree_root, is not included yet as
// it is not present in noir,
public_data_tree_root,
global_variables_hash };
Expand Down
77 changes: 75 additions & 2 deletions yarn-project/acir-simulator/src/acvm/oracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { padArrayEnd } from '@aztec/foundation/collection';
import { Fr, Point } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { UnencryptedL2Log } from '@aztec/types';
import { MerkleTreeId, UnencryptedL2Log } from '@aztec/types';

import { ACVMField } from '../acvm_types.js';
import { fromACVMField } from '../deserialize.js';
import { frToNumber, fromACVMField } from '../deserialize.js';
import {
toACVMField,
toAcvmCallPrivateStackItem,
Expand Down Expand Up @@ -46,6 +46,79 @@ export class Oracle {
return [publicKey.x, publicKey.y, partialAddress].map(toACVMField);
}

async getMembershipWitness(
[blockNumber]: ACVMField[],
[treeId]: ACVMField[],
[leafValue]: ACVMField[],
): Promise<ACVMField[]> {
const parsedBlockNumber = frToNumber(fromACVMField(blockNumber));
const parsedTreeId = frToNumber(fromACVMField(treeId));
const parsedLeafValue = fromACVMField(leafValue);

const witness = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeafValue);
if (!witness) {
throw new Error(
`Leaf ${leafValue} not found in the tree ${MerkleTreeId[parsedTreeId]} at block ${parsedBlockNumber}.`,
);
}
return witness.map(toACVMField);
}

async getSiblingPath(
[blockNumber]: ACVMField[],
[treeId]: ACVMField[],
[leafIndex]: ACVMField[],
): Promise<ACVMField[]> {
const parsedBlockNumber = frToNumber(fromACVMField(blockNumber));
const parsedTreeId = frToNumber(fromACVMField(treeId));
const parsedLeafIndex = fromACVMField(leafIndex);

const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedLeafIndex);
return path.map(toACVMField);
}

async getNullifierMembershipWitness(
[blockNumber]: ACVMField[],
[nullifier]: ACVMField[], // nullifier, we try to find the witness for (to prove inclusion)
): Promise<ACVMField[]> {
const parsedBlockNumber = frToNumber(fromACVMField(blockNumber));
const parsedNullifier = fromACVMField(nullifier);

const witness = await this.typedOracle.getNullifierMembershipWitness(parsedBlockNumber, parsedNullifier);
if (!witness) {
throw new Error(
`Low nullifier witness not found for nullifier ${parsedNullifier} at block ${parsedBlockNumber}.`,
);
}
return witness.toFieldArray().map(toACVMField);
}

async getLowNullifierMembershipWitness(
[blockNumber]: ACVMField[],
[nullifier]: ACVMField[], // nullifier, we try to find the low nullifier witness for (to prove non-inclusion)
): Promise<ACVMField[]> {
const parsedBlockNumber = frToNumber(fromACVMField(blockNumber));
const parsedNullifier = fromACVMField(nullifier);

const witness = await this.typedOracle.getLowNullifierMembershipWitness(parsedBlockNumber, parsedNullifier);
if (!witness) {
throw new Error(
`Low nullifier witness not found for nullifier ${parsedNullifier} at block ${parsedBlockNumber}.`,
);
}
return witness.toFieldArray().map(toACVMField);
}

async getBlockData([blockNumber]: ACVMField[]): Promise<ACVMField[]> {
const parsedBlockNumber = frToNumber(fromACVMField(blockNumber));

const blockData = await this.typedOracle.getBlockData(parsedBlockNumber);
if (!blockData) {
throw new Error(`Block data not found for block ${parsedBlockNumber}.`);
}
return blockData.toArray().map(toACVMField);
}

async getAuthWitness([messageHash]: ACVMField[]): Promise<ACVMField[]> {
const messageHashField = fromACVMField(messageHash);
const witness = await this.typedOracle.getAuthWitness(messageHashField);
Expand Down
38 changes: 36 additions & 2 deletions yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js';
import { HistoricBlockData, PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js';
import { FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr, GrumpkinScalar } from '@aztec/foundation/fields';
import { CompleteAddress, Note, PublicKey, UnencryptedL2Log } from '@aztec/types';
import {
CompleteAddress,
MerkleTreeId,
Note,
NullifierMembershipWitness,
PublicKey,
UnencryptedL2Log,
} from '@aztec/types';

/**
* Information about a note needed during execution.
Expand Down Expand Up @@ -72,6 +79,33 @@ export abstract class TypedOracle {
throw new Error('Not available.');
}

getPublicKeyAndPartialAddress(_address: AztecAddress): Promise<Fr[] | undefined> {
throw new Error('Not available.');
}

getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise<Fr[] | undefined> {
throw new Error('Not available.');
}

getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leafIndex: Fr): Promise<Fr[]> {
throw new Error('Not available.');
}

getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
throw new Error('Not available.');
}

getLowNullifierMembershipWitness(
_blockNumber: number,
_nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
throw new Error('Not available.');
}

getBlockData(_blockNumber: number): Promise<HistoricBlockData | undefined> {
throw new Error('Not available.');
}

getCompleteAddress(_address: AztecAddress): Promise<CompleteAddress> {
throw new Error('Not available.');
}
Expand Down
45 changes: 45 additions & 0 deletions yarn-project/acir-simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FunctionArtifact, FunctionDebugMetadata, FunctionSelector } from '@azte
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { L2Block, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types';

import { NoteData } from '../acvm/index.js';
import { CommitmentsDB } from '../public/index.js';
Expand Down Expand Up @@ -114,4 +115,48 @@ export interface DBOracle extends CommitmentsDB {
* @returns A Promise that resolves to a HistoricBlockData object.
*/
getHistoricBlockData(): Promise<HistoricBlockData>;

/**
* Fetch the index of the leaf in the respective tree
* @param blockNumber - The block number at which to get the leaf index.
* @param treeId - The id of the tree to search.
* @param leafValue - The leaf value buffer.
* @returns - The index of the leaf. Undefined if it does not exist in the tree.
*/
findLeafIndex(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<bigint | undefined>;

/**
* Fetch the sibling path of the leaf in the respective tree
* @param blockNumber - The block number at which to get the sibling path.
* @param treeId - The id of the tree to search.
* @param leafIndex - The index of the leaf.
* @returns - The sibling path of the leaf.
*/
getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise<Fr[]>;

/**
* Returns a nullifier membership witness for a given nullifier at a given block.
* @param blockNumber - The block number at which to get the index.
* @param nullifier - Nullifier we try to find witness for.
* @returns The nullifier membership witness (if found).
*/
getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;

/**
* Returns a low nullifier membership witness for a given nullifier at a given block.
* @param blockNumber - The block number at which to get the index.
* @param nullifier - Nullifier we try to find the low nullifier witness for.
* @returns The low nullifier membership witness (if found).
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
* we are trying to prove non-inclusion for.
*/
getLowNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;

/**
* Fetch a block corresponding to the given block number.
* @param blockNumber - The block number of a block to fetch.
* @returns - The block corresponding to the given block number. Undefined if it does not exist.
*/
getBlock(blockNumber: number): Promise<L2Block | undefined>;
}
82 changes: 80 additions & 2 deletions yarn-project/acir-simulator/src/client/view_data_oracle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { HistoricBlockData, PublicKey } from '@aztec/circuits.js';
import { siloNullifier } from '@aztec/circuits.js/abis';
import { computeGlobalsHash, siloNullifier } from '@aztec/circuits.js/abis';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { AuthWitness, AztecNode, CompleteAddress } from '@aztec/types';
import { AuthWitness, AztecNode, CompleteAddress, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types';

import { NoteData, TypedOracle } from '../acvm/index.js';
import { DBOracle } from './db_oracle.js';
Expand Down Expand Up @@ -35,6 +35,84 @@ export class ViewDataOracle extends TypedOracle {
return this.db.getSecretKey(this.contractAddress, owner);
}

/**
* Fetches the index and sibling path of a leaf at a given block from a given tree.
* @param blockNumber - The block number at which to get the membership witness.
* @param treeId - Id of the tree to get the sibling path from.
* @param leafValue - The leaf value
* @returns The index and sibling path concatenated [index, sibling_path]
*/
public async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise<Fr[]> {
const index = await this.db.findLeafIndex(blockNumber, treeId, leafValue);
if (!index) {
throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]}`);
}
const siblingPath = await this.db.getSiblingPath(blockNumber, treeId, index);
return [new Fr(index), ...siblingPath];
}

/**
* Fetches a sibling path at a given block and index from a tree specified by `treeId`.
* @param blockNumber - The block number at which to get the membership witness.
* @param treeId - Id of the tree to get the sibling path from.
* @param leafIndex - Index of the leaf to get sibling path for
* @returns The sibling path.
*/
public getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr): Promise<Fr[]> {
return this.db.getSiblingPath(blockNumber, treeId, leafIndex.toBigInt());
}

/**
* Returns a nullifier membership witness for a given nullifier at a given block.
* @param blockNumber - The block number at which to get the index.
* @param nullifier - Nullifier we try to find witness for.
* @returns The nullifier membership witness (if found).
*/
public async getNullifierMembershipWitness(
blockNumber: number,
nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
return await this.db.getNullifierMembershipWitness(blockNumber, nullifier);
}

/**
* Returns a low nullifier membership witness for a given nullifier at a given block.
* @param blockNumber - The block number at which to get the index.
* @param nullifier - Nullifier we try to find the low nullifier witness for.
* @returns The low nullifier membership witness (if found).
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
* we are trying to prove non-inclusion for.
*/
public async getLowNullifierMembershipWitness(
blockNumber: number,
nullifier: Fr,
): Promise<NullifierMembershipWitness | undefined> {
return await this.db.getLowNullifierMembershipWitness(blockNumber, nullifier);
}

/**
* Fetches historic block data for a given block.
* @param blockNumber - The block number at which to get the historic block data.
* @returns Historic block data extracted from a block with block number `blockNumber`.
*/
public async getBlockData(blockNumber: number): Promise<HistoricBlockData | undefined> {
const block = await this.db.getBlock(blockNumber);
if (!block) {
return undefined;
}
return new HistoricBlockData(
block.endNoteHashTreeSnapshot.root,
block.endNullifierTreeSnapshot.root,
block.endContractTreeSnapshot.root,
block.endL1ToL2MessagesTreeSnapshot.root,
block.endHistoricBlocksTreeSnapshot.root,
new Fr(0), // TODO(#3441) privateKernelVkTreeRoot is not present in L2Block and it's not yet populated in noir
block.endPublicDataTreeRoot,
computeGlobalsHash(block.globalVariables),
);
}

/**
* Retrieve the complete address associated to a given address.
* @param address - Address to fetch the complete address for.
Expand Down
Loading

0 comments on commit b911e65

Please sign in to comment.