diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index 223f4d1e29ea..5803432b342b 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -398,6 +398,33 @@ describe('aztec node', () => { }); }); + describe('getLowNullifierMembershipWitness', () => { + beforeEach(() => { + lastBlockNumber = BlockNumber(1); + }); + + it('throws when nullifier already exists in the tree', async () => { + const nullifier = Fr.random(); + merkleTreeOps.getPreviousValueIndex.mockImplementation((treeId: MerkleTreeId, value: bigint) => { + if (treeId === MerkleTreeId.NULLIFIER_TREE && value === nullifier.toBigInt()) { + return Promise.resolve({ index: 42n, alreadyPresent: true }); + } + return Promise.resolve(undefined); + }); + + await expect(node.getLowNullifierMembershipWitness('latest', nullifier)).rejects.toThrow( + /Cannot prove nullifier non-inclusion/, + ); + }); + + it('returns undefined when nullifier not found', async () => { + merkleTreeOps.getPreviousValueIndex.mockResolvedValue(undefined); + + const result = await node.getLowNullifierMembershipWitness('latest', Fr.random()); + expect(result).toBeUndefined(); + }); + }); + describe('findLeavesIndexes', () => { const blockHash1 = Fr.random(); const blockHash2 = Fr.random(); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 203d2b4f866a..8aeee75d54b5 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1133,21 +1133,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path); } - /** - * Returns a low nullifier membership witness for a given nullifier at a given block. - * @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data - * (which contains the root of the nullifier tree in which we are searching for the nullifier). - * @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. - * - * Note: This function returns the membership witness of the nullifier itself and not the low nullifier when - * the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the - * index of the nullifier itself when it already exists in the tree. - * TODO: This is a confusing behavior and we should eventually address that. - */ public async getLowNullifierMembershipWitness( referenceBlock: BlockParameter, nullifier: Fr, @@ -1159,7 +1144,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { } const { index, alreadyPresent } = findResult; if (alreadyPresent) { - this.log.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); + throw new Error( + `Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`, + ); } const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!; diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 94ec70a55654..183354098fd5 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -122,6 +122,7 @@ export interface AztecNode * @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data. * @param nullifier - Nullifier we try to find the low nullifier witness for. * @returns The low nullifier membership witness (if found). + * @throws If the nullifier already exists in the tree, since non-inclusion cannot be proven. * @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.