From ac1edc1d30352cc3e3dcbfce4e69fe38777cc0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Tue, 12 Dec 2023 15:08:59 +0100 Subject: [PATCH] feat: block header block number oracle (#3648) --- .../acir-simulator/src/acvm/oracle/oracle.ts | 11 +++++ .../src/acvm/oracle/typed_oracle.ts | 5 +++ .../acir-simulator/src/client/db_oracle.ts | 6 +++ .../src/client/view_data_oracle.ts | 33 ++++++++++++++- yarn-project/aztec-nr/aztec/src/context.nr | 2 +- .../aztec/src/oracle/get_block_header.nr | 42 ++++++++++++------- .../src/oracle/get_membership_witness.nr | 4 +- .../get_nullifier_membership_witness.nr | 8 ++-- .../aztec/src/oracle/get_sibling_path.nr | 4 +- .../src/e2e_inclusion_proofs_contract.test.ts | 35 ++++++++++++---- .../inclusion_proofs_contract/src/main.nr | 28 ++----------- .../pxe/src/simulator_oracle/index.ts | 8 ++++ .../types/src/interfaces/aztec-node.ts | 6 --- .../src/interfaces/state_info_provider.ts | 6 +++ .../merkle_tree_snapshot_operations_facade.ts | 2 +- 15 files changed, 134 insertions(+), 66 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 87a32f072c0..5b93133e406 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -119,6 +119,17 @@ export class Oracle { return blockHeader.toArray().map(toACVMField); } + // TODO(#3564) - Nuke this oracle and inject the number directly to context + async getNullifierRootBlockNumber([nullifierTreeRoot]: ACVMField[]): Promise { + const parsedRoot = fromACVMField(nullifierTreeRoot); + + const blockNumber = await this.typedOracle.getNullifierRootBlockNumber(parsedRoot); + if (!blockNumber) { + throw new Error(`Block header not found for block ${parsedRoot}.`); + } + return toACVMField(blockNumber); + } + async getAuthWitness([messageHash]: ACVMField[]): Promise { const messageHashField = fromACVMField(messageHash); const witness = await this.typedOracle.getAuthWitness(messageHashField); diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 7013689a8ca..556da1bebf8 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -106,6 +106,11 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + // TODO(#3564) - Nuke this oracle and inject the number directly to context + getNullifierRootBlockNumber(_nullifierTreeRoot: Fr): Promise { + throw new Error('Not available.'); + } + getCompleteAddress(_address: AztecAddress): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 6ed7a6a8e6a..5d866dac224 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -159,4 +159,10 @@ export interface DBOracle extends CommitmentsDB { * @returns - The block corresponding to the given block number. Undefined if it does not exist. */ getBlock(blockNumber: number): Promise; + + /** + * Fetches the current block number. + * @returns The block number. + */ + getBlockNumber(): Promise; } diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 8a1c2258737..1edcfe3998b 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -3,7 +3,14 @@ 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, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types'; +import { + AuthWitness, + AztecNode, + CompleteAddress, + INITIAL_L2_BLOCK_NUM, + MerkleTreeId, + NullifierMembershipWitness, +} from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -113,6 +120,30 @@ export class ViewDataOracle extends TypedOracle { ); } + /** + * Gets number of a block in which a given nullifier tree root was included. + * @param nullifierTreeRoot - The nullifier tree root to get the block number for. + * @returns The block number. + * + * TODO(#3564) - Nuke this oracle and inject the number directly to context + */ + public async getNullifierRootBlockNumber(nullifierTreeRoot: Fr): Promise { + const currentBlockNumber = await this.db.getBlockNumber(); + for (let i = currentBlockNumber; i >= INITIAL_L2_BLOCK_NUM; i -= 2) { + const block = await this.db.getBlock(i); + if (!block) { + throw new Error(`Block ${i} not found`); + } + if (block.endNullifierTreeSnapshot.root.equals(nullifierTreeRoot)) { + return i; + } + if (block.startNullifierTreeSnapshot.root.equals(nullifierTreeRoot)) { + return i - 1; + } + } + throw new Error(`Failed to find block containing nullifier tree root ${nullifierTreeRoot}`); + } + /** * Retrieve the complete address associated to a given address. * @param address - Address to fetch the complete address for. diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index c7609942d4c..020a9410ce5 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -129,7 +129,7 @@ impl PrivateContext { self.inputs.call_context.function_selector } - pub fn get_block_header(self, block_number: Field) -> BlockHeader { + pub fn get_block_header(self, block_number: u32) -> BlockHeader { get_block_header(block_number, self) } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr index 9d15d693393..7d10a895646 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr @@ -13,34 +13,44 @@ use crate::{ }, }; +// TODO(#3564) - Nuke this oracle and Inject the number directly to context +#[oracle(getNullifierRootBlockNumber)] +fn get_nullifier_root_block_number_oracle(_nullifier_tree_root: Field) -> Field {} + +unconstrained pub fn get_nullifier_root_block_number(nullifier_tree_root: Field) -> u32 { + get_nullifier_root_block_number_oracle(nullifier_tree_root) as u32 +} + #[oracle(getBlockHeader)] -fn get_block_header_oracle(_block_number: Field) -> [Field; BLOCK_HEADER_LENGTH] {} +fn get_block_header_oracle(_block_number: u32) -> [Field; BLOCK_HEADER_LENGTH] {} -unconstrained pub fn get_block_header_internal(block_number: Field) -> BlockHeader { +unconstrained pub fn get_block_header_internal(block_number: u32) -> BlockHeader { let block_header = get_block_header_oracle(block_number); BlockHeader::deserialize(block_header) } -pub fn get_block_header(block_number: Field, context: PrivateContext) -> BlockHeader { - // 1) Get block header of a given block from oracle +pub fn get_block_header(block_number: u32, context: PrivateContext) -> BlockHeader { + // 1) Get block number corresponding to block header inside context + // Using nullifier tree root to get the block header block number because that changes in every block (every tx emits a nullifier). + let block_header_block_number = get_nullifier_root_block_number(context.block_header.nullifier_tree_root); + + // 2) Check that the block header block number is more than or equal to the block number we want to prove against + // We could not perform the proof otherwise because the archive root from the header would not "contain" the block we want to prove against + assert(block_header_block_number >= block_number, "Block header block number is smaller than the block number we want to prove against"); + + // 3) Get block header of a given block from oracle let block_header = get_block_header_internal(block_number); - // 2) Compute the block hash from the block header + // 4) Compute the block hash from the block header let block_hash = block_header.block_hash(); - // 3) Get the membership witness of the block in the archive + // 5) Get the membership witness of the block in the archive let archive_id = 5; // TODO(#3443) - - // Using `block_number` here for path is incorrect and it will break if we pass in an incorrect block number on input. - // Instead here should be the block number corresponding to `context.block_header.blocks_tree_root` - // This is not currently available in private context. See issue #3564 - let path_block_number = block_number; - - let witness: MembershipWitness = get_membership_witness(path_block_number, archive_id, block_hash); - - // 4) Check that the block is in the archive (i.e. the witness is valid) + let witness: MembershipWitness = get_membership_witness(block_header_block_number, archive_id, block_hash); + + // 6) Check that the block is in the archive (i.e. the witness is valid) assert(context.block_header.archive_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in archive failed"); - // 5) Return the block header + // 7) Return the block header block_header } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr index 1f43ae52f57..320b57cedc3 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr @@ -14,9 +14,9 @@ struct MembershipWitness { } #[oracle(getMembershipWitness)] -fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf_value: Field) -> [Field; M] {} +fn get_membership_witness_oracle(_block_number: u32, _tree_id: Field, _leaf_value: Field) -> [Field; M] {} -unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf_value: Field) -> MembershipWitness { +unconstrained pub fn get_membership_witness(block_number: u32, tree_id: Field, leaf_value: Field) -> MembershipWitness { let fields: [Field; M] = get_membership_witness_oracle(block_number, tree_id, leaf_value); MembershipWitness { index: fields[0], path: arr_copy_slice(fields, [0; N], 1) } } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr index 2cc493d7966..204bde2b1c1 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr @@ -32,11 +32,11 @@ struct NullifierMembershipWitness { } #[oracle(getLowNullifierMembershipWitness)] -fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} +fn get_low_nullifier_membership_witness_oracle(_block_number: u32, _nullifier: Field) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} // Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower // nullifier's next_value is bigger than the nullifier) -unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> NullifierMembershipWitness { +unconstrained pub fn get_low_nullifier_membership_witness(block_number: u32, nullifier: Field) -> NullifierMembershipWitness { let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); NullifierMembershipWitness { index: fields[0], @@ -46,11 +46,11 @@ unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, n } #[oracle(getNullifierMembershipWitness)] -fn get_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} +fn get_nullifier_membership_witness_oracle(_block_number: u32, _nullifier: Field) -> [Field; NULLIFIER_MEMBERSHIP_WITNESS] {} // Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower // nullifier's next_value is bigger than the nullifier) -unconstrained pub fn get_nullifier_membership_witness(block_number: Field, nullifier: Field) -> NullifierMembershipWitness { +unconstrained pub fn get_nullifier_membership_witness(block_number: u32, nullifier: Field) -> NullifierMembershipWitness { let fields = get_nullifier_membership_witness_oracle(block_number, nullifier); NullifierMembershipWitness { index: fields[0], diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr index 7fbe0936997..2109767a211 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr @@ -2,9 +2,9 @@ use dep::protocol_types::constants::NOTE_HASH_TREE_HEIGHT; use crate::utils::arr_copy_slice; #[oracle(getSiblingPath)] -fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _leaf_index: Field) -> [Field; N] {} +fn get_sibling_path_oracle(_block_number: u32, _tree_id: Field, _leaf_index: Field) -> [Field; N] {} -unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, leaf_index: Field) -> [Field; N] { +unconstrained pub fn get_sibling_path(block_number: u32, tree_id: Field, leaf_index: Field) -> [Field; N] { let value: [Field; N] = get_sibling_path_oracle(block_number, tree_id, leaf_index); value } diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 56807979809..4240a863566 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -19,12 +19,15 @@ describe('e2e_inclusion_proofs_contract', () => { let accounts: CompleteAddress[]; let contract: InclusionProofsContract; + let deploymentBlockNumber: number; const publicValue = 236n; beforeAll(async () => { ({ pxe, teardown, wallets, accounts } = await setup(1)); - contract = await InclusionProofsContract.deploy(wallets[0], publicValue).send().deployed(); + const receipt = await InclusionProofsContract.deploy(wallets[0], publicValue).send().wait(); + contract = receipt.contract; + deploymentBlockNumber = receipt.blockNumber!; }, 100_000); afterAll(() => teardown()); @@ -32,11 +35,15 @@ describe('e2e_inclusion_proofs_contract', () => { it('proves note existence and its nullifier non-existence and nullifier non-existence failure case', async () => { // Owner of a note const owner = accounts[0].address; + let noteCreationBlockNumber: number; { // Create a note const value = 100n; const receipt = await contract.methods.create_note(owner, value).send().wait({ debug: true }); + + noteCreationBlockNumber = receipt.blockNumber!; const { newCommitments, visibleNotes } = receipt.debugInfo!; + expect(newCommitments.length).toBe(1); expect(visibleNotes.length).toBe(1); const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; @@ -46,17 +53,14 @@ describe('e2e_inclusion_proofs_contract', () => { { // Prove note inclusion in a given block. - // TODO: Use here note block number from the creation note tx to test archival node. This is currently not - // possible because of issue #3564 - const blockNumber = await pxe.getBlockNumber(); const ignoredCommitment = 0; // Not ignored only when the note doesn't exist - await contract.methods.proveNoteInclusion(owner, blockNumber, ignoredCommitment).send().wait(); + await contract.methods.proveNoteInclusion(owner, noteCreationBlockNumber, ignoredCommitment).send().wait(); } { // Prove that the note has not been nullified - // TODO: Use here note block number from the creation note tx to test archival node. This is currently not - // possible because of issue #3564 + // TODO(#3535): Prove the nullifier non-inclusion at older block to test archival node. This is currently not + // possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 const blockNumber = await pxe.getBlockNumber(); const ignoredNullifier = 0; // Not ignored only when the note doesn't exist await contract.methods.proveNullifierNonInclusion(owner, blockNumber, ignoredNullifier).send().wait(); @@ -93,12 +97,16 @@ describe('e2e_inclusion_proofs_contract', () => { }); it('proves an existence of a public value in private context', async () => { - const blockNumber = await pxe.getBlockNumber(); + // Chose random block number between deployment and current block number to test archival node + const blockNumber = await getRandomBlockNumberSinceDeployment(); + await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); }); it('public value existence failure case', async () => { - const blockNumber = await pxe.getBlockNumber(); + // Chose random block number between deployment and current block number to test archival node + const blockNumber = await getRandomBlockNumberSinceDeployment(); + const randomPublicValue = Fr.random(); await expect( contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), @@ -106,6 +114,8 @@ describe('e2e_inclusion_proofs_contract', () => { }); it('proves existence of a nullifier in private context', async () => { + // TODO(#3535): Test this at "random" block to test archival node. This is currently not possible because of + // issue https://github.com/AztecProtocol/aztec-packages/issues/3535 const blockNumber = await pxe.getBlockNumber(); const block = await pxe.getBlock(blockNumber); const nullifier = block?.newNullifiers[0]; @@ -114,6 +124,8 @@ describe('e2e_inclusion_proofs_contract', () => { }); it('nullifier existence failure case', async () => { + // TODO(#3535): Test this at "random" block to test archival node. This is currently not possible because of + // issue https://github.com/AztecProtocol/aztec-packages/issues/3535 const blockNumber = await pxe.getBlockNumber(); const randomNullifier = Fr.random(); @@ -121,4 +133,9 @@ describe('e2e_inclusion_proofs_contract', () => { /Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/, ); }); + + const getRandomBlockNumberSinceDeployment = async () => { + const currentBlockNumber = await pxe.getBlockNumber(); + return deploymentBlockNumber + Math.floor(Math.random() * (currentBlockNumber - deploymentBlockNumber)); + }; }); diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr index d9235ccd6ee..a06c832cd50 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr @@ -98,14 +98,9 @@ contract InclusionProofs { #[aztec(private)] fn proveNoteInclusion( owner: AztecAddress, - block_number: Field, // The block at which we'll prove that the note exists + block_number: u32, // The block at which we'll prove that the note exists spare_commitment: Field, // This is only used when the note is not found --> used to test the failure case ) { - // TODO: assert that block number is less than the block number of context.block_header - // --> This will either require a new oracle method that returns block_header.global_variables_hash preimage - // or modifying the private context so that we somehow expose it. - // Blocked by #3564 - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree // root. let block_header = context.get_block_header(block_number); @@ -142,14 +137,9 @@ contract InclusionProofs { #[aztec(private)] fn proveNullifierNonInclusion( owner: AztecAddress, - block_number: Field, // The block at which we'll prove that the nullifier does not exists + block_number: u32, // The block at which we'll prove that the nullifier does not exists spare_nullifier: Field, // This is only used when the note is not found --> used to test the failure case ) { - // TODO: assert that block number is less than the block number of context.block_header - // --> This will either require a new oracle method that returns block_header.global_variables_hash preimage - // or modifying the private context so that we somehow expose it. - // Blocked by #3564 - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree // root. let block_header = context.get_block_header(block_number); @@ -215,13 +205,8 @@ contract InclusionProofs { #[aztec(private)] fn proveNullifierInclusion( nullifier: Field, - block_number: Field, // The block at which we'll prove that the nullifier not exists in the tree + block_number: u32, // The block at which we'll prove that the nullifier not exists in the tree ) { - // TODO: assert that block number is less than the block number of context.block_header - // --> This will either require a new oracle method that returns block_header.global_variables_hash preimage - // or modifying the private context so that we somehow expose it. - // Blocked by #3564 - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree // root. let block_header = context.get_block_header(block_number); @@ -248,13 +233,8 @@ contract InclusionProofs { #[aztec(private)] fn provePublicValueInclusion( public_value: Field, - block_number: Field, // The block at which we'll prove that the public value exists + block_number: u32, // The block at which we'll prove that the public value exists ) { - // TODO: assert that block number is less than the block number of context.block_header - // --> This will either require a new oracle method that returns block_header.global_variables_hash preimage - // or modifying the private context so that we somehow expose it. - // Blocked by #3564 - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree // root. let block_header = context.get_block_header(block_number); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 7e495c173ea..2eb485fd287 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -183,4 +183,12 @@ export class SimulatorOracle implements DBOracle { getBlockHeader(): Promise { return Promise.resolve(this.db.getBlockHeader()); } + + /** + * Fetches the current block number. + * @returns The block number. + */ + public async getBlockNumber(): Promise { + return await this.stateInfoProvider.getBlockNumber(); + } } diff --git a/yarn-project/types/src/interfaces/aztec-node.ts b/yarn-project/types/src/interfaces/aztec-node.ts index 92463e96b0f..ac57cd2f3a5 100644 --- a/yarn-project/types/src/interfaces/aztec-node.ts +++ b/yarn-project/types/src/interfaces/aztec-node.ts @@ -38,12 +38,6 @@ export interface AztecNode extends StateInfoProvider { */ getBlocks(from: number, limit: number): Promise; - /** - * Fetches the current block number. - * @returns The block number. - */ - getBlockNumber(): Promise; - /** * Method to fetch the version of the rollup the node is connected to. * @returns The rollup version. diff --git a/yarn-project/types/src/interfaces/state_info_provider.ts b/yarn-project/types/src/interfaces/state_info_provider.ts index 7818f984dab..97616711fb9 100644 --- a/yarn-project/types/src/interfaces/state_info_provider.ts +++ b/yarn-project/types/src/interfaces/state_info_provider.ts @@ -138,4 +138,10 @@ export interface StateInfoProvider { * @returns The requested block. */ getBlock(number: number): Promise; + + /** + * Fetches the current block number. + * @returns The block number. + */ + getBlockNumber(): Promise; } diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts index 1fd883b98b5..450b6c5bca8 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_snapshot_operations_facade.ts @@ -65,7 +65,7 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations } | undefined > { - return Promise.reject(new Error('not implemented')); + return Promise.reject(new Error('Snapshots not implemented for nullifier tree')); } async getSiblingPath(treeId: MerkleTreeId, index: bigint): Promise> {