From c37efa54709843e9fb4075512f49bb80df49a9b3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 7 Nov 2023 09:21:53 +0000 Subject: [PATCH 01/69] WIP --- .../src/e2e_liquidity_mining.test.ts | 53 +++++++ .../liquidity_mining_contract/Nargo.toml | 9 ++ .../liquidity_mining_contract/src/main.nr | 74 +++++++++ .../src/types/lp_note.nr | 140 ++++++++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts create mode 100644 yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml create mode 100644 yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr create mode 100644 yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts new file mode 100644 index 00000000000..4251b6dceec --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -0,0 +1,53 @@ +import { + AccountWallet, + CheatCodes, + CompleteAddress, + DebugLogger +} from '@aztec/aztec.js'; +import { CircuitsWasm } from '@aztec/circuits.js'; +import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; +import { SlowTreeContract } from '@aztec/noir-contracts/types'; + +import { jest } from '@jest/globals'; +import levelup from 'levelup'; +import { default as memdown, type MemDown } from 'memdown'; + +import { setup } from './fixtures/utils.js'; +import { TokenSimulator } from './simulators/token_simulator.js'; + +export const createMemDown = () => (memdown as any)() as MemDown; + +const TIMEOUT = 90_000; + +describe('e2e_liquidity_mining', () => { + jest.setTimeout(TIMEOUT); + + let teardown: () => Promise; + let wallets: AccountWallet[]; + let accounts: CompleteAddress[]; + let logger: DebugLogger; + + let tokenSim: TokenSimulator; + + let tree: SparseTree; + + let cheatCodes: CheatCodes; + + beforeAll(async () => { + ({ teardown, logger, wallets, accounts, cheatCodes } = await setup(4)); + + slowTree = await SlowTreeContract.deploy(wallets[0]).send().deployed(); + + const db = levelup(createMemDown()); + const hasher = new Pedersen(await CircuitsWasm.get()); + const depth = 254; + tree = await newTree(SparseTree, db, hasher, 'test', depth); + + }, 100_000); + + afterAll(() => teardown()); + + afterEach(async () => { + await tokenSim.check(); + }, TIMEOUT); +}); diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml new file mode 100644 index 00000000000..f4990198fab --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "liquidity_mining_contract" +authors = [""] +compiler_version = ">=0.18.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../../aztec-nr/aztec" } +field_note = { path = "../../../../aztec-nr/field-note" } diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr new file mode 100644 index 00000000000..09db9410d12 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -0,0 +1,74 @@ +// A demonstration of private liquidity mining. +contract LiquidityMining { + + use crate::types::{ + lp_note::{LPNote, LPNoteMethods, LP_NOTE_LEN}, + safe_u120_serialization::{SafeU120SerializationMethods, SAFE_U120_SERIALIZED_LEN} + }; + + struct Storage { + balances: Map>, + } + + impl Storage { + fn init(context: Context) -> pub Self { + Storage { + balances: Map::new( + context, + 1, // Storage slot + |context, slot| { + Set::new(context, slot, LPNoteMethods) + }, + ), + } + } + } + + #[aztec(private)] + fn constructor() {} + + // Mints `amount` of LP tokens to `owner`. + #[aztec(private)] + fn mint( + owner: AztecAddress, + amount: Field, + ) -> Field { + // TODO + + 1 + } + + + // Proves that the owner owned an LP note for a specific time period and distributes reward based on the deposit + // amount and the length of time the note was owned. + // The proof works as follows: + // 1) Prove that I owned the note at the start of the time period. + // a) Pop HAT root of the stack (this will eventually be injected by kernel since this is not safe and for + // experimental purposes only). + // b) Pop all the preimages of block hash from the stack (one of those is note hash tree root). + // c) Compute the block hash. + // c) Pop sibling path of the block hash in the HAT from the stack. + // c) Prove that the block hash is in the HAT. + // a) Get the note/preimage from PXE. + // d) Compute the commitment from the note. + // d) Pop the note commitment sibling path in the note hash tree from the stack. + // e) Verify that the commitment is in the note hash tree. + // 2) Prove that the note has not yet been nullified by emitting a nullifier. + // 3) Distribute reward based on the time period and the note amount. + #[aztec(private)] + fn claim( + owner: AztecAddress, + ) -> Field { + // TODO + + 1 + } + + // Computes note hash and nullifier. + // Note 1: Needs to be defined by every contract producing logs. + // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. + unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; LP_NOTE_LEN]) -> [Field; 4] { + assert(storage_slot == 1, "Invalid storage slot"); + note_utils::compute_note_hash_and_nullifier(LPNoteMethods, note_header, serialized_note) + } +} diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr new file mode 100644 index 00000000000..bb4e180418c --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr @@ -0,0 +1,140 @@ +use dep::aztec::{ + note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, + }, + context::PrivateContext, + constants_gen::MAX_READ_REQUESTS_PER_CALL, + state_vars::set::Set, + log::emit_encrypted_log, + hash::pedersen_hash, + types::address::AztecAddress, + oracle::{ + rand::rand, + get_secret_key::get_secret_key, + get_public_key::get_public_key, + }, +}; +use dep::safe_math::SafeU120; +use dep::std::option::Option; + +global LP_NOTE_LEN: Field = 3; // 3 plus a header. + +struct LPNote { + // the amount of LP tokens in the note + amount: SafeU120, + // the provider of secrets for the nullifier. The owner (recipient) to ensure that the note + // can be privately spent. When nullifier secret and encryption private key is same + // we can simply use the owner for this one. + owner: AztecAddress, + // randomness of the note to hide contents. + randomness: Field, + // the note header (contract_address, nonce, storage_slot) + // included in the note such that it becomes part of encrypted logs for later use. + header: NoteHeader, +} + +impl LPNote { + pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { + Self { + amount, + owner, + randomness: rand(), + header: NoteHeader::empty(), + } + } + + pub fn serialize(self) -> [Field; LP_NOTE_LEN] { + [self.amount.value as Field, self.owner.address, self.randomness] + } + + pub fn deserialize(serialized_note: [Field; LP_NOTE_LEN]) -> Self { + Self { + amount: SafeU120::new(serialized_note[0]), + owner: AztecAddress::new(serialized_note[1]), + randomness: serialized_note[2], + header: NoteHeader::empty(), + } + } + + pub fn compute_note_hash(self) -> Field { + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + self.amount.value as Field, + self.owner.address as Field, + self.randomness, + ],0) + } + + // docs:start:nullifier + pub fn compute_nullifier(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(LPNoteMethods, self); + let secret = get_secret_key(self.owner.address); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + // docs:end:nullifier + + pub fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } + + // Broadcasts the note as an encrypted log on L1. + pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + // We only bother inserting the note if non-empty to save funds on gas. + if !self.amount.is_zero() { + let encryption_pub_key = get_public_key(self.owner.address); + emit_encrypted_log( + context, + (*context).this_address(), + slot, + encryption_pub_key, + self.serialize(), + ); + } + } +} + +fn deserialize(serialized_note: [Field; LP_NOTE_LEN]) -> LPNote { + LPNote::deserialize(serialized_note) +} + +fn serialize(note: LPNote) -> [Field; LP_NOTE_LEN] { + note.serialize() +} + +fn compute_note_hash(note: LPNote) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: LPNote) -> Field { + note.compute_nullifier() +} + +fn get_header(note: LPNote) -> NoteHeader { + note.header +} + +fn set_header(note: &mut LPNote, header: NoteHeader) { + note.set_header(header) +} + +// Broadcasts the note as an encrypted log on L1. +fn broadcast(context: &mut PrivateContext, slot: Field, note: LPNote) { + note.broadcast(context, slot); +} + +global LPNoteMethods = NoteInterface { + deserialize, + serialize, + compute_note_hash, + compute_nullifier, + get_header, + set_header, + broadcast, +}; \ No newline at end of file From 3da1c7881104e4a7c61de977a41f74dccccc93c9 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 7 Nov 2023 10:02:22 +0000 Subject: [PATCH 02/69] WIP --- .../liquidity_mining_contract/Nargo.toml | 2 +- .../liquidity_mining_contract/src/main.nr | 44 ++++++++++++------- .../liquidity_mining_contract/src/types.nr | 1 + 3 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml index f4990198fab..ea290776b4d 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml @@ -6,4 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../../aztec-nr/aztec" } -field_note = { path = "../../../../aztec-nr/field-note" } +safe_math = { path = "../../../../aztec-nr/safe-math" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 09db9410d12..fbb60267063 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -1,11 +1,22 @@ +mod types; + // A demonstration of private liquidity mining. contract LiquidityMining { - - use crate::types::{ - lp_note::{LPNote, LPNoteMethods, LP_NOTE_LEN}, - safe_u120_serialization::{SafeU120SerializationMethods, SAFE_U120_SERIALIZED_LEN} + use dep::aztec::{ + state_vars::{ + map::Map, + set::Set, + }, + types::address::AztecAddress, + context::Context, + note::{ + note_header::NoteHeader, + utils as note_utils, + }, }; + use crate::types::lp_note::{LPNote, LPNoteMethods, LP_NOTE_LEN}; + struct Storage { balances: Map>, } @@ -39,22 +50,24 @@ contract LiquidityMining { } - // Proves that the owner owned an LP note for a specific time period and distributes reward based on the deposit - // amount and the length of time the note was owned. - // The proof works as follows: + // Proves that the owner owned an LP note for a specific time period and distributes the reward based + // on the deposit amount and the length of time the note was owned. + // + // The scheme works as follows: // 1) Prove that I owned the note at the start of the time period. // a) Pop HAT root of the stack (this will eventually be injected by kernel since this is not safe and for // experimental purposes only). - // b) Pop all the preimages of block hash from the stack (one of those is note hash tree root). + // b) Pop all the preimages of block hash from the stack (this contains note hash tree root and timestamp). // c) Compute the block hash. - // c) Pop sibling path of the block hash in the HAT from the stack. - // c) Prove that the block hash is in the HAT. - // a) Get the note/preimage from PXE. - // d) Compute the commitment from the note. - // d) Pop the note commitment sibling path in the note hash tree from the stack. - // e) Verify that the commitment is in the note hash tree. + // d) Pop sibling path of the block hash in the HAT from the stack. + // e) Prove that the block hash is in the HAT. + // f) Get the note/preimage from PXE. + // g) Compute the commitment from the note. + // h) Pop the note commitment sibling path in the note hash tree from the stack. + // i) Verify that the commitment is in the note hash tree. // 2) Prove that the note has not yet been nullified by emitting a nullifier. - // 3) Distribute reward based on the time period and the note amount. + // 3) Use the timestamp from 1.b), current timestamp and the note amount from 1.f) to compute the reward. + // 4) Transfer the reward to the owner. #[aztec(private)] fn claim( owner: AztecAddress, @@ -69,6 +82,7 @@ contract LiquidityMining { // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; LP_NOTE_LEN]) -> [Field; 4] { assert(storage_slot == 1, "Invalid storage slot"); + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); note_utils::compute_note_hash_and_nullifier(LPNoteMethods, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr new file mode 100644 index 00000000000..b068916f145 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr @@ -0,0 +1 @@ +mod lp_note; \ No newline at end of file From 94f43be7d8b4ecccb811520640165ad5e58a1bfe Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 7 Nov 2023 10:17:31 +0000 Subject: [PATCH 03/69] WIP --- .../src/e2e_liquidity_mining.test.ts | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index 4251b6dceec..aed67768765 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -1,19 +1,13 @@ -import { - AccountWallet, - CheatCodes, - CompleteAddress, - DebugLogger -} from '@aztec/aztec.js'; +import { AccountWallet, CheatCodes, CompleteAddress, DebugLogger } from '@aztec/aztec.js'; import { CircuitsWasm } from '@aztec/circuits.js'; import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; -import { SlowTreeContract } from '@aztec/noir-contracts/types'; +import { LiquidityMiningContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; import levelup from 'levelup'; -import { default as memdown, type MemDown } from 'memdown'; +import { type MemDown, default as memdown } from 'memdown'; import { setup } from './fixtures/utils.js'; -import { TokenSimulator } from './simulators/token_simulator.js'; export const createMemDown = () => (memdown as any)() as MemDown; @@ -23,31 +17,26 @@ describe('e2e_liquidity_mining', () => { jest.setTimeout(TIMEOUT); let teardown: () => Promise; + let logger: DebugLogger; let wallets: AccountWallet[]; let accounts: CompleteAddress[]; - let logger: DebugLogger; - - let tokenSim: TokenSimulator; + let cheatCodes: CheatCodes; - let tree: SparseTree; + let contract: LiquidityMiningContract; - let cheatCodes: CheatCodes; + let simulatorTree: SparseTree; beforeAll(async () => { ({ teardown, logger, wallets, accounts, cheatCodes } = await setup(4)); - slowTree = await SlowTreeContract.deploy(wallets[0]).send().deployed(); + contract = await LiquidityMiningContract.deploy(wallets[0]).send().deployed(); const db = levelup(createMemDown()); const hasher = new Pedersen(await CircuitsWasm.get()); const depth = 254; - tree = await newTree(SparseTree, db, hasher, 'test', depth); - + simulatorTree = await newTree(SparseTree, db, hasher, 'test', depth); }, 100_000); afterAll(() => teardown()); - afterEach(async () => { - await tokenSim.check(); - }, TIMEOUT); }); From 32d2e39ef283c996dbdf65aa56ed9c14f434170b Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 7 Nov 2023 13:40:32 +0000 Subject: [PATCH 04/69] minting --- .../src/e2e_liquidity_mining.test.ts | 24 ++++++++++++++++++- .../liquidity_mining_contract/src/main.nr | 9 +++---- .../src/types/lp_note.nr | 13 +++++----- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index aed67768765..a95add9f9e1 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -27,7 +27,7 @@ describe('e2e_liquidity_mining', () => { let simulatorTree: SparseTree; beforeAll(async () => { - ({ teardown, logger, wallets, accounts, cheatCodes } = await setup(4)); + ({ teardown, logger, wallets, accounts, cheatCodes } = await setup(1)); contract = await LiquidityMiningContract.deploy(wallets[0]).send().deployed(); @@ -39,4 +39,26 @@ describe('e2e_liquidity_mining', () => { afterAll(() => teardown()); + it('mints and claims', async () => { + // The account which apes into the pool + const ape = accounts[0].address; + { + // Mint + const mintAmount = 100n; + const receipt = await contract.methods.mint(ape, mintAmount).send().wait({ debug: true }); + const { newCommitments, visibleNotes } = receipt.debugInfo!; + expect(newCommitments.length).toBe(1); + expect(visibleNotes.length).toBe(1); + const [noteAmount, noteOwner, _randomness] = visibleNotes[0].note.items; + expect(noteAmount.value).toBe(mintAmount); + expect(noteOwner).toEqual(ape.toField()); + } + + { + // Claim + const receipt = await contract.methods.claim(accounts[0].address).send().wait({ debug: true }); + const { newNullifiers } = receipt.debugInfo!; + expect(newNullifiers.length).toBe(1); + } + }); }); diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index fbb60267063..e7fbf52bf9e 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -13,6 +13,7 @@ contract LiquidityMining { note_header::NoteHeader, utils as note_utils, }, + // oracle::debug_log::debug_log_format, }; use crate::types::lp_note::{LPNote, LPNoteMethods, LP_NOTE_LEN}; @@ -43,10 +44,10 @@ contract LiquidityMining { fn mint( owner: AztecAddress, amount: Field, - ) -> Field { - // TODO - - 1 + ) { + let owner_balance = storage.balances.at(owner.address); + let mut note = LPNote::new(amount, owner); + owner_balance.insert(&mut note, true); } diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr index bb4e180418c..c825b4b8110 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr @@ -23,7 +23,8 @@ global LP_NOTE_LEN: Field = 3; // 3 plus a header. struct LPNote { // the amount of LP tokens in the note - amount: SafeU120, + // Note: We don't ever do any operations on this value, so we don't need to use SafeU120. + amount: Field, // the provider of secrets for the nullifier. The owner (recipient) to ensure that the note // can be privately spent. When nullifier secret and encryption private key is same // we can simply use the owner for this one. @@ -36,7 +37,7 @@ struct LPNote { } impl LPNote { - pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { + pub fn new(amount: Field, owner: AztecAddress) -> Self { Self { amount, owner, @@ -46,12 +47,12 @@ impl LPNote { } pub fn serialize(self) -> [Field; LP_NOTE_LEN] { - [self.amount.value as Field, self.owner.address, self.randomness] + [self.amount, self.owner.address, self.randomness] } pub fn deserialize(serialized_note: [Field; LP_NOTE_LEN]) -> Self { Self { - amount: SafeU120::new(serialized_note[0]), + amount: serialized_note[0], owner: AztecAddress::new(serialized_note[1]), randomness: serialized_note[2], header: NoteHeader::empty(), @@ -61,7 +62,7 @@ impl LPNote { pub fn compute_note_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ - self.amount.value as Field, + self.amount, self.owner.address as Field, self.randomness, ],0) @@ -87,7 +88,7 @@ impl LPNote { // Broadcasts the note as an encrypted log on L1. pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. - if !self.amount.is_zero() { + if self.amount != 0 { let encryption_pub_key = get_public_key(self.owner.address); emit_encrypted_log( context, From 63435537c29760fd153ec1f5a4f59025836442c0 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 7 Nov 2023 14:16:00 +0000 Subject: [PATCH 05/69] nullifying the note --- .../end-to-end/src/e2e_liquidity_mining.test.ts | 2 +- .../contracts/liquidity_mining_contract/src/main.nr | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index a95add9f9e1..06098f0cbeb 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -58,7 +58,7 @@ describe('e2e_liquidity_mining', () => { // Claim const receipt = await contract.methods.claim(accounts[0].address).send().wait({ debug: true }); const { newNullifiers } = receipt.debugInfo!; - expect(newNullifiers.length).toBe(1); + expect(newNullifiers.length).toBe(2); // tx hash and note nullifier } }); }); diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index e7fbf52bf9e..8906172f262 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -10,6 +10,7 @@ contract LiquidityMining { types::address::AztecAddress, context::Context, note::{ + note_getter_options::NoteGetterOptions, note_header::NoteHeader, utils as note_utils, }, @@ -72,17 +73,21 @@ contract LiquidityMining { #[aztec(private)] fn claim( owner: AztecAddress, - ) -> Field { - // TODO + ) { + let balances = storage.balances.at(owner.address); + + let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); + let notes = balances.get_notes(options); + let note = notes[0].unwrap(); - 1 + // Remove/nullify the note + balances.remove(note); } // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; LP_NOTE_LEN]) -> [Field; 4] { - assert(storage_slot == 1, "Invalid storage slot"); let note_header = NoteHeader::new(contract_address, nonce, storage_slot); note_utils::compute_note_hash_and_nullifier(LPNoteMethods, note_header, serialized_note) } From ce3c4ab51940d1494c76c7dc2fd653182f7f00fd Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 10:00:01 +0000 Subject: [PATCH 06/69] fix after rebase --- yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index 06098f0cbeb..3d51e67e3e5 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -1,11 +1,10 @@ import { AccountWallet, CheatCodes, CompleteAddress, DebugLogger } from '@aztec/aztec.js'; -import { CircuitsWasm } from '@aztec/circuits.js'; import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; import { LiquidityMiningContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; import levelup from 'levelup'; -import { type MemDown, default as memdown } from 'memdown'; +import { default as memdown, type MemDown } from 'memdown'; import { setup } from './fixtures/utils.js'; @@ -32,7 +31,7 @@ describe('e2e_liquidity_mining', () => { contract = await LiquidityMiningContract.deploy(wallets[0]).send().deployed(); const db = levelup(createMemDown()); - const hasher = new Pedersen(await CircuitsWasm.get()); + const hasher = new Pedersen(); const depth = 254; simulatorTree = await newTree(SparseTree, db, hasher, 'test', depth); }, 100_000); From f1598247fd5150ec26cec5dbf4808135123abee5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 11:30:14 +0000 Subject: [PATCH 07/69] formatting --- yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index 3d51e67e3e5..d2a798c39b0 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -4,7 +4,7 @@ import { LiquidityMiningContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; import levelup from 'levelup'; -import { default as memdown, type MemDown } from 'memdown'; +import { type MemDown, default as memdown } from 'memdown'; import { setup } from './fixtures/utils.js'; From c98a5af7ba2844ac48f1213c5e5a353451c7fd77 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 12:20:20 +0000 Subject: [PATCH 08/69] WIP --- .../src/contracts/liquidity_mining_contract/src/main.nr | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 8906172f262..3abacdf3907 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -76,10 +76,19 @@ contract LiquidityMining { ) { let balances = storage.balances.at(owner.address); + // 1.f) let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); + // 1.g) + let note_commitment = note_utils::compute_unique_siloed_note_hash(LPNoteMethods, note); + + // 1.h) + + // 1.i) + let note_hash_tree_root = context.block_data.note_hash_tree_root; + // Remove/nullify the note balances.remove(note); } From bbf4e66af1d0ad4306ef57906558e3bc7fd907c3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 13:22:26 +0000 Subject: [PATCH 09/69] fix: getPublicKeyAndPartialAddress in TypesOracle --- yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 0dfbd9affcd..5ff5dddb05a 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -72,6 +72,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getPublicKeyAndPartialAddress(_address: AztecAddress): Promise { + throw new Error('Not available.'); + } + getCompleteAddress(_address: AztecAddress): Promise { throw new Error('Not available.'); } From e152b2c63d8c412311a0060897cbe5f2b3942fcc Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 15:16:13 +0000 Subject: [PATCH 10/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 14 +++++++++++++- .../acir-simulator/src/acvm/oracle/typed_oracle.ts | 6 +++++- .../liquidity_mining_contract/src/main.nr | 11 +++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 443b1cda7bb..e6854d4347a 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -7,7 +7,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { UnencryptedL2Log } from '@aztec/types'; import { ACVMField } from '../acvm_types.js'; -import { fromACVMField } from '../deserialize.js'; +import { fromACVMField, frToNumber } from '../deserialize.js'; import { toACVMField, toAcvmCallPrivateStackItem, @@ -46,6 +46,18 @@ export class Oracle { return [publicKey.x, publicKey.y, partialAddress].map(toACVMField); } + async getSiblingPath([blockNumber]: ACVMField[], [treeId]: ACVMField[], [leaf]: ACVMField[]): Promise { + const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); + const parsedTreeId = frToNumber(fromACVMField(treeId)); + const parsedLeaf = fromACVMField(leaf); + + const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedLeaf); + if (!path) { + throw new Error(`Leaf ${leaf} not found in note hash tree.`); + } + return path.map(toACVMField); + } + 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 5ff5dddb05a..f3a66542988 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -3,7 +3,7 @@ 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, Note, MerkleTreeId, PublicKey, UnencryptedL2Log } from '@aztec/types'; /** * Information about a note needed during execution. @@ -76,6 +76,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leaf: Fr): Promise { + throw new Error('Not available.'); + } + getCompleteAddress(_address: AztecAddress): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 3abacdf3907..d9265e7fd2c 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -2,6 +2,7 @@ mod types; // A demonstration of private liquidity mining. contract LiquidityMining { + use dep::std::merkle::compute_merkle_root; use dep::aztec::{ state_vars::{ map::Map, @@ -14,6 +15,8 @@ contract LiquidityMining { note_header::NoteHeader, utils as note_utils, }, + constants_gen::NOTE_HASH_TREE_HEIGHT, + oracle::get_sibling_path::get_sibling_path, // oracle::debug_log::debug_log_format, }; @@ -85,9 +88,17 @@ contract LiquidityMining { let note_commitment = note_utils::compute_unique_siloed_note_hash(LPNoteMethods, note); // 1.h) + let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block + let note_hash_tree_id = 2; + let note_sibling_path: [Field; NOTE_HASH_TREE_HEIGHT + 1] = get_sibling_path(ignored_block_number, note_hash_tree_id, note_commitment); // 1.i) + // This should be tree root at the latest block and should correspond to the sibling path we obtain from oracle let note_hash_tree_root = context.block_data.note_hash_tree_root; + assert( + note_hash_tree_root == compute_merkle_root(note_commitment, p.index, note_sibling_path), + "Before root don't match" + ); // Remove/nullify the note balances.remove(note); From af94bc96fbf61fb7f81f43a0e5e35c8a2582c942 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 15:47:56 +0000 Subject: [PATCH 11/69] WIP --- yarn-project/aztec-nr/aztec/src/oracle.nr | 1 + .../aztec/src/oracle/get_membership_witness.nr | 18 ++++++++++++++++++ .../liquidity_mining_contract/src/main.nr | 18 +++++++++++------- 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index f5c27c53298..03362e631f8 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -7,6 +7,7 @@ mod call_private_function; mod context; mod debug_log; mod get_l1_to_l2_message; +mod get_membership_witness; mod get_public_key; mod get_secret_key; mod rand; 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 new file mode 100644 index 00000000000..afdbbe74ff8 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_membership_witness.nr @@ -0,0 +1,18 @@ +use crate::constants_gen::NOTE_HASH_TREE_HEIGHT; +use crate::utils::arr_copy_slice; + +struct MembershipWitness { + index: Field, + path: [Field; N], +} + +#[oracle(getMembershipWitness)] +fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf: Field) -> [Field; T] {} + +unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf: Field) -> MembershipWitness { + let fields: [Field; T] = get_membership_witness_oracle(block_number, tree_id, leaf); + MembershipWitness { + index: fields[0], + path: arr_copy_slice(fields, [0; N], 1) + } +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index d9265e7fd2c..09bb647b199 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -16,7 +16,10 @@ contract LiquidityMining { utils as note_utils, }, constants_gen::NOTE_HASH_TREE_HEIGHT, - oracle::get_sibling_path::get_sibling_path, + oracle::get_membership_witness::{ + get_membership_witness, + MembershipWitness, + }, // oracle::debug_log::debug_log_format, }; @@ -27,7 +30,7 @@ contract LiquidityMining { } impl Storage { - fn init(context: Context) -> pub Self { + fn init(context: Context) -> Self { Storage { balances: Map::new( context, @@ -68,7 +71,7 @@ contract LiquidityMining { // e) Prove that the block hash is in the HAT. // f) Get the note/preimage from PXE. // g) Compute the commitment from the note. - // h) Pop the note commitment sibling path in the note hash tree from the stack. + // h) Get the membership witness of a note in the note hash tree. // i) Verify that the commitment is in the note hash tree. // 2) Prove that the note has not yet been nullified by emitting a nullifier. // 3) Use the timestamp from 1.b), current timestamp and the note amount from 1.f) to compute the reward. @@ -90,14 +93,15 @@ contract LiquidityMining { // 1.h) let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block let note_hash_tree_id = 2; - let note_sibling_path: [Field; NOTE_HASH_TREE_HEIGHT + 1] = get_sibling_path(ignored_block_number, note_hash_tree_id, note_commitment); + let witness: MembershipWitness = + get_membership_witness(ignored_block_number, note_hash_tree_id, note_commitment); // 1.i) - // This should be tree root at the latest block and should correspond to the sibling path we obtain from oracle + // In our test case this should be tree root at the latest block and should correspond to the sibling path we obtain from oracle let note_hash_tree_root = context.block_data.note_hash_tree_root; assert( - note_hash_tree_root == compute_merkle_root(note_commitment, p.index, note_sibling_path), - "Before root don't match" + note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), + "Proving membership of a note failed" ); // Remove/nullify the note From e267f8d4ac5283639580ba7cdbe23c4f70ea3615 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 20 Nov 2023 16:08:22 +0000 Subject: [PATCH 12/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 10 +++++++--- .../src/acvm/oracle/typed_oracle.ts | 4 ++-- .../acir-simulator/src/client/db_oracle.ts | 18 ++++++++++++++++++ .../src/client/view_data_oracle.ts | 18 +++++++++++++++++- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index e6854d4347a..fbb8e4279ca 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -7,7 +7,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { UnencryptedL2Log } from '@aztec/types'; import { ACVMField } from '../acvm_types.js'; -import { fromACVMField, frToNumber } from '../deserialize.js'; +import { frToNumber, fromACVMField } from '../deserialize.js'; import { toACVMField, toAcvmCallPrivateStackItem, @@ -46,12 +46,16 @@ export class Oracle { return [publicKey.x, publicKey.y, partialAddress].map(toACVMField); } - async getSiblingPath([blockNumber]: ACVMField[], [treeId]: ACVMField[], [leaf]: ACVMField[]): Promise { + async getMembershipWitness( + [blockNumber]: ACVMField[], + [treeId]: ACVMField[], + [leaf]: ACVMField[], + ): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); const parsedTreeId = frToNumber(fromACVMField(treeId)); const parsedLeaf = fromACVMField(leaf); - const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedLeaf); + const path = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeaf); if (!path) { throw new Error(`Leaf ${leaf} not found in note hash tree.`); } 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 f3a66542988..413535794f3 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -3,7 +3,7 @@ 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, MerkleTreeId, PublicKey, UnencryptedL2Log } from '@aztec/types'; +import { CompleteAddress, MerkleTreeId, Note, PublicKey, UnencryptedL2Log } from '@aztec/types'; /** * Information about a note needed during execution. @@ -76,7 +76,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leaf: Fr): Promise { + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leaf: Fr): 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 7b61a60e6bd..f7ed6002555 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -6,6 +6,7 @@ import { Fr } from '@aztec/foundation/fields'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; +import { MerkleTreeId } from '@aztec/types'; /** * A function artifact with optional debug metadata @@ -114,4 +115,21 @@ export interface DBOracle extends CommitmentsDB { * @returns A Promise that resolves to a HistoricBlockData object. */ getHistoricBlockData(): Promise; + + + /** + * Fetch the index of the leaf in the respective tree + * @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(treeId: MerkleTreeId, leafValue: Buffer): Promise; + + /** + * Fetch the sibling path of the leaf in the respective tree + * @param blockNumber - The block number at which to get the membership witness. + * @param treeId - The id of the tree to search. + * @param leafIndex - The index of the leaf. + */ + getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): 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 cc168317756..16352de566e 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,7 @@ import { 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 } from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -35,6 +35,22 @@ export class ViewDataOracle extends TypedOracle { return this.db.getSecretKey(this.contractAddress, owner); } + /** + * Fetches the index and sibling path for a leaf value + * @param blockNumber - The block number at which to get the membership witness. + * @param treeId - The tree id + * @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 { + const index = await this.db.findLeafIndex(treeId, leafValue.toBuffer()); + if (!index) { + throw new Error(`Leaf value: ${leafValue} not found in tree ${treeId}`); + } + const siblingPath = await this.db.getSiblingPath(blockNumber, treeId, index); + return [new Fr(index), ...siblingPath]; + } + /** * Retrieve the complete address associated to a given address. * @param address - Address to fetch the complete address for. From 7d2fab6749c6e37e61331fa14a17aa8d22a446b1 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 21 Nov 2023 13:20:08 +0000 Subject: [PATCH 13/69] WIP --- .../acir-simulator/src/client/db_oracle.ts | 8 +++--- .../src/client/view_data_oracle.ts | 2 +- .../pxe/src/simulator_oracle/index.ts | 25 +++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index f7ed6002555..8eaa12a7411 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,10 +3,10 @@ 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 { MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; -import { MerkleTreeId } from '@aztec/types'; /** * A function artifact with optional debug metadata @@ -116,18 +116,18 @@ export interface DBOracle extends CommitmentsDB { */ getHistoricBlockData(): Promise; - /** * 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(treeId: MerkleTreeId, leafValue: Buffer): Promise; + findLeafIndex(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise; /** * Fetch the sibling path of the leaf in the respective tree - * @param blockNumber - The block number at which to get the membership witness. + * @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. */ 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 16352de566e..e5a781d1d4e 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -43,7 +43,7 @@ export class ViewDataOracle extends TypedOracle { * @returns The index and sibling path concatenated [index, sibling_path] */ public async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { - const index = await this.db.findLeafIndex(treeId, leafValue.toBuffer()); + const index = await this.db.findLeafIndex(blockNumber, treeId, leafValue); if (!index) { throw new Error(`Leaf value: ${leafValue} not found in tree ${treeId}`); } diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index f62b7683fa2..faf2129b5ce 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -9,6 +9,7 @@ import { HistoricBlockData, PublicKey, } from '@aztec/circuits.js'; +import { createDebugLogger } from '@aztec/foundation/log'; import { KeyStore, MerkleTreeId, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; @@ -23,6 +24,7 @@ export class SimulatorOracle implements DBOracle { private db: Database, private keyStore: KeyStore, private stateInfoProvider: StateInfoProvider, + private log = createDebugLogger('aztec:pxe:simulator_oracle'), ) {} getSecretKey(_contractAddress: AztecAddress, pubKey: PublicKey): Promise { @@ -134,6 +136,29 @@ export class SimulatorOracle implements DBOracle { return await this.stateInfoProvider.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier); } + public async findLeafIndex(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { + this.log.warn('Block number ignored in SimulatorOracle.findLeafIndex because archival node is not yet implemented'); + switch (treeId) { + case MerkleTreeId.NOTE_HASH_TREE: + return await this.stateInfoProvider.findLeafIndex(treeId, leafValue); + default: + throw new Error('Not implemented'); + } + } + + public async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise { + this.log.warn( + 'Block number ignored in SimulatorOracle.getSiblingPath because archival node is not yet implemented', + ); + // @todo This is doing a nasty workaround as http_rpc_client was not happy about a generic `getSiblingPath` function being exposed. + switch (treeId) { + case MerkleTreeId.NOTE_HASH_TREE: + return (await this.stateInfoProvider.getNoteHashSiblingPath(leafIndex)).toFieldArray(); + default: + throw new Error('Not implemented'); + } + } + /** * Retrieve the databases view of the Historic Block Data object. * This structure is fed into the circuits simulator and is used to prove against certain historic roots. From 7cdd90e61a5370bf4f127e60444fcb4e75ddfcb7 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 21 Nov 2023 14:11:32 +0000 Subject: [PATCH 14/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 12 +++++++----- .../contracts/liquidity_mining_contract/src/main.nr | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index fbb8e4279ca..b712c303402 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -4,7 +4,7 @@ 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 { frToNumber, fromACVMField } from '../deserialize.js'; @@ -55,11 +55,13 @@ export class Oracle { const parsedTreeId = frToNumber(fromACVMField(treeId)); const parsedLeaf = fromACVMField(leaf); - const path = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeaf); - if (!path) { - throw new Error(`Leaf ${leaf} not found in note hash tree.`); + const witness = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeaf); + if (!witness) { + throw new Error( + `Leaf ${leaf} not found in the tree ${MerkleTreeId[parsedTreeId]} at block ${parsedBlockNumber}.`, + ); } - return path.map(toACVMField); + return witness.map(toACVMField); } async getAuthWitness([messageHash]: ACVMField[]): Promise { diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 09bb647b199..00131b8cf9b 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -97,7 +97,8 @@ contract LiquidityMining { get_membership_witness(ignored_block_number, note_hash_tree_id, note_commitment); // 1.i) - // In our test case this should be tree root at the latest block and should correspond to the sibling path we obtain from oracle + // In our test case this should be tree root at the latest block and should correspond to the membership + // witness we obtain from oracle let note_hash_tree_root = context.block_data.note_hash_tree_root; assert( note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), From 3bf3036a3b274a503dae9619e34c06b5fa7e0a1d Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 21 Nov 2023 14:39:59 +0000 Subject: [PATCH 15/69] fix --- .../aztec/src/oracle/get_membership_witness.nr | 17 ++++++++++++----- .../liquidity_mining_contract/src/main.nr | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) 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 afdbbe74ff8..3fba3807380 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 @@ -1,18 +1,25 @@ use crate::constants_gen::NOTE_HASH_TREE_HEIGHT; use crate::utils::arr_copy_slice; -struct MembershipWitness { +// Note: We have M here because we need to somehow set it when calling get_membership_witness function and one way to +// do it is to set M here and then set type of the return param, e.g.: +// +// `let witness: MembershipWitness = get_membership_witness(...);` +// +// Another way to do it would be to add "type_hint: [Field; T]" as argument to `get_membership_witness` but that's +// a bit too boilerplatey for my taste. +struct MembershipWitness { index: Field, path: [Field; N], } #[oracle(getMembershipWitness)] -fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf: Field) -> [Field; T] {} +fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf: Field) -> [Field; M] {} -unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf: Field) -> MembershipWitness { - let fields: [Field; T] = get_membership_witness_oracle(block_number, tree_id, leaf); +unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf: Field) -> MembershipWitness { + let fields: [Field; M] = get_membership_witness_oracle(block_number, tree_id, leaf); MembershipWitness { index: fields[0], - path: arr_copy_slice(fields, [0; N], 1) + path: arr_copy_slice(fields, [0; N], 1), } } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 00131b8cf9b..09bd64e7825 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -93,7 +93,7 @@ contract LiquidityMining { // 1.h) let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block let note_hash_tree_id = 2; - let witness: MembershipWitness = + let witness: MembershipWitness = get_membership_witness(ignored_block_number, note_hash_tree_id, note_commitment); // 1.i) From 78eaadb07e83fd15c59a175a4a55456a4b110b17 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 21 Nov 2023 15:45:12 +0000 Subject: [PATCH 16/69] computing blocks hash --- .../liquidity_mining_contract/src/main.nr | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 09bd64e7825..7f42151e7e8 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -2,7 +2,10 @@ mod types; // A demonstration of private liquidity mining. contract LiquidityMining { - use dep::std::merkle::compute_merkle_root; + use dep::std::{ + merkle::compute_merkle_root, + hash::pedersen_hash_with_separator, + }; use dep::aztec::{ state_vars::{ map::Map, @@ -15,7 +18,10 @@ contract LiquidityMining { note_header::NoteHeader, utils as note_utils, }, - constants_gen::NOTE_HASH_TREE_HEIGHT, + constants_gen::{ + NOTE_HASH_TREE_HEIGHT, + GENERATOR_INDEX__BLOCK_HASH, + }, oracle::get_membership_witness::{ get_membership_witness, MembershipWitness, @@ -82,6 +88,21 @@ contract LiquidityMining { ) { let balances = storage.balances.at(owner.address); + // 1.c) + // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. + // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering + // in the block hash preimage --> This seems to require changes in the circuits. + let block_data = context.block_data; + let inputs = [ + block_data.global_variables_hash, + block_data.note_hash_tree_root, + block_data.nullifier_tree_root, + block_data.contract_tree_root, + block_data.l1_to_l2_messages_tree_root, + block_data.public_data_tree_root + ]; + let block_hash = pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH); + // 1.f) let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); @@ -99,7 +120,7 @@ contract LiquidityMining { // 1.i) // In our test case this should be tree root at the latest block and should correspond to the membership // witness we obtain from oracle - let note_hash_tree_root = context.block_data.note_hash_tree_root; + let note_hash_tree_root = block_data.note_hash_tree_root; assert( note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving membership of a note failed" From 5528be53eae603e79ee6d9d1b7e4f71e6e4e07ed Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 21 Nov 2023 16:51:50 +0000 Subject: [PATCH 17/69] WIP on verifying block hash --- .../liquidity_mining_contract/src/main.nr | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 7f42151e7e8..c8e6d41fbc4 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -20,6 +20,7 @@ contract LiquidityMining { }, constants_gen::{ NOTE_HASH_TREE_HEIGHT, + HISTORIC_BLOCKS_TREE_HEIGHT, GENERATOR_INDEX__BLOCK_HASH, }, oracle::get_membership_witness::{ @@ -73,7 +74,7 @@ contract LiquidityMining { // experimental purposes only). // b) Pop all the preimages of block hash from the stack (this contains note hash tree root and timestamp). // c) Compute the block hash. - // d) Pop sibling path of the block hash in the HAT from the stack. + // d) Get the membership witness of a block in the blocks tree. // e) Prove that the block hash is in the HAT. // f) Get the note/preimage from PXE. // g) Compute the commitment from the note. @@ -88,6 +89,8 @@ contract LiquidityMining { ) { let balances = storage.balances.at(owner.address); + let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block + // 1.c) // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering @@ -103,6 +106,20 @@ contract LiquidityMining { ]; let block_hash = pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH); + // 1.d) + let blocks_tree_root = block_data.blocks_tree_root; + let blocks_tree_id = 5; + let witness: MembershipWitness = + get_membership_witness(ignored_block_number, blocks_tree_id, block_hash); + + // 1.e) + // In our test case this should be tree root at the latest block and should correspond to the membership + // witness we obtain from oracle + assert( + blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), + "Proving membership of a block in blocks tree failed" + ); + // 1.f) let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); @@ -112,7 +129,6 @@ contract LiquidityMining { let note_commitment = note_utils::compute_unique_siloed_note_hash(LPNoteMethods, note); // 1.h) - let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block let note_hash_tree_id = 2; let witness: MembershipWitness = get_membership_witness(ignored_block_number, note_hash_tree_id, note_commitment); @@ -122,8 +138,8 @@ contract LiquidityMining { // witness we obtain from oracle let note_hash_tree_root = block_data.note_hash_tree_root; assert( - note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), - "Proving membership of a note failed" + note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), + "Proving membership of a note in note has tree failed" ); // Remove/nullify the note From 61a36829b99c1deb2e51268668ef8c9b0f9fea36 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 23 Nov 2023 13:47:42 +0000 Subject: [PATCH 18/69] fix --- .../aztec-node/src/aztec-node/server.ts | 13 +++++++++++++ .../pxe/src/simulator_oracle/index.ts | 11 ++++------- .../types/src/interfaces/state_provider.ts | 19 ++++++++++++++++++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index b528a699343..b1cd9fc9346 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -3,6 +3,7 @@ import { CONTRACT_TREE_HEIGHT, Fr, GlobalVariables, + HISTORIC_BLOCKS_TREE_HEIGHT, HistoricBlockData, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, @@ -339,6 +340,18 @@ export class AztecNodeService implements AztecNode { return committedDb.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leafIndex); } + /** + * Returns the sibling path for a leaf in the committed historic blocks tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + */ + public async getHistoricBlocksTreeSiblingPath( + leafIndex: bigint, + ): Promise> { + const committedDb = await this.#getWorldState(); + return committedDb.getSiblingPath(MerkleTreeId.BLOCKS_TREE, leafIndex); + } + /** * Gets the storage value at the given contract storage slot. * diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index faf2129b5ce..15502abd011 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -138,22 +138,19 @@ export class SimulatorOracle implements DBOracle { public async findLeafIndex(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { this.log.warn('Block number ignored in SimulatorOracle.findLeafIndex because archival node is not yet implemented'); - switch (treeId) { - case MerkleTreeId.NOTE_HASH_TREE: - return await this.stateInfoProvider.findLeafIndex(treeId, leafValue); - default: - throw new Error('Not implemented'); - } + return await this.stateInfoProvider.findLeafIndex(treeId, leafValue); } public async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise { this.log.warn( 'Block number ignored in SimulatorOracle.getSiblingPath because archival node is not yet implemented', ); - // @todo This is doing a nasty workaround as http_rpc_client was not happy about a generic `getSiblingPath` function being exposed. + // @todo Doing a nasty workaround here because of https://github.com/AztecProtocol/aztec-packages/issues/3414 switch (treeId) { case MerkleTreeId.NOTE_HASH_TREE: return (await this.stateInfoProvider.getNoteHashSiblingPath(leafIndex)).toFieldArray(); + case MerkleTreeId.BLOCKS_TREE: + return (await this.stateInfoProvider.getHistoricBlocksTreeSiblingPath(leafIndex)).toFieldArray(); default: throw new Error('Not implemented'); } diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index a08347cee39..bfef6db4d9e 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -1,4 +1,10 @@ -import { CONTRACT_TREE_HEIGHT, Fr, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT } from '@aztec/circuits.js'; +import { + CONTRACT_TREE_HEIGHT, + Fr, + HISTORIC_BLOCKS_TREE_HEIGHT, + L1_TO_L2_MSG_TREE_HEIGHT, + NOTE_HASH_TREE_HEIGHT, +} from '@aztec/circuits.js'; import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; @@ -20,6 +26,7 @@ export interface StateInfoProvider { * Returns the sibling path for the given index in the contract tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 */ getContractSiblingPath(leafIndex: bigint): Promise>; @@ -27,6 +34,7 @@ export interface StateInfoProvider { * Returns the sibling path for the given index in the note hash tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 */ getNoteHashSiblingPath(leafIndex: bigint): Promise>; @@ -42,6 +50,15 @@ export interface StateInfoProvider { * Returns the sibling path for a leaf in the committed l1 to l2 data tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 */ getL1ToL2MessageSiblingPath(leafIndex: bigint): Promise>; + + /** + * Returns the sibling path for a leaf in the committed historic blocks tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 + */ + getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; } From 39b6011449142d203eb3aec2fe7e8c1201769a01 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 23 Nov 2023 15:23:44 +0000 Subject: [PATCH 19/69] feat: get_block_data.nr --- yarn-project/aztec-nr/aztec/src/abi.nr | 12 ++++++++++++ yarn-project/aztec-nr/aztec/src/oracle.nr | 1 + .../aztec-nr/aztec/src/oracle/get_block_data.nr | 10 ++++++++++ .../liquidity_mining_contract/src/main.nr | 17 ++++++++++++----- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr diff --git a/yarn-project/aztec-nr/aztec/src/abi.nr b/yarn-project/aztec-nr/aztec/src/abi.nr index 541dc73cf3a..1ef9498469b 100644 --- a/yarn-project/aztec-nr/aztec/src/abi.nr +++ b/yarn-project/aztec-nr/aztec/src/abi.nr @@ -166,6 +166,18 @@ impl HistoricBlockData { ] } + pub fn deserialize(deserialized: [Field; HISTORIC_BLOCK_DATA_LENGTH]) -> Self { + HistoricBlockData { + note_hash_tree_root: deserialized[0], + nullifier_tree_root: deserialized[1], + contract_tree_root: deserialized[2], + l1_to_l2_messages_tree_root: deserialized[3], + blocks_tree_root: deserialized[4], + public_data_tree_root: deserialized[5], + global_variables_hash: deserialized[6], + } + } + pub fn empty() -> Self { Self { note_hash_tree_root: 0, nullifier_tree_root: 0, contract_tree_root: 0, l1_to_l2_messages_tree_root: 0, blocks_tree_root: 0, public_data_tree_root: 0, global_variables_hash: 0 } } diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index 03362e631f8..e6a2b544c87 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -12,6 +12,7 @@ mod get_public_key; mod get_secret_key; mod rand; mod enqueue_public_function_call; +mod get_block_data; mod public_call; mod notes; mod storage; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr new file mode 100644 index 00000000000..2a36a6076e8 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr @@ -0,0 +1,10 @@ +use crate::constants_gen::HISTORIC_BLOCK_DATA_LENGTH; +use crate::abi::HistoricBlockData; + +#[oracle(getBlockData)] +fn get_block_data_oracle(_block_number: Field) -> [Field; HISTORIC_BLOCK_DATA_LENGTH] {} + +unconstrained pub fn get_block_data(block_number: Field) -> HistoricBlockData { + let block_data = get_block_data_oracle(block_number); + HistoricBlockData::deserialize(block_data) +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index c8e6d41fbc4..73920e80d7e 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -23,9 +23,12 @@ contract LiquidityMining { HISTORIC_BLOCKS_TREE_HEIGHT, GENERATOR_INDEX__BLOCK_HASH, }, - oracle::get_membership_witness::{ - get_membership_witness, - MembershipWitness, + oracle::{ + get_membership_witness::{ + get_membership_witness, + MembershipWitness, + }, + get_block_data::get_block_data, }, // oracle::debug_log::debug_log_format, }; @@ -86,16 +89,20 @@ contract LiquidityMining { #[aztec(private)] fn claim( owner: AztecAddress, + block_number: Field, // The block at which we'll prove that the note exists ) { let balances = storage.balances.at(owner.address); - let ignored_block_number = 0; // Oracle ignores this now and only gets the sibling path at the latest block + let not_ignored_block_number = block_number; + let ignored_block_number = 0; // Sibling path oracle ignores this now and only gets the path at the latest block // 1.c) + let block_data = get_block_data(not_ignored_block_number); + // let block_data = context.block_data; + // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering // in the block hash preimage --> This seems to require changes in the circuits. - let block_data = context.block_data; let inputs = [ block_data.global_variables_hash, block_data.note_hash_tree_root, From 227553fc95176a32277aa2c5ffe853ace02e6fab Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 23 Nov 2023 16:27:58 +0000 Subject: [PATCH 20/69] finished get_block_data oracle --- .../acir-simulator/src/acvm/oracle/oracle.ts | 10 ++++++++ .../src/acvm/oracle/typed_oracle.ts | 6 ++++- .../acir-simulator/src/client/db_oracle.ts | 10 +++++++- .../src/client/view_data_oracle.ts | 24 ++++++++++++++++++- yarn-project/circuits.js/src/abis/abis.ts | 1 + .../src/e2e_liquidity_mining.test.ts | 19 +++++---------- .../liquidity_mining_contract/src/main.nr | 16 ++++--------- .../pxe/src/simulator_oracle/index.ts | 6 ++++- .../types/src/interfaces/aztec-node.ts | 7 ------ .../types/src/interfaces/state_provider.ts | 8 +++++++ 10 files changed, 72 insertions(+), 35 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index b712c303402..0a790097345 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -64,6 +64,16 @@ export class Oracle { return witness.map(toACVMField); } + async getBlockData([blockNumber]: ACVMField[]): Promise { + 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 { 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 413535794f3..3e2e0ed743e 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -1,4 +1,4 @@ -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'; @@ -80,6 +80,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getBlockData(_blockNumber: number): 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 8eaa12a7411..567c779bc6c 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,7 +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 { MerkleTreeId } from '@aztec/types'; +import { L2Block, MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -130,6 +130,14 @@ export interface DBOracle extends CommitmentsDB { * @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. Undefined if it does not exist in the tree. */ getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise; + + /** + * 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; } 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 e5a781d1d4e..316fa836b52 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -1,5 +1,5 @@ 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'; @@ -51,6 +51,28 @@ export class ViewDataOracle extends TypedOracle { return [new Fr(index), ...siblingPath]; } + /** + * 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 { + 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), // 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. diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index fda355297e4..dd249bbb5d6 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -323,6 +323,7 @@ export function computeBlockHash( * Computes the globals hash given the globals. * @param globals - The global variables to put into the block hash. * @returns The globals hash. + * TODO: move this to GlobalVariables? */ export function computeGlobalsHash(globals: GlobalVariables): Fr { return Fr.fromBuffer( diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts index d2a798c39b0..47ce7e1ec27 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts @@ -1,9 +1,7 @@ -import { AccountWallet, CheatCodes, CompleteAddress, DebugLogger } from '@aztec/aztec.js'; -import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; +import { AccountWallet, CompleteAddress, DebugLogger, PXE } from '@aztec/aztec.js'; import { LiquidityMiningContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; -import levelup from 'levelup'; import { type MemDown, default as memdown } from 'memdown'; import { setup } from './fixtures/utils.js'; @@ -15,25 +13,18 @@ const TIMEOUT = 90_000; describe('e2e_liquidity_mining', () => { jest.setTimeout(TIMEOUT); + let pxe: PXE; let teardown: () => Promise; let logger: DebugLogger; let wallets: AccountWallet[]; let accounts: CompleteAddress[]; - let cheatCodes: CheatCodes; let contract: LiquidityMiningContract; - let simulatorTree: SparseTree; - beforeAll(async () => { - ({ teardown, logger, wallets, accounts, cheatCodes } = await setup(1)); + ({ pxe, teardown, logger, wallets, accounts } = await setup(1)); contract = await LiquidityMiningContract.deploy(wallets[0]).send().deployed(); - - const db = levelup(createMemDown()); - const hasher = new Pedersen(); - const depth = 254; - simulatorTree = await newTree(SparseTree, db, hasher, 'test', depth); }, 100_000); afterAll(() => teardown()); @@ -55,7 +46,9 @@ describe('e2e_liquidity_mining', () => { { // Claim - const receipt = await contract.methods.claim(accounts[0].address).send().wait({ debug: true }); + // We prove the note existence at current block number because we don't currently have historical data + const blockNumber = await pxe.getBlockNumber(); + const receipt = await contract.methods.claim(accounts[0].address, blockNumber).send().wait({ debug: true }); const { newNullifiers } = receipt.debugInfo!; expect(newNullifiers.length).toBe(2); // tx hash and note nullifier } diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index 73920e80d7e..ef690f77043 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -93,12 +93,8 @@ contract LiquidityMining { ) { let balances = storage.balances.at(owner.address); - let not_ignored_block_number = block_number; - let ignored_block_number = 0; // Sibling path oracle ignores this now and only gets the path at the latest block - // 1.c) - let block_data = get_block_data(not_ignored_block_number); - // let block_data = context.block_data; + let block_data = get_block_data(block_number); // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering @@ -114,16 +110,15 @@ contract LiquidityMining { let block_hash = pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH); // 1.d) - let blocks_tree_root = block_data.blocks_tree_root; let blocks_tree_id = 5; let witness: MembershipWitness = - get_membership_witness(ignored_block_number, blocks_tree_id, block_hash); + get_membership_witness(block_number, blocks_tree_id, block_hash); // 1.e) // In our test case this should be tree root at the latest block and should correspond to the membership // witness we obtain from oracle assert( - blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), + block_data.blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in blocks tree failed" ); @@ -138,14 +133,13 @@ contract LiquidityMining { // 1.h) let note_hash_tree_id = 2; let witness: MembershipWitness = - get_membership_witness(ignored_block_number, note_hash_tree_id, note_commitment); + get_membership_witness(block_number, note_hash_tree_id, note_commitment); // 1.i) // In our test case this should be tree root at the latest block and should correspond to the membership // witness we obtain from oracle - let note_hash_tree_root = block_data.note_hash_tree_root; assert( - note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), + block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving membership of a note in note has tree failed" ); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 15502abd011..54d446a0d59 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,7 +10,7 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, MerkleTreeId, StateInfoProvider } from '@aztec/types'; +import { KeyStore, L2Block, MerkleTreeId, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -156,6 +156,10 @@ export class SimulatorOracle implements DBOracle { } } + public async getBlock(blockNumber: number): Promise { + return await this.stateInfoProvider.getBlock(blockNumber); + } + /** * Retrieve the databases view of the Historic Block Data object. * This structure is fed into the circuits simulator and is used to prove against certain historic roots. diff --git a/yarn-project/types/src/interfaces/aztec-node.ts b/yarn-project/types/src/interfaces/aztec-node.ts index a366e7332d5..e03966ec8ae 100644 --- a/yarn-project/types/src/interfaces/aztec-node.ts +++ b/yarn-project/types/src/interfaces/aztec-node.ts @@ -30,13 +30,6 @@ export interface AztecNode extends StateInfoProvider { */ isReady(): Promise; - /** - * Get the a given block. - * @param number - The block number being requested. - * @returns The blocks requested. - */ - getBlock(number: number): Promise; - /** * Method to request blocks. Will attempt to return all requested blocks but will return only those available. * @param from - The start of the range of blocks to return. diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index bfef6db4d9e..af9f36217c6 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -7,6 +7,7 @@ import { } from '@aztec/circuits.js'; import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; +import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; @@ -61,4 +62,11 @@ export interface StateInfoProvider { * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 */ getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; + + /** + * Get the a given block. + * @param number - The block number being requested. + * @returns The blocks requested. + */ + getBlock(number: number): Promise; } From 36358662e4c2ab01d24f03e6fc37a3923c347c4e Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 08:15:00 +0000 Subject: [PATCH 21/69] comment --- .../src/contracts/liquidity_mining_contract/src/main.nr | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index ef690f77043..d94ed4e7715 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -95,6 +95,9 @@ contract LiquidityMining { // 1.c) let block_data = get_block_data(block_number); + + // TODO: This should be injected directly from kernel --> GETTING IT FROM ORACLE IS NOT SAFE! + let blocks_tree_root = block_data.blocks_tree_root; // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering @@ -118,7 +121,7 @@ contract LiquidityMining { // In our test case this should be tree root at the latest block and should correspond to the membership // witness we obtain from oracle assert( - block_data.blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), + blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in blocks tree failed" ); From 71508b1a739c744d4fca12b05ea28d0c5cf65a04 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 08:26:51 +0000 Subject: [PATCH 22/69] HistoricBlockData::block_hash() --- yarn-project/aztec-nr/aztec/src/abi.nr | 15 ++++++++++++++ .../liquidity_mining_contract/src/main.nr | 20 ++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/abi.nr b/yarn-project/aztec-nr/aztec/src/abi.nr index 1ef9498469b..bf5019d9f04 100644 --- a/yarn-project/aztec-nr/aztec/src/abi.nr +++ b/yarn-project/aztec-nr/aztec/src/abi.nr @@ -20,6 +20,7 @@ use crate::constants_gen::{ CONTRACT_STORAGE_READ_LENGTH, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, + GENERATOR_INDEX__BLOCK_HASH, GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS, GENERATOR_INDEX__FUNCTION_DATA, GENERATOR_INDEX__PUBLIC_DATA_READ, @@ -181,6 +182,20 @@ impl HistoricBlockData { pub fn empty() -> Self { Self { note_hash_tree_root: 0, nullifier_tree_root: 0, contract_tree_root: 0, l1_to_l2_messages_tree_root: 0, blocks_tree_root: 0, public_data_tree_root: 0, global_variables_hash: 0 } } + + pub fn block_hash(self) -> Field { + // TODO: Unify the ordering in `HistoricBlockData::serialize` function and the ordering + // in the block hash preimage --> This requires changes in the circuits. + let inputs = [ + self.global_variables_hash, + self.note_hash_tree_root, + self.nullifier_tree_root, + self.contract_tree_root, + self.l1_to_l2_messages_tree_root, + self.public_data_tree_root + ]; + pedersen_hash(inputs, GENERATOR_INDEX__BLOCK_HASH) + } } struct FunctionData { diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr index d94ed4e7715..b9646da876d 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr @@ -2,10 +2,7 @@ mod types; // A demonstration of private liquidity mining. contract LiquidityMining { - use dep::std::{ - merkle::compute_merkle_root, - hash::pedersen_hash_with_separator, - }; + use dep::std::merkle::compute_merkle_root; use dep::aztec::{ state_vars::{ map::Map, @@ -21,7 +18,6 @@ contract LiquidityMining { constants_gen::{ NOTE_HASH_TREE_HEIGHT, HISTORIC_BLOCKS_TREE_HEIGHT, - GENERATOR_INDEX__BLOCK_HASH, }, oracle::{ get_membership_witness::{ @@ -95,22 +91,10 @@ contract LiquidityMining { // 1.c) let block_data = get_block_data(block_number); + let block_hash = block_data.block_hash(); // TODO: This should be injected directly from kernel --> GETTING IT FROM ORACLE IS NOT SAFE! let blocks_tree_root = block_data.blocks_tree_root; - - // TODO: Seems to make sense to move the following to `HistoricBlockData` struct. - // TODO: Would make sense to unify the ordering in `HistoricBlockData::serialize` function and the ordering - // in the block hash preimage --> This seems to require changes in the circuits. - let inputs = [ - block_data.global_variables_hash, - block_data.note_hash_tree_root, - block_data.nullifier_tree_root, - block_data.contract_tree_root, - block_data.l1_to_l2_messages_tree_root, - block_data.public_data_tree_root - ]; - let block_hash = pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH); // 1.d) let blocks_tree_id = 5; From eb5c2608456e11afec6cdfc3faee068f8455690d Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 08:34:01 +0000 Subject: [PATCH 23/69] renaming --- ...ning.test.ts => e2e_inclusion_proofs_contract.test.ts} | 8 ++++---- .../Nargo.toml | 2 +- .../src/main.nr | 4 ++-- .../src/types.nr | 0 .../src/types/lp_note.nr | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename yarn-project/end-to-end/src/{e2e_liquidity_mining.test.ts => e2e_inclusion_proofs_contract.test.ts} (88%) rename yarn-project/noir-contracts/src/contracts/{liquidity_mining_contract => inclusion_proofs_contract}/Nargo.toml (84%) rename yarn-project/noir-contracts/src/contracts/{liquidity_mining_contract => inclusion_proofs_contract}/src/main.nr (98%) rename yarn-project/noir-contracts/src/contracts/{liquidity_mining_contract => inclusion_proofs_contract}/src/types.nr (100%) rename yarn-project/noir-contracts/src/contracts/{liquidity_mining_contract => inclusion_proofs_contract}/src/types/lp_note.nr (100%) diff --git a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts similarity index 88% rename from yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts rename to yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 47ce7e1ec27..cc411e5d4b9 100644 --- a/yarn-project/end-to-end/src/e2e_liquidity_mining.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -1,5 +1,5 @@ import { AccountWallet, CompleteAddress, DebugLogger, PXE } from '@aztec/aztec.js'; -import { LiquidityMiningContract } from '@aztec/noir-contracts/types'; +import { InclusionProofsContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; import { type MemDown, default as memdown } from 'memdown'; @@ -10,7 +10,7 @@ export const createMemDown = () => (memdown as any)() as MemDown; const TIMEOUT = 90_000; -describe('e2e_liquidity_mining', () => { +describe('e2e_inclusion_proofs_contract', () => { jest.setTimeout(TIMEOUT); let pxe: PXE; @@ -19,12 +19,12 @@ describe('e2e_liquidity_mining', () => { let wallets: AccountWallet[]; let accounts: CompleteAddress[]; - let contract: LiquidityMiningContract; + let contract: InclusionProofsContract; beforeAll(async () => { ({ pxe, teardown, logger, wallets, accounts } = await setup(1)); - contract = await LiquidityMiningContract.deploy(wallets[0]).send().deployed(); + contract = await InclusionProofsContract.deploy(wallets[0]).send().deployed(); }, 100_000); afterAll(() => teardown()); diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml similarity index 84% rename from yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml rename to yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml index ea290776b4d..eca5e7d2157 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "liquidity_mining_contract" +name = "inclusion_proofs_contract" authors = [""] compiler_version = ">=0.18.0" type = "contract" diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr similarity index 98% rename from yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr rename to yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr index b9646da876d..9b03d65396c 100644 --- a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr @@ -1,7 +1,7 @@ mod types; -// A demonstration of private liquidity mining. -contract LiquidityMining { +// A demonstration of inclusion proofs. +contract InclusionProofs { use dep::std::merkle::compute_merkle_root; use dep::aztec::{ state_vars::{ diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr similarity index 100% rename from yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types.nr rename to yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr diff --git a/yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr similarity index 100% rename from yarn-project/noir-contracts/src/contracts/liquidity_mining_contract/src/types/lp_note.nr rename to yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr From 508ab15b93d9b36d5f6b93506dda7e31cd6ba1a8 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 09:32:33 +0000 Subject: [PATCH 24/69] cleanup --- .../src/e2e_inclusion_proofs_contract.test.ts | 22 +-- .../inclusion_proofs_contract/Nargo.toml | 3 +- .../inclusion_proofs_contract/src/main.nr | 74 ++++----- .../inclusion_proofs_contract/src/types.nr | 1 - .../src/types/lp_note.nr | 141 ------------------ 5 files changed, 46 insertions(+), 195 deletions(-) delete mode 100644 yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr delete mode 100644 yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr 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 cc411e5d4b9..a6cc1546944 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 @@ -29,26 +29,26 @@ describe('e2e_inclusion_proofs_contract', () => { afterAll(() => teardown()); - it('mints and claims', async () => { - // The account which apes into the pool - const ape = accounts[0].address; + it('creates a note and proves its existence', async () => { + // Owner of a note + const owner = accounts[0].address; { - // Mint - const mintAmount = 100n; - const receipt = await contract.methods.mint(ape, mintAmount).send().wait({ debug: true }); + // Create note + const amount = 100n; + const receipt = await contract.methods.create_note(owner, amount).send().wait({ debug: true }); const { newCommitments, visibleNotes } = receipt.debugInfo!; expect(newCommitments.length).toBe(1); expect(visibleNotes.length).toBe(1); - const [noteAmount, noteOwner, _randomness] = visibleNotes[0].note.items; - expect(noteAmount.value).toBe(mintAmount); - expect(noteOwner).toEqual(ape.toField()); + const [receivedAmount, receivedOwner, _randomness] = visibleNotes[0].note.items; + expect(receivedAmount.toBigInt()).toBe(amount); + expect(receivedOwner).toEqual(owner.toField()); } { - // Claim + // Prove note inclusion in a given block. // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - const receipt = await contract.methods.claim(accounts[0].address, blockNumber).send().wait({ debug: true }); + const receipt = await contract.methods.proveNoteInclusion(accounts[0].address, blockNumber).send().wait({ debug: true }); const { newNullifiers } = receipt.debugInfo!; expect(newNullifiers.length).toBe(2); // tx hash and note nullifier } diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml index eca5e7d2157..633b6030352 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml @@ -6,4 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../../aztec-nr/aztec" } -safe_math = { path = "../../../../aztec-nr/safe-math" } \ No newline at end of file +safe_math = { path = "../../../../aztec-nr/safe-math" } +value_note = { path = "../../../../aztec-nr/value-note" } \ No newline at end of file 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 9b03d65396c..be6cde23b39 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 @@ -1,5 +1,3 @@ -mod types; - // A demonstration of inclusion proofs. contract InclusionProofs { use dep::std::merkle::compute_merkle_root; @@ -28,11 +26,10 @@ contract InclusionProofs { }, // oracle::debug_log::debug_log_format, }; - - use crate::types::lp_note::{LPNote, LPNoteMethods, LP_NOTE_LEN}; + use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; struct Storage { - balances: Map>, + balances: Map>, } impl Storage { @@ -42,7 +39,7 @@ contract InclusionProofs { context, 1, // Storage slot |context, slot| { - Set::new(context, slot, LPNoteMethods) + Set::new(context, slot, ValueNoteMethods) }, ), } @@ -54,54 +51,54 @@ contract InclusionProofs { // Mints `amount` of LP tokens to `owner`. #[aztec(private)] - fn mint( + fn create_note( owner: AztecAddress, amount: Field, ) { let owner_balance = storage.balances.at(owner.address); - let mut note = LPNote::new(amount, owner); + let mut note = ValueNote::new(amount, owner.address); owner_balance.insert(&mut note, true); } - // Proves that the owner owned an LP note for a specific time period and distributes the reward based - // on the deposit amount and the length of time the note was owned. + // Proves that the owner owned a ValueNote at block `block_number`. // // The scheme works as follows: - // 1) Prove that I owned the note at the start of the time period. - // a) Pop HAT root of the stack (this will eventually be injected by kernel since this is not safe and for - // experimental purposes only). - // b) Pop all the preimages of block hash from the stack (this contains note hash tree root and timestamp). - // c) Compute the block hash. - // d) Get the membership witness of a block in the blocks tree. - // e) Prove that the block hash is in the HAT. - // f) Get the note/preimage from PXE. - // g) Compute the commitment from the note. - // h) Get the membership witness of a note in the note hash tree. - // i) Verify that the commitment is in the note hash tree. - // 2) Prove that the note has not yet been nullified by emitting a nullifier. - // 3) Use the timestamp from 1.b), current timestamp and the note amount from 1.f) to compute the reward. - // 4) Transfer the reward to the owner. + // 1) Get historic block data from oracle. + // 2) From that get the blocks tree root (this will eventually be injected by kernel since this is not safe and + // is for experimental purposes only). + // 3) Compute block hash from the historic block data obtained in 1). + // 4) Get the membership witness of a block in the blocks tree. + // 5) Prove that the block hash is in the blocks tree. + // 6) Get the note/preimage from PXE. + // 7) Compute the commitment from the note. + // 8) Get the membership witness of the note in the note hash tree. + // 9) Verify that the commitment is in the note hash tree. + // --> Now we have traversed the trees all the way up to blocks tree root. Last thing remaining is to verify that + // the blocks tree root is in the grandfather tree. That will be done in the root rollup circuit. #[aztec(private)] - fn claim( + fn proveNoteInclusion( owner: AztecAddress, block_number: Field, // The block at which we'll prove that the note exists ) { let balances = storage.balances.at(owner.address); - // 1.c) + // 1) let block_data = get_block_data(block_number); - let block_hash = block_data.block_hash(); + // 2) // TODO: This should be injected directly from kernel --> GETTING IT FROM ORACLE IS NOT SAFE! let blocks_tree_root = block_data.blocks_tree_root; - // 1.d) + // 3) + let block_hash = block_data.block_hash(); + + // 4) let blocks_tree_id = 5; let witness: MembershipWitness = get_membership_witness(block_number, blocks_tree_id, block_hash); - // 1.e) + // 5) // In our test case this should be tree root at the latest block and should correspond to the membership // witness we obtain from oracle assert( @@ -109,36 +106,31 @@ contract InclusionProofs { "Proving membership of a block in blocks tree failed" ); - // 1.f) + // 6) let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); - // 1.g) - let note_commitment = note_utils::compute_unique_siloed_note_hash(LPNoteMethods, note); + // 7) + let note_commitment = note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, note); - // 1.h) + // 8) let note_hash_tree_id = 2; let witness: MembershipWitness = get_membership_witness(block_number, note_hash_tree_id, note_commitment); - // 1.i) - // In our test case this should be tree root at the latest block and should correspond to the membership - // witness we obtain from oracle + // 9) assert( block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving membership of a note in note has tree failed" ); - - // Remove/nullify the note - balances.remove(note); } // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; LP_NOTE_LEN]) -> [Field; 4] { + unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(LPNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr deleted file mode 100644 index b068916f145..00000000000 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types.nr +++ /dev/null @@ -1 +0,0 @@ -mod lp_note; \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr deleted file mode 100644 index c825b4b8110..00000000000 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/types/lp_note.nr +++ /dev/null @@ -1,141 +0,0 @@ -use dep::aztec::{ - note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, - }, - context::PrivateContext, - constants_gen::MAX_READ_REQUESTS_PER_CALL, - state_vars::set::Set, - log::emit_encrypted_log, - hash::pedersen_hash, - types::address::AztecAddress, - oracle::{ - rand::rand, - get_secret_key::get_secret_key, - get_public_key::get_public_key, - }, -}; -use dep::safe_math::SafeU120; -use dep::std::option::Option; - -global LP_NOTE_LEN: Field = 3; // 3 plus a header. - -struct LPNote { - // the amount of LP tokens in the note - // Note: We don't ever do any operations on this value, so we don't need to use SafeU120. - amount: Field, - // the provider of secrets for the nullifier. The owner (recipient) to ensure that the note - // can be privately spent. When nullifier secret and encryption private key is same - // we can simply use the owner for this one. - owner: AztecAddress, - // randomness of the note to hide contents. - randomness: Field, - // the note header (contract_address, nonce, storage_slot) - // included in the note such that it becomes part of encrypted logs for later use. - header: NoteHeader, -} - -impl LPNote { - pub fn new(amount: Field, owner: AztecAddress) -> Self { - Self { - amount, - owner, - randomness: rand(), - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; LP_NOTE_LEN] { - [self.amount, self.owner.address, self.randomness] - } - - pub fn deserialize(serialized_note: [Field; LP_NOTE_LEN]) -> Self { - Self { - amount: serialized_note[0], - owner: AztecAddress::new(serialized_note[1]), - randomness: serialized_note[2], - header: NoteHeader::empty(), - } - } - - pub fn compute_note_hash(self) -> Field { - // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.owner.address as Field, - self.randomness, - ],0) - } - - // docs:start:nullifier - pub fn compute_nullifier(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(LPNoteMethods, self); - let secret = get_secret_key(self.owner.address); - // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - note_hash_for_nullify, - secret.low, - secret.high, - ],0) - } - // docs:end:nullifier - - pub fn set_header(&mut self, header: NoteHeader) { - self.header = header; - } - - // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { - // We only bother inserting the note if non-empty to save funds on gas. - if self.amount != 0 { - let encryption_pub_key = get_public_key(self.owner.address); - emit_encrypted_log( - context, - (*context).this_address(), - slot, - encryption_pub_key, - self.serialize(), - ); - } - } -} - -fn deserialize(serialized_note: [Field; LP_NOTE_LEN]) -> LPNote { - LPNote::deserialize(serialized_note) -} - -fn serialize(note: LPNote) -> [Field; LP_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: LPNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: LPNote) -> Field { - note.compute_nullifier() -} - -fn get_header(note: LPNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut LPNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: LPNote) { - note.broadcast(context, slot); -} - -global LPNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - get_header, - set_header, - broadcast, -}; \ No newline at end of file From cf14859b8937be5106668bcd8454cc3b5c9e8d00 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 09:40:22 +0000 Subject: [PATCH 25/69] cleanup --- .../src/e2e_inclusion_proofs_contract.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 a6cc1546944..75b57cf16e8 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 @@ -1,4 +1,4 @@ -import { AccountWallet, CompleteAddress, DebugLogger, PXE } from '@aztec/aztec.js'; +import { AccountWallet, CompleteAddress, PXE } from '@aztec/aztec.js'; import { InclusionProofsContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; @@ -15,14 +15,13 @@ describe('e2e_inclusion_proofs_contract', () => { let pxe: PXE; let teardown: () => Promise; - let logger: DebugLogger; let wallets: AccountWallet[]; let accounts: CompleteAddress[]; let contract: InclusionProofsContract; beforeAll(async () => { - ({ pxe, teardown, logger, wallets, accounts } = await setup(1)); + ({ pxe, teardown, wallets, accounts } = await setup(1)); contract = await InclusionProofsContract.deploy(wallets[0]).send().deployed(); }, 100_000); @@ -48,7 +47,10 @@ describe('e2e_inclusion_proofs_contract', () => { // Prove note inclusion in a given block. // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - const receipt = await contract.methods.proveNoteInclusion(accounts[0].address, blockNumber).send().wait({ debug: true }); + const receipt = await contract.methods + .proveNoteInclusion(accounts[0].address, blockNumber) + .send() + .wait({ debug: true }); const { newNullifiers } = receipt.debugInfo!; expect(newNullifiers.length).toBe(2); // tx hash and note nullifier } From b10f34c5b8da7fd4be7514ed862666d7e319161f Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 09:40:32 +0000 Subject: [PATCH 26/69] running test in CI --- .circleci/config.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index a732a5be024..0d0833c6363 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -664,6 +664,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 @@ -1094,6 +1105,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 @@ -1130,6 +1142,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 From 38ba6b43c3aee5337f00139bc806ff3fe57ba52a Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 10:10:10 +0000 Subject: [PATCH 27/69] fix --- .../end-to-end/src/e2e_inclusion_proofs_contract.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) 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 75b57cf16e8..e4d3f3e0e7e 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 @@ -47,12 +47,7 @@ describe('e2e_inclusion_proofs_contract', () => { // Prove note inclusion in a given block. // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - const receipt = await contract.methods - .proveNoteInclusion(accounts[0].address, blockNumber) - .send() - .wait({ debug: true }); - const { newNullifiers } = receipt.debugInfo!; - expect(newNullifiers.length).toBe(2); // tx hash and note nullifier + await contract.methods.proveNoteInclusion(accounts[0].address, blockNumber).send().wait(); } }); }); From ab2ba46dd2e6973e75ae1871544dcb0d9976f44e Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 10:39:37 +0000 Subject: [PATCH 28/69] typo --- .../src/contracts/inclusion_proofs_contract/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 be6cde23b39..ea779f421da 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 @@ -122,7 +122,7 @@ contract InclusionProofs { // 9) assert( block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), - "Proving membership of a note in note has tree failed" + "Proving membership of a note in note hash tree failed" ); } From 1819c2438ea6442389a40c0f8c29ee7c1f4ef89f Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 11:21:05 +0000 Subject: [PATCH 29/69] using blocks tree root from context --- .../inclusion_proofs_contract/src/main.nr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 ea779f421da..0b36921d898 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 @@ -64,9 +64,8 @@ contract InclusionProofs { // Proves that the owner owned a ValueNote at block `block_number`. // // The scheme works as follows: - // 1) Get historic block data from oracle. - // 2) From that get the blocks tree root (this will eventually be injected by kernel since this is not safe and - // is for experimental purposes only). + // 1) Get the blocks tree root from context. + // 2) Get historic block data from oracle. // 3) Compute block hash from the historic block data obtained in 1). // 4) Get the membership witness of a block in the blocks tree. // 5) Prove that the block hash is in the blocks tree. @@ -81,14 +80,14 @@ contract InclusionProofs { owner: AztecAddress, block_number: Field, // The block at which we'll prove that the note exists ) { - let balances = storage.balances.at(owner.address); + // TODO: assert that block number is less than the block number of block data + // --> This will require a new oracle method that returns given global variables hash preimage - // 1) - let block_data = get_block_data(block_number); + // 1) + let blocks_tree_root = context.block_data.blocks_tree_root; // 2) - // TODO: This should be injected directly from kernel --> GETTING IT FROM ORACLE IS NOT SAFE! - let blocks_tree_root = block_data.blocks_tree_root; + let block_data = get_block_data(block_number); // 3) let block_hash = block_data.block_hash(); @@ -107,6 +106,7 @@ contract InclusionProofs { ); // 6) + let balances = storage.balances.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); From 0016721f1effc4e388ecdf8f74bdfa6db1ed6674 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 12:07:45 +0000 Subject: [PATCH 30/69] comment --- .../src/contracts/inclusion_proofs_contract/src/main.nr | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 0b36921d898..2e4b8d549df 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 @@ -80,8 +80,9 @@ contract InclusionProofs { owner: AztecAddress, block_number: Field, // The block at which we'll prove that the note exists ) { - // TODO: assert that block number is less than the block number of block data - // --> This will require a new oracle method that returns given global variables hash preimage + // TODO: assert that block number is less than the block number of context.block_data + // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage + // or modifying the private context so that we somehow expose it. // 1) let blocks_tree_root = context.block_data.blocks_tree_root; From 61c6f851e807107e79275166028d21d6402e08ff Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 12:10:52 +0000 Subject: [PATCH 31/69] fix --- yarn-project/end-to-end/src/cli_docs_sandbox.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts index 2e7bdb152f1..4d2aea1628d 100644 --- a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts @@ -106,6 +106,7 @@ EasyPrivateTokenContractArtifact EcdsaAccountContractArtifact EscrowContractArtifact ImportTestContractArtifact +InclusionProofsContractArtifact LendingContractArtifact ParentContractArtifact PendingCommitmentsContractArtifact From 7360736151be703dff7426c43f261213742560d5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 13:47:40 +0000 Subject: [PATCH 32/69] feat: note utils compute_siloed_nullifier --- yarn-project/aztec-nr/aztec/src/note/utils.nr | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/note/utils.nr b/yarn-project/aztec-nr/aztec/src/note/utils.nr index bce81ff225e..cf40eaeba9b 100644 --- a/yarn-project/aztec-nr/aztec/src/note/utils.nr +++ b/yarn-project/aztec-nr/aztec/src/note/utils.nr @@ -1,9 +1,13 @@ -use crate::note::{ - note_hash::{compute_inner_hash, compute_siloed_hash, compute_unique_hash}, - note_header::NoteHeader, - note_interface::NoteInterface, +use crate::{ + constants_gen::GENERATOR_INDEX__OUTER_NULLIFIER, + note::{ + note_hash::{compute_inner_hash, compute_siloed_hash, compute_unique_hash}, + note_header::NoteHeader, + note_interface::NoteInterface, + }, + utils::arr_copy_slice, + hash::pedersen_hash, }; -use crate::utils::arr_copy_slice; pub fn compute_inner_note_hash(note_interface: NoteInterface, note: Note) -> Field { let get_header = note_interface.get_header; @@ -33,6 +37,17 @@ pub fn compute_unique_siloed_note_hash(note_interface: NoteInterface(note_interface: NoteInterface, note_with_header: Note) -> Field { + let get_header = note_interface.get_header; + let header = get_header(note_with_header); + + let compute_nullifier = note_interface.compute_nullifier; + let inner_nullifier = compute_nullifier(note_with_header); + + let input = [header.contract_address, inner_nullifier]; + pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) +} + pub fn compute_note_hash_for_read_or_nullify(note_interface: NoteInterface, note_with_header: Note) -> Field { let get_header = note_interface.get_header; let header = get_header(note_with_header); From f407ce73c48d308416c0b8b5cc3f152df9504c2f Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 13:48:09 +0000 Subject: [PATCH 33/69] get_low_nullifier_membership_witness.nr --- yarn-project/aztec-nr/aztec/src/oracle.nr | 1 + .../get_low_nullifier_membership_witness.nr | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index e6a2b544c87..f16cc41ccab 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -7,6 +7,7 @@ mod call_private_function; mod context; mod debug_log; mod get_l1_to_l2_message; +mod get_low_nullifier_membership_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr new file mode 100644 index 00000000000..7b407083177 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -0,0 +1,36 @@ +use crate::constants_gen::NULLIFIER_TREE_HEIGHT; +use crate::utils::arr_copy_slice; + +global LEAF_DATA_LENGTH: Field = 3; +// TODO: move this to constants.hpp so that it gets computed as LEAF_DATA_LENGTH + NULLIFIER_TREE_HEIGHT +global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 23; + +// Noir version of LeafData interface from indexed merkle tree. +struct LeafData { + value: Field, + next_index: Field, + next_value: Field, +} + +struct LowNullifierMembershipWitness { + leaf_preimage: LeafData, + path: [Field; NULLIFIER_TREE_HEIGHT], +} + +#[oracle(getLowNullifierMembershipWitness)] +fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_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) -> LowNullifierMembershipWitness { + let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); + let preimage = LeafData { + value: fields[0], + next_index: fields[1], + next_value: fields[2], + }; + LowNullifierMembershipWitness { + leaf_preimage: preimage, + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], LEAF_DATA_LENGTH), + } +} \ No newline at end of file From 4b779affe148372b5f5a9c9ada413dd009322433 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 13:59:11 +0000 Subject: [PATCH 34/69] updated get_low_nullifier_membership_witness.nr --- .../get_low_nullifier_membership_witness.nr | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr index 7b407083177..6c8da640cc6 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -1,19 +1,31 @@ use crate::constants_gen::NULLIFIER_TREE_HEIGHT; use crate::utils::arr_copy_slice; +use crate::hash::pedersen_hash; -global LEAF_DATA_LENGTH: Field = 3; -// TODO: move this to constants.hpp so that it gets computed as LEAF_DATA_LENGTH + NULLIFIER_TREE_HEIGHT +global LEAF_PREIMAGE_LENGTH: Field = 3; +// TODO: move this to constants.hpp so that it gets computed as LEAF_PREIMAGE_LENGTH + NULLIFIER_TREE_HEIGHT global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 23; -// Noir version of LeafData interface from indexed merkle tree. -struct LeafData { +// Noir version of LeafPreimage interface from indexed merkle tree. +struct LeafPreimage { value: Field, next_index: Field, next_value: Field, } +impl LeafPreimage { + fn serialize(self) -> [Field; LEAF_PREIMAGE_LENGTH] { + [self.value, self.next_index, self.next_value] + } + + fn hash(self) -> Field { + // Performs the same hashing as StandardIndexedTree::encodeLeaf(...) + pedersen_hash(self.serialize(), 0) + } +} + struct LowNullifierMembershipWitness { - leaf_preimage: LeafData, + leaf_preimage: LeafPreimage, path: [Field; NULLIFIER_TREE_HEIGHT], } @@ -24,13 +36,13 @@ fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: // nullifier's next_value is bigger than the nullifier) unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); - let preimage = LeafData { + let preimage = LeafPreimage { value: fields[0], next_index: fields[1], next_value: fields[2], }; LowNullifierMembershipWitness { leaf_preimage: preimage, - path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], LEAF_DATA_LENGTH), + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], LEAF_PREIMAGE_LENGTH), } } \ No newline at end of file From 1a5c4ab445dde867ffd45a2fd2d15b7defe4615d Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 13:59:30 +0000 Subject: [PATCH 35/69] WIP on proveNullifierNonInclusion --- .../inclusion_proofs_contract/src/main.nr | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) 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 2e4b8d549df..5e47825a3cd 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 @@ -18,11 +18,15 @@ contract InclusionProofs { HISTORIC_BLOCKS_TREE_HEIGHT, }, oracle::{ + get_block_data::get_block_data, get_membership_witness::{ get_membership_witness, MembershipWitness, }, - get_block_data::get_block_data, + get_low_nullifier_membership_witness::{ + get_low_nullifier_membership_witness, + LowNullifierMembershipWitness, + }, }, // oracle::debug_log::debug_log_format, }; @@ -127,6 +131,25 @@ contract InclusionProofs { ); } + #[aztec(private)] + fn proveNullifierNonInclusion( + owner: AztecAddress, + block_number: Field, // The block at which we'll prove that the nullifier does not exists + ) { + // TODO: assert that block number is less than the block number of context.block_data + // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage + // or modifying the private context so that we somehow expose it. + + let balances = storage.balances.at(owner.address); + let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); + let notes = balances.get_notes(options); + let note = notes[0].unwrap(); + + let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); + + let low_nullifier_membership_witness = get_low_nullifier_membership_witness(block_number, nullifier); + } + // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. From ec0d319c5efcbdceb0b499c93bf847e316fe1e61 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 15:51:39 +0000 Subject: [PATCH 36/69] WIP --- .../get_low_nullifier_membership_witness.nr | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr index 6c8da640cc6..b2265542307 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -3,10 +3,10 @@ use crate::utils::arr_copy_slice; use crate::hash::pedersen_hash; global LEAF_PREIMAGE_LENGTH: Field = 3; -// TODO: move this to constants.hpp so that it gets computed as LEAF_PREIMAGE_LENGTH + NULLIFIER_TREE_HEIGHT -global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 23; +// TODO: move this to constants.hpp so that it gets computed as INDEX_LENGTH + LEAF_PREIMAGE_LENGTH + NULLIFIER_TREE_HEIGHT +global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; -// Noir version of LeafPreimage interface from indexed merkle tree. +// Noir version of LeafData interface from indexed merkle tree. struct LeafPreimage { value: Field, next_index: Field, @@ -25,6 +25,7 @@ impl LeafPreimage { } struct LowNullifierMembershipWitness { + index: Field, leaf_preimage: LeafPreimage, path: [Field; NULLIFIER_TREE_HEIGHT], } @@ -37,12 +38,13 @@ fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); let preimage = LeafPreimage { - value: fields[0], - next_index: fields[1], - next_value: fields[2], + value: fields[1], + next_index: fields[2], + next_value: fields[3], }; LowNullifierMembershipWitness { + index: fields[0], leaf_preimage: preimage, - path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], LEAF_PREIMAGE_LENGTH), + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_PREIMAGE_LENGTH), } } \ No newline at end of file From b9699d7a25f37f8d9fb79fed74b99f513231b330 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 15:54:47 +0000 Subject: [PATCH 37/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 16 ++++++++++ .../src/acvm/oracle/typed_oracle.ts | 4 +++ .../acir-simulator/src/client/db_oracle.ts | 27 ++++++++++++++++ .../src/client/view_data_oracle.ts | 25 +++++++++++++++ .../pxe/src/simulator_oracle/index.ts | 31 +++++++++++++++++++ .../types/src/interfaces/state_provider.ts | 26 ++++++++++++++++ 6 files changed, 129 insertions(+) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 0a790097345..b8d204fb63e 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -64,6 +64,22 @@ export class Oracle { return witness.map(toACVMField); } + async getLowNullifierMembershipWitness( + [blockNumber]: ACVMField[], + [nullifier]: ACVMField[], // nullifier, we try to find the low nullifier witness for (to prove non-inclusion) + ): Promise { + 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.map(toACVMField); + } + async getBlockData([blockNumber]: ACVMField[]): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); 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 3e2e0ed743e..839719ce6a0 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -80,6 +80,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getLowNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { + throw new Error('Not available.'); + } + getBlockData(_blockNumber: number): 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 567c779bc6c..a181d0f9977 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -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 { LeafData } from '@aztec/merkle-tree'; import { L2Block, MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; @@ -134,6 +135,32 @@ export interface DBOracle extends CommitmentsDB { */ getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise; + /** + * Returns a previous index at a block for a given value in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param nullifier - Nullifier we try to find the low nullifier index for. + */ + getLowNullifierIndex( + blockNumber: number, + nullifier: Fr, + ): Promise<{ + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + }>; + + /** + * Returns a leaf data at a block for a given index in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param index - The index of the leaf to get. + */ + getNullifierLeafData(blockNumber: number, index: bigint): Promise; + /** * Fetch a block corresponding to the given block number. * @param blockNumber - The block number of a block to fetch. 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 316fa836b52..4b858ada9da 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -51,6 +51,31 @@ export class ViewDataOracle extends TypedOracle { return [new Fr(index), ...siblingPath]; } + /** + * Fetches the index and sibling path for a leaf value + * @param blockNumber - The block number at which to get the membership witness. + * @param nullifier - The nullifier we try to find the low nullifier witness for (to prove non-inclusion). + * @returns The index and sibling path concatenated [index, sibling_path] + */ + public async getLowNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise { + const { index } = await this.db.getLowNullifierIndex(blockNumber, nullifier); + if (!index) { + throw new Error(`Low nullifier not found for nullifier ${nullifier}`); + } + const leafData = await this.db.getNullifierLeafData(blockNumber, index); + if (!leafData) { + throw new Error(`Leaf data not found for index ${index}`); + } + const siblingPath = await this.db.getSiblingPath(blockNumber, MerkleTreeId.NULLIFIER_TREE, index); + return [ + new Fr(index), + new Fr(leafData.value), + new Fr(leafData.nextIndex), + new Fr(leafData.nextValue), + ...siblingPath, + ]; + } + /** * Fetches historic block data for a given block. * @param blockNumber - The block number at which to get the historic block data. diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 54d446a0d59..2c307f54fbf 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -14,6 +14,7 @@ import { KeyStore, L2Block, MerkleTreeId, StateInfoProvider } from '@aztec/types import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; +import { LeafData } from '@aztec/merkle-tree'; /** * A data oracle that provides information needed for simulating a transaction. @@ -156,6 +157,36 @@ export class SimulatorOracle implements DBOracle { } } + /** + * Returns a previous index at a block for a given value in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param nullifier - Nullifier we try to find the low nullifier index for. + */ + public async getLowNullifierIndex( + blockNumber: number, + nullifier: Fr, + ): Promise<{ + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + }> { + + } + + /** + * Returns a leaf data at a block for a given index in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param index - The index of the leaf to get. + */ + getNullifierLeafData(blockNumber: number, index: bigint): Promise { + + } + public async getBlock(blockNumber: number): Promise { return await this.stateInfoProvider.getBlock(blockNumber); } diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index af9f36217c6..b88a3d2ade7 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -63,6 +63,32 @@ export interface StateInfoProvider { */ getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; + /** + * Returns a previous index at a block for a given value in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param nullifier - Nullifier we try to find the low nullifier index for. + */ + getLowNullifierIndex( + blockNumber: number, + nullifier: Fr, + ): Promise<{ + /** + * The index of the found leaf. + */ + index: bigint; + /** + * A flag indicating if the corresponding leaf's value is equal to `newValue`. + */ + alreadyPresent: boolean; + }>; + + /** + * Returns a leaf data at a block for a given index in the nullifier tree. + * @param blockNumber - The block number at which to get the index. + * @param index - The index of the leaf to get. + */ + getNullifierLeafData(blockNumber: number, index: bigint): Promise; + /** * Get the a given block. * @param number - The block number being requested. From ff79a1f8b25e6660ab5e52fc40022b236ba1ff90 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 24 Nov 2023 15:56:10 +0000 Subject: [PATCH 38/69] naming fix --- .../get_low_nullifier_membership_witness.nr | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr index b2265542307..a0d6e94b1ae 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -2,19 +2,19 @@ use crate::constants_gen::NULLIFIER_TREE_HEIGHT; use crate::utils::arr_copy_slice; use crate::hash::pedersen_hash; -global LEAF_PREIMAGE_LENGTH: Field = 3; -// TODO: move this to constants.hpp so that it gets computed as INDEX_LENGTH + LEAF_PREIMAGE_LENGTH + NULLIFIER_TREE_HEIGHT +global LEAF_DATA_LENGTH: Field = 3; +// TODO: move this to constants.hpp so that it gets computed as INDEX_LENGTH + LEAF_DATA_LENGTH + NULLIFIER_TREE_HEIGHT global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; // Noir version of LeafData interface from indexed merkle tree. -struct LeafPreimage { +struct LeafData { value: Field, next_index: Field, next_value: Field, } -impl LeafPreimage { - fn serialize(self) -> [Field; LEAF_PREIMAGE_LENGTH] { +impl LeafData { + fn serialize(self) -> [Field; LEAF_DATA_LENGTH] { [self.value, self.next_index, self.next_value] } @@ -26,7 +26,7 @@ impl LeafPreimage { struct LowNullifierMembershipWitness { index: Field, - leaf_preimage: LeafPreimage, + leaf_data: LeafData, path: [Field; NULLIFIER_TREE_HEIGHT], } @@ -37,14 +37,13 @@ fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: // nullifier's next_value is bigger than the nullifier) unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); - let preimage = LeafPreimage { - value: fields[1], - next_index: fields[2], - next_value: fields[3], - }; LowNullifierMembershipWitness { index: fields[0], - leaf_preimage: preimage, - path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_PREIMAGE_LENGTH), + leaf_data: LeafData { + value: fields[1], + next_index: fields[2], + next_value: fields[3], + }, + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_DATA_LENGTH), } } \ No newline at end of file From 5533c1ad8463b2b9344838c23194d0d76c20e3e3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 09:29:37 +0000 Subject: [PATCH 39/69] WIP --- .../acir-simulator/src/client/db_oracle.ts | 3 +-- .../src/interfaces/indexed_tree.ts | 20 +------------------ .../src/interfaces/update_only_tree.ts | 3 ++- .../standard_indexed_tree.ts | 4 ++-- .../test/standard_indexed_tree_with_append.ts | 3 ++- .../pxe/src/simulator_oracle/index.ts | 3 +-- yarn-project/types/src/interfaces/index.ts | 1 + .../types/src/interfaces/leaf_data.ts | 17 ++++++++++++++++ .../types/src/interfaces/state_provider.ts | 1 + .../merkle_tree_operations_facade.ts | 13 +++--------- .../world-state/src/world-state-db/index.ts | 1 - .../src/world-state-db/merkle_tree_db.ts | 4 ++-- .../src/world-state-db/merkle_trees.ts | 3 +-- 13 files changed, 34 insertions(+), 42 deletions(-) create mode 100644 yarn-project/types/src/interfaces/leaf_data.ts diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index a181d0f9977..3b8c7cd40e2 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,8 +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 { LeafData } from '@aztec/merkle-tree'; -import { L2Block, MerkleTreeId } from '@aztec/types'; +import { L2Block, LeafData, MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index a684541fa48..1b8bade092e 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -1,26 +1,8 @@ -import { SiblingPath } from '@aztec/types'; +import { LeafData, SiblingPath } from '@aztec/types'; import { LowLeafWitnessData } from '../index.js'; import { AppendOnlyTree } from './append_only_tree.js'; -/** - * A leaf of a tree. - */ -export interface LeafData { - /** - * A value of the leaf. - */ - value: bigint; - /** - * An index of the next leaf. - */ - nextIndex: bigint; - /** - * A value of the next leaf. - */ - nextValue: bigint; -} - /** * Indexed merkle tree. */ diff --git a/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts b/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts index df77c5fd6cf..59a82d0b118 100644 --- a/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/update_only_tree.ts @@ -1,4 +1,5 @@ -import { LeafData } from '../index.js'; +import { LeafData } from '@aztec/types'; + import { MerkleTree } from './merkle_tree.js'; /** diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 62a1f69cb76..4cf3899abfd 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -1,8 +1,8 @@ import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; import { createDebugLogger } from '@aztec/foundation/log'; -import { SiblingPath } from '@aztec/types'; +import { LeafData, SiblingPath } from '@aztec/types'; -import { IndexedTree, LeafData } from '../interfaces/indexed_tree.js'; +import { IndexedTree } from '../interfaces/indexed_tree.js'; import { TreeBase } from '../tree_base.js'; const log = createDebugLogger('aztec:standard-indexed-tree'); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index e3ebc9b167b..49a90e611f1 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -1,6 +1,7 @@ import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; +import { LeafData } from '@aztec/types'; -import { LeafData, StandardIndexedTree } from '../../index.js'; +import { StandardIndexedTree } from '../../index.js'; /** * A testing utility which is here to store the original implementation of StandardIndexedTree.appendLeaves method diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 2c307f54fbf..3add420b47a 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,11 +10,10 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, L2Block, MerkleTreeId, StateInfoProvider } from '@aztec/types'; +import { KeyStore, L2Block, LeafData, MerkleTreeId, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; -import { LeafData } from '@aztec/merkle-tree'; /** * A data oracle that provides information needed for simulating a transaction. diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index ddef4f6b1f8..4f3b2a86729 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -6,3 +6,4 @@ export * from './deployed-contract.js'; export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; +export * from './leaf_data.js'; diff --git a/yarn-project/types/src/interfaces/leaf_data.ts b/yarn-project/types/src/interfaces/leaf_data.ts new file mode 100644 index 00000000000..2edc8e09818 --- /dev/null +++ b/yarn-project/types/src/interfaces/leaf_data.ts @@ -0,0 +1,17 @@ +/** + * A leaf of a tree. + */ +export interface LeafData { + /** + * A value of the leaf. + */ + value: bigint; + /** + * An index of the next leaf. + */ + nextIndex: bigint; + /** + * A value of the next leaf. + */ + nextValue: bigint; +} diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index b88a3d2ade7..246643d3eee 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -10,6 +10,7 @@ import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; +import { LeafData } from './leaf_data.js'; /** * Interface providing methods for retrieving information about content of the state trees. diff --git a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts index 7dfb03d8db2..5917a863694 100644 --- a/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/merkle-tree/merkle_tree_operations_facade.ts @@ -1,15 +1,8 @@ import { Fr } from '@aztec/foundation/fields'; import { LowLeafWitnessData } from '@aztec/merkle-tree'; -import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; - -import { - CurrentTreeRoots, - HandleL2BlockResult, - LeafData, - MerkleTreeDb, - MerkleTreeOperations, - TreeInfo, -} from '../index.js'; +import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; + +import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeDb, MerkleTreeOperations, TreeInfo } from '../index.js'; /** * Wraps a MerkleTreeDbOperations to call all functions with a preset includeUncommitted flag. diff --git a/yarn-project/world-state/src/world-state-db/index.ts b/yarn-project/world-state/src/world-state-db/index.ts index 8c0a92a77dd..9ebc6f456d7 100644 --- a/yarn-project/world-state/src/world-state-db/index.ts +++ b/yarn-project/world-state/src/world-state-db/index.ts @@ -1,3 +1,2 @@ export * from './merkle_trees.js'; export * from './merkle_tree_db.js'; -export { LeafData } from '@aztec/merkle-tree'; diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 0f23abaa257..04f4d749fee 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -1,8 +1,8 @@ import { MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { LeafData, LowLeafWitnessData } from '@aztec/merkle-tree'; -import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { LowLeafWitnessData } from '@aztec/merkle-tree'; +import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; /** * Type alias for the nullifier tree ID. diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index 16275724b9b..4cc11afec3f 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -16,7 +16,6 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { AppendOnlyTree, IndexedTree, - LeafData, LowLeafWitnessData, Pedersen, SparseTree, @@ -26,7 +25,7 @@ import { loadTree, newTree, } from '@aztec/merkle-tree'; -import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types'; +import { L2Block, LeafData, MerkleTreeId, SiblingPath } from '@aztec/types'; import { default as levelup } from 'levelup'; From 9e8890cc2870f546b438ed12f1a7cda93ed27017 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 11:35:17 +0000 Subject: [PATCH 40/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 6 +-- .../src/acvm/oracle/typed_oracle.ts | 4 +- .../acir-simulator/src/client/db_oracle.ts | 29 ++++--------- .../src/client/view_data_oracle.ts | 32 +++++---------- .../aztec-node/src/aztec-node/server.ts | 25 +++++++++++ yarn-project/aztec-nr/aztec/src/oracle.nr | 2 +- ...itness.nr => get_low_nullifier_witness.nr} | 8 ++-- .../inclusion_proofs_contract/src/main.nr | 6 +-- .../pxe/src/simulator_oracle/index.ts | 32 ++------------- yarn-project/types/src/interfaces/index.ts | 1 + .../types/src/interfaces/leaf_data.ts | 2 +- .../src/interfaces/low_nullifier_witness.ts | 41 +++++++++++++++++++ .../types/src/interfaces/state_provider.ts | 27 ++++-------- 13 files changed, 109 insertions(+), 106 deletions(-) rename yarn-project/aztec-nr/aztec/src/oracle/{get_low_nullifier_membership_witness.nr => get_low_nullifier_witness.nr} (77%) create mode 100644 yarn-project/types/src/interfaces/low_nullifier_witness.ts diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index b8d204fb63e..741bb15849f 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -64,20 +64,20 @@ export class Oracle { return witness.map(toACVMField); } - async getLowNullifierMembershipWitness( + async getLowNullifierWitness( [blockNumber]: ACVMField[], [nullifier]: ACVMField[], // nullifier, we try to find the low nullifier witness for (to prove non-inclusion) ): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); const parsedNullifier = fromACVMField(nullifier); - const witness = await this.typedOracle.getLowNullifierMembershipWitness(parsedBlockNumber, parsedNullifier); + const witness = await this.typedOracle.getLowNullifierWitness(parsedBlockNumber, parsedNullifier); if (!witness) { throw new Error( `Low nullifier witness not found for nullifier ${parsedNullifier} at block ${parsedBlockNumber}.`, ); } - return witness.map(toACVMField); + return witness.toFieldArray().map(toACVMField); } async getBlockData([blockNumber]: ACVMField[]): Promise { 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 839719ce6a0..301583c6d0b 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -3,7 +3,7 @@ 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, MerkleTreeId, Note, PublicKey, UnencryptedL2Log } from '@aztec/types'; +import { CompleteAddress, LowNullifierWitness, MerkleTreeId, Note, PublicKey, UnencryptedL2Log } from '@aztec/types'; /** * Information about a note needed during execution. @@ -80,7 +80,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getLowNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { + getLowNullifierWitness(_blockNumber: number, _nullifier: Fr): 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 3b8c7cd40e2..c5dff614f6d 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,7 +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, LeafData, MerkleTreeId } from '@aztec/types'; +import { L2Block, LowNullifierWitness, MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -135,30 +135,15 @@ export interface DBOracle extends CommitmentsDB { getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise; /** - * Returns a previous index at a block for a given value in the nullifier tree. + * Returns a low nullifier 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 index for. + * @returns The low nullifier witness. + * @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. */ - getLowNullifierIndex( - blockNumber: number, - nullifier: Fr, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }>; - - /** - * Returns a leaf data at a block for a given index in the nullifier tree. - * @param blockNumber - The block number at which to get the index. - * @param index - The index of the leaf to get. - */ - getNullifierLeafData(blockNumber: number, index: bigint): Promise; + getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise; /** * Fetch a block corresponding to the given block number. 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 4b858ada9da..1ecb30f1d3e 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,7 @@ 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 } from '@aztec/types'; +import { AuthWitness, AztecNode, CompleteAddress, LowNullifierWitness, MerkleTreeId } from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -52,28 +52,16 @@ export class ViewDataOracle extends TypedOracle { } /** - * Fetches the index and sibling path for a leaf value - * @param blockNumber - The block number at which to get the membership witness. - * @param nullifier - The nullifier we try to find the low nullifier witness for (to prove non-inclusion). - * @returns The index and sibling path concatenated [index, sibling_path] + * Returns a low nullifier 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 index for. + * @returns The low nullifier witness. + * @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 { - const { index } = await this.db.getLowNullifierIndex(blockNumber, nullifier); - if (!index) { - throw new Error(`Low nullifier not found for nullifier ${nullifier}`); - } - const leafData = await this.db.getNullifierLeafData(blockNumber, index); - if (!leafData) { - throw new Error(`Leaf data not found for index ${index}`); - } - const siblingPath = await this.db.getSiblingPath(blockNumber, MerkleTreeId.NULLIFIER_TREE, index); - return [ - new Fr(index), - new Fr(leafData.value), - new Fr(leafData.nextIndex), - new Fr(leafData.nextValue), - ...siblingPath, - ]; + public async getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { + return await this.db.getLowNullifierWitness(blockNumber, nullifier); } /** diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index b1cd9fc9346..97dc4380dbe 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -7,6 +7,7 @@ import { HistoricBlockData, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, + NULLIFIER_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; @@ -34,6 +35,7 @@ import { L2Tx, LogFilter, LogType, + LowNullifierWitness, MerkleTreeId, SequencerConfig, SiblingPath, @@ -352,6 +354,29 @@ export class AztecNodeService implements AztecNode { return committedDb.getSiblingPath(MerkleTreeId.BLOCKS_TREE, leafIndex); } + /** + * Returns a low nullifier 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 index for. + * @returns The low nullifier witness. + * @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 getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { + const committedDb = await this.#getWorldState(); + const { index } = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, index); + if (!leafData) { + return undefined; + } + const siblingPath = await committedDb.getSiblingPath( + MerkleTreeId.NULLIFIER_TREE, + BigInt(index), + ); + return new LowNullifierWitness(BigInt(index), leafData, siblingPath); + } + /** * Gets the storage value at the given contract storage slot. * diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index f16cc41ccab..56617d8ddd2 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -7,7 +7,7 @@ mod call_private_function; mod context; mod debug_log; mod get_l1_to_l2_message; -mod get_low_nullifier_membership_witness; +mod get_low_nullifier_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr similarity index 77% rename from yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr rename to yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr index a0d6e94b1ae..2994eb4eef3 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr @@ -30,13 +30,13 @@ struct LowNullifierMembershipWitness { path: [Field; NULLIFIER_TREE_HEIGHT], } -#[oracle(getLowNullifierMembershipWitness)] -fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_NULLIFIER_MEMBERSHIP_WITNESS] {} +#[oracle(getLowNullifierWitness)] +fn get_low_nullifier_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_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) -> LowNullifierMembershipWitness { - let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); +unconstrained pub fn get_low_nullifier_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { + let fields = get_low_nullifier_witness_oracle(block_number, nullifier); LowNullifierMembershipWitness { index: fields[0], leaf_data: LeafData { 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 5e47825a3cd..05dc040ac0b 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 @@ -23,8 +23,8 @@ contract InclusionProofs { get_membership_witness, MembershipWitness, }, - get_low_nullifier_membership_witness::{ - get_low_nullifier_membership_witness, + get_low_nullifier_witness::{ + get_low_nullifier_witness, LowNullifierMembershipWitness, }, }, @@ -147,7 +147,7 @@ contract InclusionProofs { let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); - let low_nullifier_membership_witness = get_low_nullifier_membership_witness(block_number, nullifier); + let low_nullifier_membership_witness = get_low_nullifier_witness(block_number, nullifier); } // Computes note hash and nullifier. diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 3add420b47a..0161f1ad5c6 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,7 +10,7 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, L2Block, LeafData, MerkleTreeId, StateInfoProvider } from '@aztec/types'; +import { KeyStore, L2Block, LowNullifierWitness, MerkleTreeId, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -156,34 +156,8 @@ export class SimulatorOracle implements DBOracle { } } - /** - * Returns a previous index at a block for a given value in the nullifier tree. - * @param blockNumber - The block number at which to get the index. - * @param nullifier - Nullifier we try to find the low nullifier index for. - */ - public async getLowNullifierIndex( - blockNumber: number, - nullifier: Fr, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }> { - - } - - /** - * Returns a leaf data at a block for a given index in the nullifier tree. - * @param blockNumber - The block number at which to get the index. - * @param index - The index of the leaf to get. - */ - getNullifierLeafData(blockNumber: number, index: bigint): Promise { - + public getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { + return this.stateInfoProvider.getLowNullifierWitness(blockNumber, nullifier); } public async getBlock(blockNumber: number): Promise { diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index 4f3b2a86729..ae00b65d4b6 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -7,3 +7,4 @@ export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; export * from './leaf_data.js'; +export * from './low_nullifier_witness.js'; \ No newline at end of file diff --git a/yarn-project/types/src/interfaces/leaf_data.ts b/yarn-project/types/src/interfaces/leaf_data.ts index 2edc8e09818..185102e7435 100644 --- a/yarn-project/types/src/interfaces/leaf_data.ts +++ b/yarn-project/types/src/interfaces/leaf_data.ts @@ -14,4 +14,4 @@ export interface LeafData { * A value of the next leaf. */ nextValue: bigint; -} +} \ No newline at end of file diff --git a/yarn-project/types/src/interfaces/low_nullifier_witness.ts b/yarn-project/types/src/interfaces/low_nullifier_witness.ts new file mode 100644 index 00000000000..c62928973eb --- /dev/null +++ b/yarn-project/types/src/interfaces/low_nullifier_witness.ts @@ -0,0 +1,41 @@ +import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; + +import { SiblingPath } from '../sibling_path.js'; +import { LeafData } from './leaf_data.js'; + +/** + * Low nullifier witness. + * @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. + */ +export class LowNullifierWitness { + constructor( + /** + * The index of low nullifier. + */ + public readonly index: bigint, + /** + * Preimage of the low nullifier that proves non membership. + */ + public readonly leafData: LeafData, + /** + * Sibling path to prove membership of low nullifier. + */ + public readonly siblingPath: SiblingPath, + ) {} + + /** + * Returns a field array representation of the low nullifier witness. + * @returns A field array representation of the low nullifier witness. + */ + public toFieldArray(): Fr[] { + return [ + new Fr(this.index), + new Fr(this.leafData.value), + new Fr(this.leafData.nextIndex), + new Fr(this.leafData.nextValue), + ...this.siblingPath.toFieldArray(), + ]; + } +} diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 246643d3eee..5e86d7578c5 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -11,6 +11,7 @@ import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; import { LeafData } from './leaf_data.js'; +import { LowNullifierWitness } from './low_nullifier_witness.js'; /** * Interface providing methods for retrieving information about content of the state trees. @@ -65,30 +66,18 @@ export interface StateInfoProvider { getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; /** - * Returns a previous index at a block for a given value in the nullifier tree. + * Returns a low nullifier 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 index for. + * @returns The low nullifier witness. + * @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. */ - getLowNullifierIndex( + getLowNullifierWitness( blockNumber: number, nullifier: Fr, - ): Promise<{ - /** - * The index of the found leaf. - */ - index: bigint; - /** - * A flag indicating if the corresponding leaf's value is equal to `newValue`. - */ - alreadyPresent: boolean; - }>; - - /** - * Returns a leaf data at a block for a given index in the nullifier tree. - * @param blockNumber - The block number at which to get the index. - * @param index - The index of the leaf to get. - */ - getNullifierLeafData(blockNumber: number, index: bigint): Promise; + ): Promise; /** * Get the a given block. From 2329113d967f9a5cd2e105ca86b71b2a950650ea Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 11:50:29 +0000 Subject: [PATCH 41/69] formatting --- yarn-project/types/src/interfaces/index.ts | 2 +- yarn-project/types/src/interfaces/leaf_data.ts | 2 +- yarn-project/types/src/interfaces/state_provider.ts | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index ae00b65d4b6..5e2aed99fcf 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -7,4 +7,4 @@ export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; export * from './leaf_data.js'; -export * from './low_nullifier_witness.js'; \ No newline at end of file +export * from './low_nullifier_witness.js'; diff --git a/yarn-project/types/src/interfaces/leaf_data.ts b/yarn-project/types/src/interfaces/leaf_data.ts index 185102e7435..2edc8e09818 100644 --- a/yarn-project/types/src/interfaces/leaf_data.ts +++ b/yarn-project/types/src/interfaces/leaf_data.ts @@ -14,4 +14,4 @@ export interface LeafData { * A value of the next leaf. */ nextValue: bigint; -} \ No newline at end of file +} diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 5e86d7578c5..966c1060876 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -10,7 +10,6 @@ import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; -import { LeafData } from './leaf_data.js'; import { LowNullifierWitness } from './low_nullifier_witness.js'; /** @@ -74,10 +73,7 @@ export interface StateInfoProvider { * 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. */ - getLowNullifierWitness( - blockNumber: number, - nullifier: Fr, - ): Promise; + getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise; /** * Get the a given block. From b298fc8b1a4d6b86f8a2d6b39a059f355d7da934 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 12:29:51 +0000 Subject: [PATCH 42/69] nullifier non-inclusion finished --- .../src/e2e_inclusion_proofs_contract.test.ts | 7 +++ .../inclusion_proofs_contract/src/main.nr | 49 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) 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 e4d3f3e0e7e..3bb473ed24a 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 @@ -49,5 +49,12 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); await contract.methods.proveNoteInclusion(accounts[0].address, blockNumber).send().wait(); } + + { + // Prove that the note has not been nullified + // We prove the note existence at current block number because we don't currently have historical data + const blockNumber = await pxe.getBlockNumber(); + await contract.methods.proveNullifierNonInclusion(accounts[0].address, blockNumber).send().wait(); + } }); }); 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 05dc040ac0b..4f46249d5ca 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 @@ -139,15 +139,62 @@ contract InclusionProofs { // TODO: assert that block number is less than the block number of context.block_data // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage // or modifying the private context so that we somehow expose it. + + // 1) Get blocks tree root from context + let blocks_tree_root = context.block_data.blocks_tree_root; + + // 2) Get historic block data from oracle + let block_data = get_block_data(block_number); + + // 3) Compute block hash from the historic block data obtained in 1) + let block_hash = block_data.block_hash(); + + // 4) Get the membership witness of a block in the blocks tree + let blocks_tree_id = 5; + let witness: MembershipWitness = + get_membership_witness(block_number, blocks_tree_id, block_hash); + + // 5) Prove that the block hash is in the blocks tree + // In our test case this should be tree root at the latest block and should correspond to the membership + // witness we obtain from oracle + assert( + blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), + "Proving membership of a block in blocks tree failed" + ); + // 6) Get the note/preimage from PXE let balances = storage.balances.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); + // 7) Compute the nullifier from the note let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); - let low_nullifier_membership_witness = get_low_nullifier_witness(block_number, nullifier); + // 8) Get the membership witness of a low nullifier of the nullifier + let witness = get_low_nullifier_witness(block_number, nullifier); + + // 9) Prove that the nullifier is not included in the nullifier tree + + // 9.a) Compute the low nullifier leaf and prove that it is in the nullifier tree + let low_nullifier_leaf = witness.leaf_data.hash(); + assert( + block_data.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), + "Proving membership of a nullifier in nullifier tree failed" + ); + + // 9.b) Prove that the low nullifier is smaller than the nullifier + assert( + witness.leaf_data.value as u120 < nullifier as u120, + "Proving that the nullifier is not included in the nullifier tree failed" + ); + + // 9.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not + // included in the nullifier tree + assert( + witness.leaf_data.next_value as u120 > nullifier as u120, + "Proving that the nullifier is not included in the nullifier tree failed" + ); } // Computes note hash and nullifier. From 4d4ecf142cfc36209e8eaaa55674c7ca69ea4ec6 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 12:38:20 +0000 Subject: [PATCH 43/69] updated comments --- .../src/contracts/inclusion_proofs_contract/src/main.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 4f46249d5ca..b0821759756 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 @@ -180,20 +180,20 @@ contract InclusionProofs { let low_nullifier_leaf = witness.leaf_data.hash(); assert( block_data.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), - "Proving membership of a nullifier in nullifier tree failed" + "Proving membership of a low nullifier in the nullifier tree failed" ); // 9.b) Prove that the low nullifier is smaller than the nullifier assert( witness.leaf_data.value as u120 < nullifier as u120, - "Proving that the nullifier is not included in the nullifier tree failed" + "Proving that the low nullifier is smaller than the nullifier failed" ); // 9.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree assert( witness.leaf_data.next_value as u120 > nullifier as u120, - "Proving that the nullifier is not included in the nullifier tree failed" + "Proving that low_nullifier.next_value > nullifier failed" ); } From 783b5cacb3edbad1e465a787c8f612b65d6791d3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 13:18:15 +0000 Subject: [PATCH 44/69] updated comments --- .../src/contracts/inclusion_proofs_contract/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b0821759756..66c4e836875 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 @@ -186,7 +186,7 @@ contract InclusionProofs { // 9.b) Prove that the low nullifier is smaller than the nullifier assert( witness.leaf_data.value as u120 < nullifier as u120, - "Proving that the low nullifier is smaller than the nullifier failed" + "Proving that low_nullifier.value < nullifier failed" ); // 9.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not From 5a1cbab952e5e3365d860ed984cf7e4ea5b0009a Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 13:33:58 +0000 Subject: [PATCH 45/69] more consistent naming --- .../acir-simulator/src/acvm/oracle/oracle.ts | 4 ++-- .../src/acvm/oracle/typed_oracle.ts | 14 ++++++++++++-- .../acir-simulator/src/client/db_oracle.ts | 13 ++++++++----- .../acir-simulator/src/client/view_data_oracle.ts | 15 +++++++++------ yarn-project/aztec-node/src/aztec-node/server.ts | 15 +++++++++------ yarn-project/aztec-nr/aztec/src/oracle.nr | 2 +- ...nr => get_low_nullifier_membership_witness.nr} | 9 +++++---- .../inclusion_proofs_contract/src/main.nr | 6 +++--- yarn-project/pxe/src/simulator_oracle/index.ts | 9 ++++++--- .../types/src/interfaces/low_nullifier_witness.ts | 10 +++++----- .../types/src/interfaces/state_provider.ts | 13 ++++++++----- 11 files changed, 68 insertions(+), 42 deletions(-) rename yarn-project/aztec-nr/aztec/src/oracle/{get_low_nullifier_witness.nr => get_low_nullifier_membership_witness.nr} (71%) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 741bb15849f..1fa04c61036 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -64,14 +64,14 @@ export class Oracle { return witness.map(toACVMField); } - async getLowNullifierWitness( + async getLowNullifierMembershipWitness( [blockNumber]: ACVMField[], [nullifier]: ACVMField[], // nullifier, we try to find the low nullifier witness for (to prove non-inclusion) ): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); const parsedNullifier = fromACVMField(nullifier); - const witness = await this.typedOracle.getLowNullifierWitness(parsedBlockNumber, parsedNullifier); + const witness = await this.typedOracle.getLowNullifierMembershipWitness(parsedBlockNumber, parsedNullifier); if (!witness) { throw new Error( `Low nullifier witness not found for nullifier ${parsedNullifier} at block ${parsedBlockNumber}.`, 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 301583c6d0b..ea3bb5651a9 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -3,7 +3,14 @@ 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, LowNullifierWitness, MerkleTreeId, Note, PublicKey, UnencryptedL2Log } from '@aztec/types'; +import { + CompleteAddress, + LowNullifierMembershipWitness, + MerkleTreeId, + Note, + PublicKey, + UnencryptedL2Log, +} from '@aztec/types'; /** * Information about a note needed during execution. @@ -80,7 +87,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getLowNullifierWitness(_blockNumber: number, _nullifier: Fr): Promise { + getLowNullifierMembershipWitness( + _blockNumber: number, + _nullifier: Fr, + ): 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 c5dff614f6d..827e7a255c3 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,7 +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, LowNullifierWitness, MerkleTreeId } from '@aztec/types'; +import { L2Block, LowNullifierMembershipWitness, MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -135,15 +135,18 @@ export interface DBOracle extends CommitmentsDB { getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise; /** - * Returns a low nullifier witness for a given nullifier at a given block. + * 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 index for. - * @returns The low nullifier witness. + * @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. */ - getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise; + getLowNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise; /** * Fetch a block corresponding to the given block number. 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 1ecb30f1d3e..07afc58ea88 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,7 @@ 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, LowNullifierWitness, MerkleTreeId } from '@aztec/types'; +import { AuthWitness, AztecNode, CompleteAddress, LowNullifierMembershipWitness, MerkleTreeId } from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -52,16 +52,19 @@ export class ViewDataOracle extends TypedOracle { } /** - * Returns a low nullifier witness for a given nullifier at a given block. + * 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 index for. - * @returns The low nullifier witness. + * @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 getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { - return await this.db.getLowNullifierWitness(blockNumber, nullifier); + public async getLowNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise { + return await this.db.getLowNullifierMembershipWitness(blockNumber, nullifier); } /** diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 97dc4380dbe..adf07a88e49 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -35,7 +35,7 @@ import { L2Tx, LogFilter, LogType, - LowNullifierWitness, + LowNullifierMembershipWitness, MerkleTreeId, SequencerConfig, SiblingPath, @@ -355,15 +355,18 @@ export class AztecNodeService implements AztecNode { } /** - * Returns a low nullifier witness for a given nullifier at a given block. + * 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 index for. - * @returns The low nullifier witness. + * @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 getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { + public async getLowNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise { const committedDb = await this.#getWorldState(); const { index } = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, index); @@ -374,7 +377,7 @@ export class AztecNodeService implements AztecNode { MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); - return new LowNullifierWitness(BigInt(index), leafData, siblingPath); + return new LowNullifierMembershipWitness(BigInt(index), leafData, siblingPath); } /** diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index 56617d8ddd2..f16cc41ccab 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -7,7 +7,7 @@ mod call_private_function; mod context; mod debug_log; mod get_l1_to_l2_message; -mod get_low_nullifier_witness; +mod get_low_nullifier_membership_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr similarity index 71% rename from yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr rename to yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr index 2994eb4eef3..c6f5345f1ce 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -7,6 +7,7 @@ global LEAF_DATA_LENGTH: Field = 3; global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; // Noir version of LeafData interface from indexed merkle tree. +// replace with /mnt/user-data/jan/aztec-packages/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/nullifier_leaf_preimage.nr struct LeafData { value: Field, next_index: Field, @@ -30,13 +31,13 @@ struct LowNullifierMembershipWitness { path: [Field; NULLIFIER_TREE_HEIGHT], } -#[oracle(getLowNullifierWitness)] -fn get_low_nullifier_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_NULLIFIER_MEMBERSHIP_WITNESS] {} +#[oracle(getLowNullifierMembershipWitness)] +fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_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_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { - let fields = get_low_nullifier_witness_oracle(block_number, nullifier); +unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> LowNullifierMembershipWitness { + let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); LowNullifierMembershipWitness { index: fields[0], leaf_data: LeafData { 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 66c4e836875..3cd763e6d0c 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 @@ -23,8 +23,8 @@ contract InclusionProofs { get_membership_witness, MembershipWitness, }, - get_low_nullifier_witness::{ - get_low_nullifier_witness, + get_low_nullifier_membership_witness::{ + get_low_nullifier_membership_witness, LowNullifierMembershipWitness, }, }, @@ -172,7 +172,7 @@ contract InclusionProofs { let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); // 8) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_witness(block_number, nullifier); + let witness = get_low_nullifier_membership_witness(block_number, nullifier); // 9) Prove that the nullifier is not included in the nullifier tree diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 0161f1ad5c6..27c415b2706 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,7 +10,7 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, L2Block, LowNullifierWitness, MerkleTreeId, StateInfoProvider } from '@aztec/types'; +import { KeyStore, L2Block, LowNullifierMembershipWitness, MerkleTreeId, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -156,8 +156,11 @@ export class SimulatorOracle implements DBOracle { } } - public getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise { - return this.stateInfoProvider.getLowNullifierWitness(blockNumber, nullifier); + public getLowNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise { + return this.stateInfoProvider.getLowNullifierMembershipWitness(blockNumber, nullifier); } public async getBlock(blockNumber: number): Promise { diff --git a/yarn-project/types/src/interfaces/low_nullifier_witness.ts b/yarn-project/types/src/interfaces/low_nullifier_witness.ts index c62928973eb..16e40aac732 100644 --- a/yarn-project/types/src/interfaces/low_nullifier_witness.ts +++ b/yarn-project/types/src/interfaces/low_nullifier_witness.ts @@ -4,12 +4,12 @@ import { SiblingPath } from '../sibling_path.js'; import { LeafData } from './leaf_data.js'; /** - * Low nullifier witness. - * @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. + * Low nullifier membership witness. + * @remarks Low nullifier membership 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. */ -export class LowNullifierWitness { +export class LowNullifierMembershipWitness { constructor( /** * The index of low nullifier. diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 966c1060876..958750eb691 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -10,7 +10,7 @@ import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; -import { LowNullifierWitness } from './low_nullifier_witness.js'; +import { LowNullifierMembershipWitness } from './low_nullifier_witness.js'; /** * Interface providing methods for retrieving information about content of the state trees. @@ -65,15 +65,18 @@ export interface StateInfoProvider { getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; /** - * Returns a low nullifier witness for a given nullifier at a given block. + * 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 index for. - * @returns The low nullifier witness. + * @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. */ - getLowNullifierWitness(blockNumber: number, nullifier: Fr): Promise; + getLowNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise; /** * Get the a given block. From 931222387e9211a75dd2b6f9f8f9495f03a0a344 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 13:50:21 +0000 Subject: [PATCH 46/69] overflow fix --- .../contracts/inclusion_proofs_contract/Nargo.toml | 1 - .../contracts/inclusion_proofs_contract/src/main.nr | 11 +++++++++-- .../contracts/inclusion_proofs_contract/src/utils.nr | 11 +++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml index 633b6030352..b11305196cd 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/Nargo.toml @@ -6,5 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../../aztec-nr/aztec" } -safe_math = { path = "../../../../aztec-nr/safe-math" } value_note = { path = "../../../../aztec-nr/value-note" } \ No newline at end of file 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 3cd763e6d0c..3f7f358d99b 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 @@ -1,3 +1,5 @@ +mod utils; + // A demonstration of inclusion proofs. contract InclusionProofs { use dep::std::merkle::compute_merkle_root; @@ -32,6 +34,11 @@ contract InclusionProofs { }; use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; + use crate::utils::{ + full_field_less_than, + full_field_greater_than, + }; + struct Storage { balances: Map>, } @@ -185,14 +192,14 @@ contract InclusionProofs { // 9.b) Prove that the low nullifier is smaller than the nullifier assert( - witness.leaf_data.value as u120 < nullifier as u120, + full_field_less_than(witness.leaf_data.value, nullifier), "Proving that low_nullifier.value < nullifier failed" ); // 9.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree assert( - witness.leaf_data.next_value as u120 > nullifier as u120, + full_field_greater_than(witness.leaf_data.next_value, nullifier), "Proving that low_nullifier.next_value > nullifier failed" ); } diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr new file mode 100644 index 00000000000..85c7b144fe6 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr @@ -0,0 +1,11 @@ +// Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599 +// TODO: move to a shared place? + +// TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports +pub fn full_field_less_than(lhs: Field, rhs: Field) -> bool { + dep::std::eddsa::lt_bytes32(lhs, rhs) +} + +pub fn full_field_greater_than(lhs: Field, rhs: Field) -> bool { + dep::std::eddsa::lt_bytes32(rhs, lhs) +} \ No newline at end of file From c6881b001a73e0db60dbe5b069871230b5d24512 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 16:33:12 +0000 Subject: [PATCH 47/69] WIP --- .../acir-simulator/src/acvm/oracle/oracle.ts | 9 +++ .../src/acvm/oracle/typed_oracle.ts | 4 + .../src/client/view_data_oracle.ts | 11 +++ yarn-project/aztec-nr/aztec/src/oracle.nr | 1 + .../aztec/src/oracle/get_sibling_path.nr | 10 +++ .../src/e2e_inclusion_proofs_contract.test.ts | 3 +- .../inclusion_proofs_contract/src/main.nr | 79 ++++++++++++++++++- .../token_bridge_contract/src/main.nr | 4 +- 8 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 1fa04c61036..6ca1382dfb4 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -64,6 +64,15 @@ export class Oracle { return witness.map(toACVMField); } + async getSiblingPath([blockNumber]: ACVMField[], [treeId]: ACVMField[], [index]: ACVMField[]): Promise { + const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); + const parsedTreeId = frToNumber(fromACVMField(treeId)); + const parsedIndex = fromACVMField(index); + + const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedIndex); + return path.map(toACVMField); + } + async getLowNullifierMembershipWitness( [blockNumber]: ACVMField[], [nullifier]: ACVMField[], // nullifier, we try to find the low nullifier witness for (to prove non-inclusion) 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 ea3bb5651a9..d2231e65b2f 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -87,6 +87,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _index: Fr): Promise { + throw new Error('Not available.'); + } + getLowNullifierMembershipWitness( _blockNumber: number, _nullifier: Fr, 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 07afc58ea88..64bccf9cb15 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -51,6 +51,17 @@ export class ViewDataOracle extends TypedOracle { return [new Fr(index), ...siblingPath]; } + /** + * Fetches the sibling path for at a given index, block from and tree. + * @param blockNumber - The block number at which to get the membership witness. + * @param treeId - The tree id + * @param index - Index of the leaf to get sibling path for + * @returns The index and sibling path concatenated [index, sibling_path] + */ + public getSiblingPath(blockNumber: number, treeId: MerkleTreeId, index: Fr): Promise { + return this.db.getSiblingPath(blockNumber, treeId, index.toBigInt()); + } + /** * 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. diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index f16cc41ccab..7b8bed1c31d 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -11,6 +11,7 @@ mod get_low_nullifier_membership_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; +mod get_sibling_path; mod rand; mod enqueue_public_function_call; mod get_block_data; 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 new file mode 100644 index 00000000000..3233b23ef09 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr @@ -0,0 +1,10 @@ +use crate::constants_gen::NOTE_HASH_TREE_HEIGHT; +use crate::utils::arr_copy_slice; + +#[oracle(geSiblingPath)] +fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _index: Field) -> [Field; N] {} + +unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, index: Field) -> [Field; N] { + let value: [Field; N] = get_sibling_path_oracle(block_number, tree_id, index); + value +} \ No newline at end of file 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 3bb473ed24a..fc79ff6ab65 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,11 +19,12 @@ describe('e2e_inclusion_proofs_contract', () => { let accounts: CompleteAddress[]; let contract: InclusionProofsContract; + const publicValue = 236n; beforeAll(async () => { ({ pxe, teardown, wallets, accounts } = await setup(1)); - contract = await InclusionProofsContract.deploy(wallets[0]).send().deployed(); + contract = await InclusionProofsContract.deploy(wallets[0], publicValue).send().deployed(); }, 100_000); afterAll(() => teardown()); 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 3f7f358d99b..fe824c4a3c4 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 @@ -7,8 +7,13 @@ contract InclusionProofs { state_vars::{ map::Map, set::Set, + public_state::PublicState, + }, + selector::compute_selector, + types::{ + address::AztecAddress, + type_serialization::field_serialization::FieldSerializationMethods, }, - types::address::AztecAddress, context::Context, note::{ note_getter_options::NoteGetterOptions, @@ -18,6 +23,8 @@ contract InclusionProofs { constants_gen::{ NOTE_HASH_TREE_HEIGHT, HISTORIC_BLOCKS_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, + GENERATOR_INDEX__PUBLIC_LEAF_INDEX, }, oracle::{ get_block_data::get_block_data, @@ -25,11 +32,13 @@ contract InclusionProofs { get_membership_witness, MembershipWitness, }, + get_sibling_path::get_sibling_path, get_low_nullifier_membership_witness::{ get_low_nullifier_membership_witness, LowNullifierMembershipWitness, }, }, + hash::pedersen_hash, // oracle::debug_log::debug_log_format, }; use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; @@ -41,6 +50,7 @@ contract InclusionProofs { struct Storage { balances: Map>, + public_value: PublicState, } impl Storage { @@ -53,12 +63,25 @@ contract InclusionProofs { Set::new(context, slot, ValueNoteMethods) }, ), + public_value: PublicState::new( + context, + 2, // Storage slot + FieldSerializationMethods, + ), } } } #[aztec(private)] - fn constructor() {} + fn constructor(public_value: Field) { + let selector = compute_selector("_initialize((Field))"); + context.call_public_function(context.this_address(), selector, [public_value]); + } + + #[aztec(public)] + internal fn _initialize(value: Field) { + storage.public_value.write(value); + } // Mints `amount` of LP tokens to `owner`. #[aztec(private)] @@ -204,6 +227,58 @@ contract InclusionProofs { ); } + #[aztec(private)] + fn provePublicValueInclusion( + public_value: Field, + block_number: Field, // 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_data + // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage + // or modifying the private context so that we somehow expose it. + + // 1) + let blocks_tree_root = context.block_data.blocks_tree_root; + + // 2) + let block_data = get_block_data(block_number); + + // 3) + let block_hash = block_data.block_hash(); + + // 4) + let blocks_tree_id = 5; + let witness: MembershipWitness = + get_membership_witness(block_number, blocks_tree_id, block_hash); + + // 5) + // In our test case this should be tree root at the latest block and should correspond to the membership + // witness we obtain from oracle + assert( + blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), + "Proving membership of a block in blocks tree failed" + ); + + // 6) + // We have to compute the leaf index here because unlike in the case of note commitments, public values are + // siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary + // value in the public data tree. + let public_value_leaf_index = pedersen_hash( + [context.this_address(), storage.public_value.storage_slot], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX + ); + + // 7) + let public_data_tree_id = 3; + let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = + get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); + + // 8) + assert( + block_data.note_hash_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), + "Proving membership of a value in public data tree failed" + ); + } + // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. diff --git a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr index fe2e708ff78..77eb9149bb0 100644 --- a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr @@ -11,9 +11,7 @@ contract TokenBridge { context::{Context}, hash::{compute_secret_hash}, state_vars::{public_state::PublicState}, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, + types::type_serialization::field_serialization::FieldSerializationMethods, types::address::{AztecAddress, EthereumAddress}, selector::compute_selector, }; From 976278c4494bc17b9a5396b6ea101f3fbf7ebfad Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 27 Nov 2023 16:56:51 +0000 Subject: [PATCH 48/69] fixes --- yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr | 2 +- .../end-to-end/src/e2e_inclusion_proofs_contract.test.ts | 5 +++++ .../src/contracts/inclusion_proofs_contract/src/main.nr | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) 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 3233b23ef09..4eb79a5442f 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 @@ -1,7 +1,7 @@ use crate::constants_gen::NOTE_HASH_TREE_HEIGHT; use crate::utils::arr_copy_slice; -#[oracle(geSiblingPath)] +#[oracle(getSiblingPath)] fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _index: Field) -> [Field; N] {} unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, index: Field) -> [Field; N] { 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 fc79ff6ab65..95e88e9f914 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 @@ -58,4 +58,9 @@ describe('e2e_inclusion_proofs_contract', () => { await contract.methods.proveNullifierNonInclusion(accounts[0].address, blockNumber).send().wait(); } }); + + it('proves an existence of a public value in private context', async () => { + const blockNumber = await pxe.getBlockNumber(); + await contract.methods.provePublicValueInclusion(accounts[0].address, blockNumber).send().wait(); + }); }); 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 fe824c4a3c4..a41aeebba0e 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 @@ -74,7 +74,7 @@ contract InclusionProofs { #[aztec(private)] fn constructor(public_value: Field) { - let selector = compute_selector("_initialize((Field))"); + let selector = compute_selector("_initialize(Field)"); context.call_public_function(context.this_address(), selector, [public_value]); } From 4e29bb52d2468375038f556480c6d525e5db399c Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 08:25:28 +0000 Subject: [PATCH 49/69] getPublicDataTreeSiblingPath --- .../aztec-node/src/aztec-node/server.ts | 19 +++++++++++++++---- .../inclusion_proofs_contract/src/main.nr | 2 +- .../pxe/src/simulator_oracle/index.ts | 2 ++ .../types/src/interfaces/state_provider.ts | 17 +++++++++++++---- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index adf07a88e49..99d46bcb797 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -8,6 +8,7 @@ import { L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computePublicDataTreeIndex } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; @@ -300,7 +301,7 @@ export class AztecNodeService implements AztecNode { } /** - * Returns the sibling path for the given index in the contract tree. + * Returns a sibling path for the given index in the contract tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. */ @@ -310,7 +311,7 @@ export class AztecNodeService implements AztecNode { } /** - * Returns the sibling path for the given index in the data tree. + * Returns a sibling path for the given index in the data tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. */ @@ -333,7 +334,7 @@ export class AztecNodeService implements AztecNode { } /** - * Returns the sibling path for a leaf in the committed l1 to l2 data tree. + * Returns a sibling path for a leaf in the committed l1 to l2 data tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. */ @@ -343,7 +344,7 @@ export class AztecNodeService implements AztecNode { } /** - * Returns the sibling path for a leaf in the committed historic blocks tree. + * Returns a sibling path for a leaf in the committed historic blocks tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. */ @@ -354,6 +355,16 @@ export class AztecNodeService implements AztecNode { return committedDb.getSiblingPath(MerkleTreeId.BLOCKS_TREE, leafIndex); } + /** + * Returns a sibling path for a leaf in the committed public data tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + */ + public async getPublicDataTreeSiblingPath(leafIndex: bigint): Promise> { + const committedDb = await this.#getWorldState(); + return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex); + } + /** * 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. 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 a41aeebba0e..ae5419ff862 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 @@ -274,7 +274,7 @@ contract InclusionProofs { // 8) assert( - block_data.note_hash_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), + block_data.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), "Proving membership of a value in public data tree failed" ); } diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 27c415b2706..475be717d07 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -151,6 +151,8 @@ export class SimulatorOracle implements DBOracle { return (await this.stateInfoProvider.getNoteHashSiblingPath(leafIndex)).toFieldArray(); case MerkleTreeId.BLOCKS_TREE: return (await this.stateInfoProvider.getHistoricBlocksTreeSiblingPath(leafIndex)).toFieldArray(); + case MerkleTreeId.PUBLIC_DATA_TREE: + return (await this.stateInfoProvider.getPublicDataTreeSiblingPath(leafIndex)).toFieldArray(); default: throw new Error('Not implemented'); } diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 958750eb691..f26c3d56b25 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -4,6 +4,7 @@ import { HISTORIC_BLOCKS_TREE_HEIGHT, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; @@ -25,7 +26,7 @@ export interface StateInfoProvider { findLeafIndex(treeId: MerkleTreeId, leafValue: Fr): Promise; /** - * Returns the sibling path for the given index in the contract tree. + * Returns a sibling path for the given index in the contract tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 @@ -33,7 +34,7 @@ export interface StateInfoProvider { getContractSiblingPath(leafIndex: bigint): Promise>; /** - * Returns the sibling path for the given index in the note hash tree. + * Returns a sibling path for the given index in the note hash tree. * @param leafIndex - The index of the leaf for which the sibling path is required. * @returns The sibling path for the leaf index. * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 @@ -49,7 +50,7 @@ export interface StateInfoProvider { getL1ToL2MessageAndIndex(messageKey: Fr): Promise; /** - * Returns the sibling path for a leaf in the committed l1 to l2 data tree. + * Returns a sibling path for a leaf in the committed l1 to l2 data tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 @@ -57,13 +58,21 @@ export interface StateInfoProvider { getL1ToL2MessageSiblingPath(leafIndex: bigint): Promise>; /** - * Returns the sibling path for a leaf in the committed historic blocks tree. + * Returns a sibling path for a leaf in the committed historic blocks tree. * @param leafIndex - Index of the leaf in the tree. * @returns The sibling path. * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 */ getHistoricBlocksTreeSiblingPath(leafIndex: bigint): Promise>; + /** + * Returns a sibling path for a leaf in the committed public data tree. + * @param leafIndex - Index of the leaf in the tree. + * @returns The sibling path. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 + */ + getPublicDataTreeSiblingPath(leafIndex: bigint): Promise>; + /** * 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. From 8dca2be96f6c48002d3045cb5cbaa59aa7345d5f Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 08:32:06 +0000 Subject: [PATCH 50/69] test fix --- .../end-to-end/src/e2e_inclusion_proofs_contract.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 95e88e9f914..107666f2393 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 @@ -61,6 +61,6 @@ describe('e2e_inclusion_proofs_contract', () => { it('proves an existence of a public value in private context', async () => { const blockNumber = await pxe.getBlockNumber(); - await contract.methods.provePublicValueInclusion(accounts[0].address, blockNumber).send().wait(); + await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); }); }); From 11bbc4d5f0481a74412ebf6bb2d6ad0ab8d318d4 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 09:19:20 +0000 Subject: [PATCH 51/69] refactor: constraining block data in a separate function --- .../aztec/src/oracle/get_block_data.nr | 32 ++++- .../get_low_nullifier_membership_witness.nr | 10 +- .../src/oracle/get_membership_witness.nr | 7 +- .../aztec/src/oracle/get_sibling_path.nr | 2 +- .../inclusion_proofs_contract/src/main.nr | 125 +++++------------- 5 files changed, 68 insertions(+), 108 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr index 2a36a6076e8..fc4a90837b7 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr @@ -1,10 +1,36 @@ -use crate::constants_gen::HISTORIC_BLOCK_DATA_LENGTH; +use dep::std::merkle::compute_merkle_root; +use crate::constants_gen::{ + HISTORIC_BLOCK_DATA_LENGTH, + HISTORIC_BLOCKS_TREE_HEIGHT, +}; use crate::abi::HistoricBlockData; +use crate::oracle::get_membership_witness::{ + get_membership_witness, + MembershipWitness, +}; #[oracle(getBlockData)] fn get_block_data_oracle(_block_number: Field) -> [Field; HISTORIC_BLOCK_DATA_LENGTH] {} -unconstrained pub fn get_block_data(block_number: Field) -> HistoricBlockData { +unconstrained pub fn get_block_data_internal(block_number: Field) -> HistoricBlockData { let block_data = get_block_data_oracle(block_number); HistoricBlockData::deserialize(block_data) -} \ No newline at end of file +} + +pub fn get_block_data(block_number: Field, current_blocks_tree_root: Field) -> HistoricBlockData { + // 1) Get historic block data from oracle at the given block + let block_data = get_block_data_internal(block_number); + + // 2) Compute the block hash from the block data + let block_hash = block_data.block_hash(); + + // 3) Get the membership wintess of the block in the blocks tree + let blocks_tree_id = 5; + let witness: MembershipWitness = get_membership_witness(block_number, blocks_tree_id, block_hash); + + // 4) Check that the block is in the blocks tree (i.e. the witness is valid) + assert(current_blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in blocks tree failed"); + + // 5) Return the block data + block_data +} diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr index c6f5345f1ce..22c5da8a643 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr @@ -40,11 +40,7 @@ unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, n let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); LowNullifierMembershipWitness { index: fields[0], - leaf_data: LeafData { - value: fields[1], - next_index: fields[2], - next_value: fields[3], - }, - path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_DATA_LENGTH), + leaf_data: LeafData { value: fields[1], next_index: fields[2], next_value: fields[3] }, + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_DATA_LENGTH) } -} \ No newline at end of file +} 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 3fba3807380..41a929a5132 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 @@ -18,8 +18,5 @@ fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf: Field) -> MembershipWitness { let fields: [Field; M] = get_membership_witness_oracle(block_number, tree_id, leaf); - MembershipWitness { - index: fields[0], - path: arr_copy_slice(fields, [0; N], 1), - } -} \ No newline at end of file + MembershipWitness { index: fields[0], path: arr_copy_slice(fields, [0; N], 1) } +} 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 4eb79a5442f..30ed1276419 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 @@ -7,4 +7,4 @@ fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _index: Fie unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, index: Field) -> [Field; N] { let value: [Field; N] = get_sibling_path_oracle(block_number, tree_id, index); value -} \ No newline at end of file +} 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 ae5419ff862..a72f4af05ef 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 @@ -39,7 +39,6 @@ contract InclusionProofs { }, }, hash::pedersen_hash, - // oracle::debug_log::debug_log_format, }; use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; @@ -96,19 +95,6 @@ contract InclusionProofs { // Proves that the owner owned a ValueNote at block `block_number`. - // - // The scheme works as follows: - // 1) Get the blocks tree root from context. - // 2) Get historic block data from oracle. - // 3) Compute block hash from the historic block data obtained in 1). - // 4) Get the membership witness of a block in the blocks tree. - // 5) Prove that the block hash is in the blocks tree. - // 6) Get the note/preimage from PXE. - // 7) Compute the commitment from the note. - // 8) Get the membership witness of the note in the note hash tree. - // 9) Verify that the commitment is in the note hash tree. - // --> Now we have traversed the trees all the way up to blocks tree root. Last thing remaining is to verify that - // the blocks tree root is in the grandfather tree. That will be done in the root rollup circuit. #[aztec(private)] fn proveNoteInclusion( owner: AztecAddress, @@ -118,49 +104,34 @@ contract InclusionProofs { // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage // or modifying the private context so that we somehow expose it. - // 1) - let blocks_tree_root = context.block_data.blocks_tree_root; + // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree + // root. + let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); - // 2) - let block_data = get_block_data(block_number); - - // 3) - let block_hash = block_data.block_hash(); - - // 4) - let blocks_tree_id = 5; - let witness: MembershipWitness = - get_membership_witness(block_number, blocks_tree_id, block_hash); - - // 5) - // In our test case this should be tree root at the latest block and should correspond to the membership - // witness we obtain from oracle - assert( - blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), - "Proving membership of a block in blocks tree failed" - ); - - // 6) + // 2) Get the note from PXE. let balances = storage.balances.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); - // 7) + // 3) Compute the commitment from the note let note_commitment = note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, note); - // 8) + // 4) Get the membership witness of the note in the note hash tree let note_hash_tree_id = 2; let witness: MembershipWitness = get_membership_witness(block_number, note_hash_tree_id, note_commitment); - // 9) + // 5) Verify that the commitment is in the note hash tree assert( block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving membership of a note in note hash tree failed" ); + + // --> Now we have traversed the trees all the way up to blocks tree root. } + // Proves that the note was not yet nullified at block `block_number`. #[aztec(private)] fn proveNullifierNonInclusion( owner: AztecAddress, @@ -170,61 +141,46 @@ contract InclusionProofs { // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage // or modifying the private context so that we somehow expose it. - // 1) Get blocks tree root from context - let blocks_tree_root = context.block_data.blocks_tree_root; - - // 2) Get historic block data from oracle - let block_data = get_block_data(block_number); - - // 3) Compute block hash from the historic block data obtained in 1) - let block_hash = block_data.block_hash(); - - // 4) Get the membership witness of a block in the blocks tree - let blocks_tree_id = 5; - let witness: MembershipWitness = - get_membership_witness(block_number, blocks_tree_id, block_hash); + // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree + // root. + let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); - // 5) Prove that the block hash is in the blocks tree - // In our test case this should be tree root at the latest block and should correspond to the membership - // witness we obtain from oracle - assert( - blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), - "Proving membership of a block in blocks tree failed" - ); - - // 6) Get the note/preimage from PXE + // 2) Get the note from PXE let balances = storage.balances.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = balances.get_notes(options); let note = notes[0].unwrap(); - // 7) Compute the nullifier from the note + // 3) Compute the nullifier from the note let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); - // 8) Get the membership witness of a low nullifier of the nullifier + // 4) Get the membership witness of a low nullifier of the nullifier let witness = get_low_nullifier_membership_witness(block_number, nullifier); - // 9) Prove that the nullifier is not included in the nullifier tree + // 5) Prove that the nullifier is not included in the nullifier tree - // 9.a) Compute the low nullifier leaf and prove that it is in the nullifier tree + // 5.a) Compute the low nullifier leaf and prove that it is in the nullifier tree let low_nullifier_leaf = witness.leaf_data.hash(); assert( block_data.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving membership of a low nullifier in the nullifier tree failed" ); - // 9.b) Prove that the low nullifier is smaller than the nullifier + // 5.b) Prove that the low nullifier is smaller than the nullifier assert( full_field_less_than(witness.leaf_data.value, nullifier), "Proving that low_nullifier.value < nullifier failed" ); - // 9.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not + // 5.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree assert( full_field_greater_than(witness.leaf_data.next_value, nullifier), "Proving that low_nullifier.next_value > nullifier failed" ); + + // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier + // was not yet included in the nullifier tree. } #[aztec(private)] @@ -236,47 +192,32 @@ contract InclusionProofs { // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage // or modifying the private context so that we somehow expose it. - // 1) - let blocks_tree_root = context.block_data.blocks_tree_root; - - // 2) - let block_data = get_block_data(block_number); - - // 3) - let block_hash = block_data.block_hash(); + // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree + // root. + let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); - // 4) - let blocks_tree_id = 5; - let witness: MembershipWitness = - get_membership_witness(block_number, blocks_tree_id, block_hash); - - // 5) - // In our test case this should be tree root at the latest block and should correspond to the membership - // witness we obtain from oracle - assert( - blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), - "Proving membership of a block in blocks tree failed" - ); - - // 6) + // 2) Compute the public value leaf index. // We have to compute the leaf index here because unlike in the case of note commitments, public values are - // siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary + // not siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary // value in the public data tree. let public_value_leaf_index = pedersen_hash( [context.this_address(), storage.public_value.storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); - // 7) + // 3) Get the sibling path of the public value leaf index in the public data tree at block `block_number`. let public_data_tree_id = 3; let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); - // 8) + // 4) Verify that the public value provided on input is in the public data tree assert( block_data.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), "Proving membership of a value in public data tree failed" ); + + // --> Now we have traversed the trees all the way up to blocks tree root and that way verified that + // a specific `public_value` was really set in a given contract storage slot at block `block_number`. } // Computes note hash and nullifier. From 4b2f7930e586035b820a7097d1544859596f98d7 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 09:25:28 +0000 Subject: [PATCH 52/69] cleanup --- .../inclusion_proofs_contract/src/main.nr | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 a72f4af05ef..683e57ce526 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 @@ -1,6 +1,6 @@ mod utils; -// A demonstration of inclusion proofs. +// A demonstration of inclusion and non-inclusion proofs. contract InclusionProofs { use dep::std::merkle::compute_merkle_root; use dep::aztec::{ @@ -48,14 +48,14 @@ contract InclusionProofs { }; struct Storage { - balances: Map>, + private_values: Map>, public_value: PublicState, } impl Storage { fn init(context: Context) -> Self { Storage { - balances: Map::new( + private_values: Map::new( context, 1, // Storage slot |context, slot| { @@ -82,15 +82,15 @@ contract InclusionProofs { storage.public_value.write(value); } - // Mints `amount` of LP tokens to `owner`. + // Creates a value note owned by `owner`. #[aztec(private)] fn create_note( owner: AztecAddress, - amount: Field, + value: Field, ) { - let owner_balance = storage.balances.at(owner.address); - let mut note = ValueNote::new(amount, owner.address); - owner_balance.insert(&mut note, true); + let owner_private_values = storage.private_values.at(owner.address); + let mut note = ValueNote::new(value, owner.address); + owner_private_values.insert(&mut note, true); } @@ -109,9 +109,9 @@ contract InclusionProofs { let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); // 2) Get the note from PXE. - let balances = storage.balances.at(owner.address); + let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); - let notes = balances.get_notes(options); + let notes = private_values.get_notes(options); let note = notes[0].unwrap(); // 3) Compute the commitment from the note @@ -146,9 +146,9 @@ contract InclusionProofs { let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); // 2) Get the note from PXE - let balances = storage.balances.at(owner.address); + let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); - let notes = balances.get_notes(options); + let notes = private_values.get_notes(options); let note = notes[0].unwrap(); // 3) Compute the nullifier from the note From 17eb82d68528119273ef18598c8971cceb92b95f Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 09:40:47 +0000 Subject: [PATCH 53/69] docs fixes --- .../acir-simulator/src/client/view_data_oracle.ts | 8 ++++---- yarn-project/aztec-node/src/aztec-node/server.ts | 4 ++-- .../src/e2e_inclusion_proofs_contract.test.ts | 10 +++++----- .../types/src/interfaces/low_nullifier_witness.ts | 4 ++-- yarn-project/types/src/interfaces/state_provider.ts | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) 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 64bccf9cb15..c4acd0ba312 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -36,9 +36,9 @@ export class ViewDataOracle extends TypedOracle { } /** - * Fetches the index and sibling path for a leaf value + * 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 - The tree id + * @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] */ @@ -52,9 +52,9 @@ export class ViewDataOracle extends TypedOracle { } /** - * Fetches the sibling path for at a given index, block from and tree. + * 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 - The tree id + * @param treeId - Id of the tree to get the sibling path from. * @param index - Index of the leaf to get sibling path for * @returns The index and sibling path concatenated [index, sibling_path] */ diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 99d46bcb797..dee243ca384 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -165,9 +165,9 @@ export class AztecNodeService implements AztecNode { } /** - * Get the a given block. + * Get a block specified by its number. * @param number - The block number being requested. - * @returns The blocks requested. + * @returns The requested block. */ public async getBlock(number: number): Promise { return await this.blockSource.getBlock(number); 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 107666f2393..9133328e7a1 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 @@ -33,14 +33,14 @@ describe('e2e_inclusion_proofs_contract', () => { // Owner of a note const owner = accounts[0].address; { - // Create note - const amount = 100n; - const receipt = await contract.methods.create_note(owner, amount).send().wait({ debug: true }); + // Create a note + const value = 100n; + const receipt = await contract.methods.create_note(owner, value).send().wait({ debug: true }); const { newCommitments, visibleNotes } = receipt.debugInfo!; expect(newCommitments.length).toBe(1); expect(visibleNotes.length).toBe(1); - const [receivedAmount, receivedOwner, _randomness] = visibleNotes[0].note.items; - expect(receivedAmount.toBigInt()).toBe(amount); + const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; + expect(receivedValue.toBigInt()).toBe(value); expect(receivedOwner).toEqual(owner.toField()); } diff --git a/yarn-project/types/src/interfaces/low_nullifier_witness.ts b/yarn-project/types/src/interfaces/low_nullifier_witness.ts index 16e40aac732..3b7fbcab2c4 100644 --- a/yarn-project/types/src/interfaces/low_nullifier_witness.ts +++ b/yarn-project/types/src/interfaces/low_nullifier_witness.ts @@ -26,8 +26,8 @@ export class LowNullifierMembershipWitness { ) {} /** - * Returns a field array representation of the low nullifier witness. - * @returns A field array representation of the low nullifier witness. + * Returns a field array representation of a low nullifier witness. + * @returns A field array representation of a low nullifier witness. */ public toFieldArray(): Fr[] { return [ diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index f26c3d56b25..47a1e3d0a96 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -88,9 +88,9 @@ export interface StateInfoProvider { ): Promise; /** - * Get the a given block. + * Get a block specified by its number. * @param number - The block number being requested. - * @returns The blocks requested. + * @returns The requested block. */ getBlock(number: number): Promise; } From 5dd0a8bd5868cb0ba0e94dcccfc9ce91969d9bec Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 11:57:59 +0000 Subject: [PATCH 54/69] Adding TODO --- circuits/cpp/src/aztec3/circuits/abis/historic_block_data.hpp | 2 +- yarn-project/acir-simulator/src/client/view_data_oracle.ts | 2 +- .../circuits.js/src/structs/kernel/historic_block_data.ts | 2 +- .../src/crates/types/src/abis/historical_block_data.nr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/circuits/cpp/src/aztec3/circuits/abis/historic_block_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/historic_block_data.hpp index d66ddf1a21a..45161128c06 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/historic_block_data.hpp +++ b/circuits/cpp/src/aztec3/circuits/abis/historic_block_data.hpp @@ -118,7 +118,7 @@ template 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 }; 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 c4acd0ba312..f7641619852 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -94,7 +94,7 @@ export class ViewDataOracle extends TypedOracle { block.endContractTreeSnapshot.root, block.endL1ToL2MessagesTreeSnapshot.root, block.endHistoricBlocksTreeSnapshot.root, - new Fr(0), // privateKernelVkTreeRoot is not present in L2Block and it's not yet populated in noir + new Fr(0), // TODO(#3441) privateKernelVkTreeRoot is not present in L2Block and it's not yet populated in noir block.endPublicDataTreeRoot, computeGlobalsHash(block.globalVariables), ); diff --git a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts index f3d09abc864..dc5d90c39a7 100644 --- a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts @@ -97,7 +97,7 @@ export class HistoricBlockData { this.nullifierTreeRoot, this.contractTreeRoot, this.l1ToL2MessagesTreeRoot, - this.blocksTreeRoot, // Note private_kernel_vk_tree_root, is not included yet as + this.blocksTreeRoot, // TODO(#3441) Note private_kernel_vk_tree_root, is not included yet as // it is not present in noir, this.publicDataTreeRoot, this.globalVariablesHash, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/historical_block_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/historical_block_data.nr index d9a8d180ade..488d4cf6ba0 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/historical_block_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/historical_block_data.nr @@ -17,7 +17,7 @@ impl HistoricalBlockData { fn to_array(self) -> [Field;7] { // This comment was copied from the cpp codebase. // - // TODO: Note private_kernel_vk_tree_root, is not included yet as + // TODO(#3441): Note private_kernel_vk_tree_root, is not included yet as // it is not present in noir, [ self.block.note_hash_tree_root, From e10c3cb2188da664de6e93e86099387a9190f682 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:07:26 +0000 Subject: [PATCH 55/69] clarified naming --- .../acir-simulator/src/acvm/oracle/oracle.ts | 18 +++++++++++------- .../src/acvm/oracle/typed_oracle.ts | 4 ++-- .../src/client/view_data_oracle.ts | 6 +++--- .../aztec/src/oracle/get_membership_witness.nr | 6 +++--- .../aztec/src/oracle/get_sibling_path.nr | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 6ca1382dfb4..437beb0b868 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -49,27 +49,31 @@ export class Oracle { async getMembershipWitness( [blockNumber]: ACVMField[], [treeId]: ACVMField[], - [leaf]: ACVMField[], + [leafValue]: ACVMField[], ): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); const parsedTreeId = frToNumber(fromACVMField(treeId)); - const parsedLeaf = fromACVMField(leaf); + const parsedLeafValue = fromACVMField(leafValue); - const witness = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeaf); + const witness = await this.typedOracle.getMembershipWitness(parsedBlockNumber, parsedTreeId, parsedLeafValue); if (!witness) { throw new Error( - `Leaf ${leaf} not found in the tree ${MerkleTreeId[parsedTreeId]} at block ${parsedBlockNumber}.`, + `Leaf ${leafValue} not found in the tree ${MerkleTreeId[parsedTreeId]} at block ${parsedBlockNumber}.`, ); } return witness.map(toACVMField); } - async getSiblingPath([blockNumber]: ACVMField[], [treeId]: ACVMField[], [index]: ACVMField[]): Promise { + async getSiblingPath( + [blockNumber]: ACVMField[], + [treeId]: ACVMField[], + [leafIndex]: ACVMField[], + ): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); const parsedTreeId = frToNumber(fromACVMField(treeId)); - const parsedIndex = fromACVMField(index); + const parsedLeafIndex = fromACVMField(leafIndex); - const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedIndex); + const path = await this.typedOracle.getSiblingPath(parsedBlockNumber, parsedTreeId, parsedLeafIndex); return path.map(toACVMField); } 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 d2231e65b2f..d6ed7e34651 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -83,11 +83,11 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leaf: Fr): Promise { + getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { throw new Error('Not available.'); } - getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _index: Fr): Promise { + getSiblingPath(_blockNumber: number, _treeId: MerkleTreeId, _leafIndex: Fr): Promise { throw new Error('Not available.'); } 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 f7641619852..99327de45b0 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -55,11 +55,11 @@ export class ViewDataOracle extends TypedOracle { * 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 index - Index of the leaf to get sibling path for + * @param leafIndex - Index of the leaf to get sibling path for * @returns The index and sibling path concatenated [index, sibling_path] */ - public getSiblingPath(blockNumber: number, treeId: MerkleTreeId, index: Fr): Promise { - return this.db.getSiblingPath(blockNumber, treeId, index.toBigInt()); + public getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr): Promise { + return this.db.getSiblingPath(blockNumber, treeId, leafIndex.toBigInt()); } /** 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 41a929a5132..5122e685d3e 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: Field) -> [Field; M] {} +fn get_membership_witness_oracle(_block_number: Field, _tree_id: Field, _leaf_value: Field) -> [Field; M] {} -unconstrained pub fn get_membership_witness(block_number: Field, tree_id: Field, leaf: Field) -> MembershipWitness { - let fields: [Field; M] = get_membership_witness_oracle(block_number, tree_id, leaf); +unconstrained pub fn get_membership_witness(block_number: Field, 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_sibling_path.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_sibling_path.nr index 30ed1276419..076a4748d69 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 crate::constants_gen::NOTE_HASH_TREE_HEIGHT; use crate::utils::arr_copy_slice; #[oracle(getSiblingPath)] -fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _index: Field) -> [Field; N] {} +fn get_sibling_path_oracle(_block_number: Field, _tree_id: Field, _leaf_index: Field) -> [Field; N] {} -unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, index: Field) -> [Field; N] { - let value: [Field; N] = get_sibling_path_oracle(block_number, tree_id, index); +unconstrained pub fn get_sibling_path(block_number: Field, tree_id: Field, leaf_index: Field) -> [Field; N] { + let value: [Field; N] = get_sibling_path_oracle(block_number, tree_id, leaf_index); value } From fdca497c9edafa6eb3d5f55a22573a4d67945c37 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:11:51 +0000 Subject: [PATCH 56/69] todo --- yarn-project/aztec-nr/aztec/src/abi.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/aztec-nr/aztec/src/abi.nr b/yarn-project/aztec-nr/aztec/src/abi.nr index bf5019d9f04..c84ad8b6d8b 100644 --- a/yarn-project/aztec-nr/aztec/src/abi.nr +++ b/yarn-project/aztec-nr/aztec/src/abi.nr @@ -184,7 +184,7 @@ impl HistoricBlockData { } pub fn block_hash(self) -> Field { - // TODO: Unify the ordering in `HistoricBlockData::serialize` function and the ordering + // TODO(#3442): Unify the ordering in `HistoricBlockData::serialize` function and the ordering // in the block hash preimage --> This requires changes in the circuits. let inputs = [ self.global_variables_hash, From acc1cb1730b8421c3f3f7c417d9b20727102d265 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:23:56 +0000 Subject: [PATCH 57/69] passing context instead of blocks tree root --- .../aztec/src/oracle/get_block_data.nr | 23 +++++++++++-------- .../inclusion_proofs_contract/src/main.nr | 6 ++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr index fc4a90837b7..6a0b6bac16c 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr @@ -1,12 +1,15 @@ use dep::std::merkle::compute_merkle_root; -use crate::constants_gen::{ - HISTORIC_BLOCK_DATA_LENGTH, - HISTORIC_BLOCKS_TREE_HEIGHT, -}; -use crate::abi::HistoricBlockData; -use crate::oracle::get_membership_witness::{ - get_membership_witness, - MembershipWitness, +use crate::{ + abi::HistoricBlockData, + constants_gen::{ + HISTORIC_BLOCK_DATA_LENGTH, + HISTORIC_BLOCKS_TREE_HEIGHT, + }, + context::PrivateContext, + oracle::get_membership_witness::{ + get_membership_witness, + MembershipWitness, + }, }; #[oracle(getBlockData)] @@ -17,7 +20,7 @@ unconstrained pub fn get_block_data_internal(block_number: Field) -> HistoricBlo HistoricBlockData::deserialize(block_data) } -pub fn get_block_data(block_number: Field, current_blocks_tree_root: Field) -> HistoricBlockData { +pub fn get_block_data(block_number: Field, context: PrivateContext) -> HistoricBlockData { // 1) Get historic block data from oracle at the given block let block_data = get_block_data_internal(block_number); @@ -29,7 +32,7 @@ pub fn get_block_data(block_number: Field, current_blocks_tree_root: Field) -> H let witness: MembershipWitness = get_membership_witness(block_number, blocks_tree_id, block_hash); // 4) Check that the block is in the blocks tree (i.e. the witness is valid) - assert(current_blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in blocks tree failed"); + assert(context.block_data.blocks_tree_root == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in blocks tree failed"); // 5) Return the block data block_data 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 683e57ce526..d8e355ce684 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 @@ -106,7 +106,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); + let block_data = get_block_data(block_number, context); // 2) Get the note from PXE. let private_values = storage.private_values.at(owner.address); @@ -143,7 +143,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); + let block_data = get_block_data(block_number, context); // 2) Get the note from PXE let private_values = storage.private_values.at(owner.address); @@ -194,7 +194,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context.block_data.blocks_tree_root); + let block_data = get_block_data(block_number, context); // 2) Compute the public value leaf index. // We have to compute the leaf index here because unlike in the case of note commitments, public values are From e9769bbee522dc551d97c513772eafa692d57f18 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:33:26 +0000 Subject: [PATCH 58/69] forgotten TODO --- .../circuits.js/src/structs/kernel/historic_block_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts index dc5d90c39a7..eda21d334f1 100644 --- a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts @@ -37,7 +37,7 @@ export class HistoricBlockData { /** * Root of the private kernel vk tree at the time of when this information was assembled. */ - public privateKernelVkTreeRoot: Fr, // future enhancement + public privateKernelVkTreeRoot: Fr, // TODO(#3441) future enhancement /** * Current public state tree hash. */ From a52985b6af7d573171663ead244d1718c49a8bb7 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:40:36 +0000 Subject: [PATCH 59/69] merkle tree id issue --- yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr | 2 +- .../src/contracts/inclusion_proofs_contract/src/main.nr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr index 6a0b6bac16c..2b645a62dd9 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_block_data.nr @@ -28,7 +28,7 @@ pub fn get_block_data(block_number: Field, context: PrivateContext) -> HistoricB let block_hash = block_data.block_hash(); // 3) Get the membership wintess of the block in the blocks tree - let blocks_tree_id = 5; + let blocks_tree_id = 5; // TODO(#3443) let witness: MembershipWitness = get_membership_witness(block_number, blocks_tree_id, block_hash); // 4) Check that the block is in the blocks tree (i.e. the witness is valid) 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 d8e355ce684..e30fe341a06 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 @@ -118,7 +118,7 @@ contract InclusionProofs { let note_commitment = note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, note); // 4) Get the membership witness of the note in the note hash tree - let note_hash_tree_id = 2; + let note_hash_tree_id = 2; // TODO(#3443) let witness: MembershipWitness = get_membership_witness(block_number, note_hash_tree_id, note_commitment); @@ -206,7 +206,7 @@ contract InclusionProofs { ); // 3) Get the sibling path of the public value leaf index in the public data tree at block `block_number`. - let public_data_tree_id = 3; + let public_data_tree_id = 3; // TODO(#3443) let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); From cea9ae82b6e5f6702f48b15116c1a38810e121f2 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 12:55:25 +0000 Subject: [PATCH 60/69] fixed TSDoc --- yarn-project/acir-simulator/src/client/db_oracle.ts | 2 +- yarn-project/acir-simulator/src/client/view_data_oracle.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 827e7a255c3..92796cd1e94 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -130,7 +130,7 @@ export interface DBOracle extends CommitmentsDB { * @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. Undefined if it does not exist in the tree. + * @returns - The sibling path of the leaf. */ getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): 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 99327de45b0..31b975060af 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -56,7 +56,7 @@ export class ViewDataOracle extends TypedOracle { * @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 index and sibling path concatenated [index, sibling_path] + * @returns The sibling path. */ public getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr): Promise { return this.db.getSiblingPath(blockNumber, treeId, leafIndex.toBigInt()); From c5a10e47fbe034ff7743dcfbd9b31498c229d722 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 14:26:52 +0000 Subject: [PATCH 61/69] WIP on nullifier inclusion proof (currently broken) --- .../aztec-node/src/aztec-node/server.ts | 10 ++++++ .../src/e2e_inclusion_proofs_contract.test.ts | 8 +++++ .../inclusion_proofs_contract/src/main.nr | 32 +++++++++++++++++++ .../pxe/src/simulator_oracle/index.ts | 2 ++ .../types/src/interfaces/state_provider.ts | 9 ++++++ 5 files changed, 61 insertions(+) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index dee243ca384..511254fb84f 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -310,6 +310,16 @@ export class AztecNodeService implements AztecNode { return committedDb.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex); } + /** + * Returns a sibling path for the given index in the nullifier tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + */ + public async getNullifierTreeSiblingPath(leafIndex: bigint): Promise> { + const committedDb = await this.#getWorldState(); + return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex); + } + /** * Returns a sibling path for the given index in the data tree. * @param leafIndex - The index of the leaf for which the sibling path is required. 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 9133328e7a1..aa8d644b276 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 @@ -63,4 +63,12 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); }); + + it('proves existence of a nullifier in private context', async () => { + const blockNumber = await pxe.getBlockNumber(); + const block = await pxe.getBlock(blockNumber); + const nullifier = block?.newNullifiers[0]; + + await contract.methods.proveNullifierInclusion(nullifier!, blockNumber).send().wait(); + }); }); 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 e30fe341a06..b03c28e26d9 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 @@ -22,6 +22,7 @@ contract InclusionProofs { }, constants_gen::{ NOTE_HASH_TREE_HEIGHT, + NULLIFIER_TREE_HEIGHT, HISTORIC_BLOCKS_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_LEAF_INDEX, @@ -183,6 +184,37 @@ contract InclusionProofs { // was not yet included in the nullifier tree. } + // Proves nullifier existed at block `block_number`. + // Note: I am not getting a nullifier of the note that was created in this contract in this function because it is + // currently not possible to obtain a nullified note from PXE. + #[aztec(private)] + fn proveNullifierInclusion( + nullifier: Field, + block_number: Field, // 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_data + // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage + // or modifying the private context so that we somehow expose it. + + // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree + // root. + let block_data = get_block_data(block_number, context); + + // 2) Get the membership witness of the nullifier + let nullifier_tree_id = 1; // TODO(#3443) + let witness: MembershipWitness = + get_membership_witness(block_number, nullifier_tree_id, nullifier); + + // 3) Verify that the nullifier is in the nullifier tree + assert( + block_data.nullifier_tree_root == compute_merkle_root(nullifier, witness.index, witness.path), + "Proving membership of a nullifier in nullifier tree failed" + ); + + // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier + // was not yet included in the nullifier tree. + } + #[aztec(private)] fn provePublicValueInclusion( public_value: Field, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 475be717d07..b6a23e61e81 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -147,6 +147,8 @@ export class SimulatorOracle implements DBOracle { ); // @todo Doing a nasty workaround here because of https://github.com/AztecProtocol/aztec-packages/issues/3414 switch (treeId) { + case MerkleTreeId.NULLIFIER_TREE: + return (await this.stateInfoProvider.getNullifierTreeSiblingPath(leafIndex)).toFieldArray(); case MerkleTreeId.NOTE_HASH_TREE: return (await this.stateInfoProvider.getNoteHashSiblingPath(leafIndex)).toFieldArray(); case MerkleTreeId.BLOCKS_TREE: diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 47a1e3d0a96..efcc5a7fca4 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -4,6 +4,7 @@ import { HISTORIC_BLOCKS_TREE_HEIGHT, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, + NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/circuits.js'; @@ -33,6 +34,14 @@ export interface StateInfoProvider { */ getContractSiblingPath(leafIndex: bigint): Promise>; + /** + * Returns a sibling path for the given index in the nullifier tree. + * @param leafIndex - The index of the leaf for which the sibling path is required. + * @returns The sibling path for the leaf index. + * TODO: https://github.com/AztecProtocol/aztec-packages/issues/3414 + */ + getNullifierTreeSiblingPath(leafIndex: bigint): Promise>; + /** * Returns a sibling path for the given index in the note hash tree. * @param leafIndex - The index of the leaf for which the sibling path is required. From 3c89221a8113524ecfeaff99c4b70e04afddbaaa Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 16:01:41 +0000 Subject: [PATCH 62/69] fix nullifier inclusion --- .../acir-simulator/src/acvm/oracle/oracle.ts | 16 ++++++++++++ .../src/acvm/oracle/typed_oracle.ts | 7 +++++ .../acir-simulator/src/client/db_oracle.ts | 8 ++++++ .../src/client/view_data_oracle.ts | 13 ++++++++++ .../aztec-node/src/aztec-node/server.ts | 26 +++++++++++++++++++ yarn-project/aztec-nr/aztec/src/oracle.nr | 2 +- ...nr => get_nullifier_membership_witness.nr} | 24 +++++++++++++---- .../inclusion_proofs_contract/src/main.nr | 23 +++++++++------- .../pxe/src/simulator_oracle/index.ts | 7 +++++ .../src/interfaces/low_nullifier_witness.ts | 1 + .../types/src/interfaces/state_provider.ts | 8 ++++++ 11 files changed, 120 insertions(+), 15 deletions(-) rename yarn-project/aztec-nr/aztec/src/oracle/{get_low_nullifier_membership_witness.nr => get_nullifier_membership_witness.nr} (60%) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 437beb0b868..ebc8ca180f1 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -77,6 +77,22 @@ export class Oracle { return path.map(toACVMField); } + async getNullifierMembershipWitness( + [blockNumber]: ACVMField[], + [nullifier]: ACVMField[], // nullifier, we try to find the witness for (to prove inclusion) + ): Promise { + 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) 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 d6ed7e34651..591e4da022a 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -91,6 +91,13 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getNullifierMembershipWitness( + _blockNumber: number, + _nullifier: Fr, + ): Promise { + throw new Error('Not available.'); + } + getLowNullifierMembershipWitness( _blockNumber: number, _nullifier: Fr, diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 92796cd1e94..55327701a72 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -134,6 +134,14 @@ export interface DBOracle extends CommitmentsDB { */ getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: bigint): Promise; + /** + * 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; + /** * 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. 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 31b975060af..81ce1366d04 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -62,6 +62,19 @@ export class ViewDataOracle extends TypedOracle { 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 { + 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. diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 511254fb84f..3e49b1c715b 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -375,6 +375,32 @@ export class AztecNodeService implements AztecNode { return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex); } + /** + * 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 { + const committedDb = await this.#getWorldState(); + const index = await committedDb.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + if (!index) { + return undefined; + } + const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, Number(index)); + if (!leafData) { + return undefined; + } + const siblingPath = await committedDb.getSiblingPath( + MerkleTreeId.NULLIFIER_TREE, + BigInt(index), + ); + return new LowNullifierMembershipWitness(BigInt(index), leafData, siblingPath); + } + /** * 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. diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index 7b8bed1c31d..4bed8383bfa 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -7,7 +7,7 @@ mod call_private_function; mod context; mod debug_log; mod get_l1_to_l2_message; -mod get_low_nullifier_membership_witness; +mod get_nullifier_membership_witness; mod get_membership_witness; mod get_public_key; mod get_secret_key; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr similarity index 60% rename from yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr rename to yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr index 22c5da8a643..a18f49c57ee 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_low_nullifier_membership_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr @@ -4,7 +4,7 @@ use crate::hash::pedersen_hash; global LEAF_DATA_LENGTH: Field = 3; // TODO: move this to constants.hpp so that it gets computed as INDEX_LENGTH + LEAF_DATA_LENGTH + NULLIFIER_TREE_HEIGHT -global LOW_NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; +global NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; // Noir version of LeafData interface from indexed merkle tree. // replace with /mnt/user-data/jan/aztec-packages/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/nullifier_leaf_preimage.nr @@ -25,22 +25,36 @@ impl LeafData { } } -struct LowNullifierMembershipWitness { +struct NullifierMembershipWitness { index: Field, leaf_data: LeafData, path: [Field; NULLIFIER_TREE_HEIGHT], } #[oracle(getLowNullifierMembershipWitness)] -fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _nullifier: Field) -> [Field; LOW_NULLIFIER_MEMBERSHIP_WITNESS] {} +fn get_low_nullifier_membership_witness_oracle(_block_number: Field, _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) -> LowNullifierMembershipWitness { +unconstrained pub fn get_low_nullifier_membership_witness(block_number: Field, nullifier: Field) -> NullifierMembershipWitness { let fields = get_low_nullifier_membership_witness_oracle(block_number, nullifier); - LowNullifierMembershipWitness { + NullifierMembershipWitness { index: fields[0], leaf_data: LeafData { value: fields[1], next_index: fields[2], next_value: fields[3] }, path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_DATA_LENGTH) } } + +#[oracle(getNullifierMembershipWitness)] +fn get_nullifier_membership_witness_oracle(_block_number: Field, _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 { + let fields = get_nullifier_membership_witness_oracle(block_number, nullifier); + NullifierMembershipWitness { + index: fields[0], + leaf_data: LeafData { value: fields[1], next_index: fields[2], next_value: fields[3] }, + path: arr_copy_slice(fields, [0; NULLIFIER_TREE_HEIGHT], 1 + LEAF_DATA_LENGTH) + } +} \ No newline at end of file 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 b03c28e26d9..9fc487a0353 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 @@ -34,9 +34,10 @@ contract InclusionProofs { MembershipWitness, }, get_sibling_path::get_sibling_path, - get_low_nullifier_membership_witness::{ + get_nullifier_membership_witness::{ get_low_nullifier_membership_witness, - LowNullifierMembershipWitness, + get_nullifier_membership_witness, + NullifierMembershipWitness, }, }, hash::pedersen_hash, @@ -123,7 +124,7 @@ contract InclusionProofs { let witness: MembershipWitness = get_membership_witness(block_number, note_hash_tree_id, note_commitment); - // 5) Verify that the commitment is in the note hash tree + // 5) Prove that the commitment is in the note hash tree assert( block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving membership of a note in note hash tree failed" @@ -201,13 +202,17 @@ contract InclusionProofs { let block_data = get_block_data(block_number, context); // 2) Get the membership witness of the nullifier - let nullifier_tree_id = 1; // TODO(#3443) - let witness: MembershipWitness = - get_membership_witness(block_number, nullifier_tree_id, nullifier); + let witness = get_nullifier_membership_witness(block_number, nullifier); - // 3) Verify that the nullifier is in the nullifier tree + // 3) Check that the witness we obtained matches the nullifier + assert(witness.leaf_data.value == nullifier, "Nullifier does not match value in witness"); + + // 4) Compute the nullifier tree leaf + let nullifier_leaf = witness.leaf_data.hash(); + + // 5) Prove that the nullifier is in the nullifier tree assert( - block_data.nullifier_tree_root == compute_merkle_root(nullifier, witness.index, witness.path), + block_data.nullifier_tree_root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving membership of a nullifier in nullifier tree failed" ); @@ -242,7 +247,7 @@ contract InclusionProofs { let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); - // 4) Verify that the public value provided on input is in the public data tree + // 4) Prove that the public value provided on input is in the public data tree assert( block_data.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), "Proving membership of a value in public data tree failed" diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index b6a23e61e81..41e2ed21042 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -160,6 +160,13 @@ export class SimulatorOracle implements DBOracle { } } + public getNullifierMembershipWitness( + blockNumber: number, + nullifier: Fr, + ): Promise { + return this.stateInfoProvider.getNullifierMembershipWitness(blockNumber, nullifier); + } + public getLowNullifierMembershipWitness( blockNumber: number, nullifier: Fr, diff --git a/yarn-project/types/src/interfaces/low_nullifier_witness.ts b/yarn-project/types/src/interfaces/low_nullifier_witness.ts index 3b7fbcab2c4..2630b4c2b7b 100644 --- a/yarn-project/types/src/interfaces/low_nullifier_witness.ts +++ b/yarn-project/types/src/interfaces/low_nullifier_witness.ts @@ -9,6 +9,7 @@ import { LeafData } from './leaf_data.js'; * 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. */ +// TODO: rename to NullifierNonMembershipWitness export class LowNullifierMembershipWitness { constructor( /** diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index efcc5a7fca4..55a8980c000 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -82,6 +82,14 @@ export interface StateInfoProvider { */ getPublicDataTreeSiblingPath(leafIndex: bigint): Promise>; + /** + * 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; + /** * 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. From df26f17fcf78e104a8e59c8282caffbef555ec19 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 28 Nov 2023 16:09:53 +0000 Subject: [PATCH 63/69] LowNullifierMembershipWitness > NullifierMembershipWitness --- .../src/acvm/oracle/typed_oracle.ts | 9 ++-- .../acir-simulator/src/client/db_oracle.ts | 9 ++-- .../src/client/view_data_oracle.ts | 6 +-- .../aztec-node/src/aztec-node/server.ts | 10 ++--- .../pxe/src/simulator_oracle/index.ts | 6 +-- yarn-project/types/src/interfaces/index.ts | 2 +- .../src/interfaces/low_nullifier_witness.ts | 42 ------------------- .../types/src/interfaces/nullifier_witness.ts | 41 ++++++++++++++++++ .../types/src/interfaces/state_provider.ts | 9 ++-- 9 files changed, 62 insertions(+), 72 deletions(-) delete mode 100644 yarn-project/types/src/interfaces/low_nullifier_witness.ts create mode 100644 yarn-project/types/src/interfaces/nullifier_witness.ts 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 591e4da022a..9138009309c 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -5,9 +5,9 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { CompleteAddress, - LowNullifierMembershipWitness, MerkleTreeId, Note, + NullifierMembershipWitness, PublicKey, UnencryptedL2Log, } from '@aztec/types'; @@ -91,17 +91,14 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getNullifierMembershipWitness( - _blockNumber: number, - _nullifier: Fr, - ): Promise { + getNullifierMembershipWitness(_blockNumber: number, _nullifier: Fr): Promise { throw new Error('Not available.'); } getLowNullifierMembershipWitness( _blockNumber: number, _nullifier: Fr, - ): Promise { + ): 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 55327701a72..ad2cb1a19c4 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,7 +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, LowNullifierMembershipWitness, MerkleTreeId } from '@aztec/types'; +import { L2Block, MerkleTreeId, NullifierMembershipWitness } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -140,7 +140,7 @@ export interface DBOracle extends CommitmentsDB { * @param nullifier - Nullifier we try to find witness for. * @returns The nullifier membership witness (if found). */ - getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; + getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; /** * Returns a low nullifier membership witness for a given nullifier at a given block. @@ -151,10 +151,7 @@ export interface DBOracle extends CommitmentsDB { * 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; + getLowNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; /** * Fetch a block corresponding to the given block number. 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 81ce1366d04..60c927545ed 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,7 @@ 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, LowNullifierMembershipWitness, MerkleTreeId } 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'; @@ -71,7 +71,7 @@ export class ViewDataOracle extends TypedOracle { public async getNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { return await this.db.getNullifierMembershipWitness(blockNumber, nullifier); } @@ -87,7 +87,7 @@ export class ViewDataOracle extends TypedOracle { public async getLowNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { return await this.db.getLowNullifierMembershipWitness(blockNumber, nullifier); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 3e49b1c715b..f97eb7ce5ac 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -36,8 +36,8 @@ import { L2Tx, LogFilter, LogType, - LowNullifierMembershipWitness, MerkleTreeId, + NullifierMembershipWitness, SequencerConfig, SiblingPath, Tx, @@ -384,7 +384,7 @@ export class AztecNodeService implements AztecNode { public async getNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { const committedDb = await this.#getWorldState(); const index = await committedDb.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); if (!index) { @@ -398,7 +398,7 @@ export class AztecNodeService implements AztecNode { MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); - return new LowNullifierMembershipWitness(BigInt(index), leafData, siblingPath); + return new NullifierMembershipWitness(BigInt(index), leafData, siblingPath); } /** @@ -413,7 +413,7 @@ export class AztecNodeService implements AztecNode { public async getLowNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { const committedDb = await this.#getWorldState(); const { index } = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, index); @@ -424,7 +424,7 @@ export class AztecNodeService implements AztecNode { MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); - return new LowNullifierMembershipWitness(BigInt(index), leafData, siblingPath); + return new NullifierMembershipWitness(BigInt(index), leafData, siblingPath); } /** diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 41e2ed21042..42c3edd74c4 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -10,7 +10,7 @@ import { PublicKey, } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; -import { KeyStore, L2Block, LowNullifierMembershipWitness, MerkleTreeId, StateInfoProvider } from '@aztec/types'; +import { KeyStore, L2Block, MerkleTreeId, NullifierMembershipWitness, StateInfoProvider } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -163,14 +163,14 @@ export class SimulatorOracle implements DBOracle { public getNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { return this.stateInfoProvider.getNullifierMembershipWitness(blockNumber, nullifier); } public getLowNullifierMembershipWitness( blockNumber: number, nullifier: Fr, - ): Promise { + ): Promise { return this.stateInfoProvider.getLowNullifierMembershipWitness(blockNumber, nullifier); } diff --git a/yarn-project/types/src/interfaces/index.ts b/yarn-project/types/src/interfaces/index.ts index 5e2aed99fcf..f351287cab2 100644 --- a/yarn-project/types/src/interfaces/index.ts +++ b/yarn-project/types/src/interfaces/index.ts @@ -7,4 +7,4 @@ export * from './node-info.js'; export * from './sync-status.js'; export * from './configs.js'; export * from './leaf_data.js'; -export * from './low_nullifier_witness.js'; +export * from './nullifier_witness.js'; diff --git a/yarn-project/types/src/interfaces/low_nullifier_witness.ts b/yarn-project/types/src/interfaces/low_nullifier_witness.ts deleted file mode 100644 index 2630b4c2b7b..00000000000 --- a/yarn-project/types/src/interfaces/low_nullifier_witness.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; - -import { SiblingPath } from '../sibling_path.js'; -import { LeafData } from './leaf_data.js'; - -/** - * Low nullifier membership witness. - * @remarks Low nullifier membership 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. - */ -// TODO: rename to NullifierNonMembershipWitness -export class LowNullifierMembershipWitness { - constructor( - /** - * The index of low nullifier. - */ - public readonly index: bigint, - /** - * Preimage of the low nullifier that proves non membership. - */ - public readonly leafData: LeafData, - /** - * Sibling path to prove membership of low nullifier. - */ - public readonly siblingPath: SiblingPath, - ) {} - - /** - * Returns a field array representation of a low nullifier witness. - * @returns A field array representation of a low nullifier witness. - */ - public toFieldArray(): Fr[] { - return [ - new Fr(this.index), - new Fr(this.leafData.value), - new Fr(this.leafData.nextIndex), - new Fr(this.leafData.nextValue), - ...this.siblingPath.toFieldArray(), - ]; - } -} diff --git a/yarn-project/types/src/interfaces/nullifier_witness.ts b/yarn-project/types/src/interfaces/nullifier_witness.ts new file mode 100644 index 00000000000..90dc6d9a1c7 --- /dev/null +++ b/yarn-project/types/src/interfaces/nullifier_witness.ts @@ -0,0 +1,41 @@ +import { Fr, NULLIFIER_TREE_HEIGHT } from '@aztec/circuits.js'; + +import { SiblingPath } from '../sibling_path.js'; +import { LeafData } from './leaf_data.js'; + +/** + * Nullifier membership witness. + * @remarks When this represents membership witness of a low nullifier it 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. + */ +export class NullifierMembershipWitness { + constructor( + /** + * The index of the nullifier in a tree. + */ + public readonly index: bigint, + /** + * Preimage of the nullifier. + */ + public readonly leafData: LeafData, + /** + * Sibling path to prove membership of the nullifier. + */ + public readonly siblingPath: SiblingPath, + ) {} + + /** + * Returns a field array representation of a nullifier witness. + * @returns A field array representation of a nullifier witness. + */ + public toFieldArray(): Fr[] { + return [ + new Fr(this.index), + new Fr(this.leafData.value), + new Fr(this.leafData.nextIndex), + new Fr(this.leafData.nextValue), + ...this.siblingPath.toFieldArray(), + ]; + } +} diff --git a/yarn-project/types/src/interfaces/state_provider.ts b/yarn-project/types/src/interfaces/state_provider.ts index 55a8980c000..68d01812a7e 100644 --- a/yarn-project/types/src/interfaces/state_provider.ts +++ b/yarn-project/types/src/interfaces/state_provider.ts @@ -12,7 +12,7 @@ import { L1ToL2MessageAndIndex } from '../l1_to_l2_message.js'; import { L2Block } from '../l2_block.js'; import { MerkleTreeId } from '../merkle_tree_id.js'; import { SiblingPath } from '../sibling_path.js'; -import { LowNullifierMembershipWitness } from './low_nullifier_witness.js'; +import { NullifierMembershipWitness } from './nullifier_witness.js'; /** * Interface providing methods for retrieving information about content of the state trees. @@ -88,7 +88,7 @@ export interface StateInfoProvider { * @param nullifier - Nullifier we try to find witness for. * @returns The nullifier membership witness (if found). */ - getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; + getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; /** * Returns a low nullifier membership witness for a given nullifier at a given block. @@ -99,10 +99,7 @@ export interface StateInfoProvider { * 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; + getLowNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; /** * Get a block specified by its number. From c90c3e225af12d440e6bce651eb81717ed1e267c Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 10:29:31 +0000 Subject: [PATCH 64/69] test: nullifier non-inclusion proof failure case --- .../aztec-node/src/aztec-node/server.ts | 13 +++++++++- .../src/e2e_inclusion_proofs_contract.test.ts | 20 ++++++++++++-- .../inclusion_proofs_contract/src/main.nr | 26 ++++++++++++++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index f97eb7ce5ac..664e4825e9f 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -409,13 +409,24 @@ export class AztecNodeService implements AztecNode { * @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( blockNumber: number, nullifier: Fr, ): Promise { const committedDb = await this.#getWorldState(); - const { index } = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + const { index, alreadyPresent } = await committedDb.getPreviousValueIndex( + MerkleTreeId.NULLIFIER_TREE, + nullifier.toBigInt(), + ); + if (alreadyPresent) { + this.log.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); + } const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, index); if (!leafData) { return undefined; 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 aa8d644b276..f87736eb4be 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 @@ -48,14 +48,30 @@ describe('e2e_inclusion_proofs_contract', () => { // Prove note inclusion in a given block. // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - await contract.methods.proveNoteInclusion(accounts[0].address, blockNumber).send().wait(); + await contract.methods.proveNoteInclusion(owner, blockNumber).send().wait(); } { // Prove that the note has not been nullified // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - await contract.methods.proveNullifierNonInclusion(accounts[0].address, blockNumber).send().wait(); + await contract.methods.proveNullifierNonInclusion(owner, blockNumber, 0).send().wait(); + } + + { + // We test the failure case now --> The proof should fail when the nullifier already exists + const receipt = await contract.methods.nullifyNote(owner).send().wait({ debug: true }); + const { newNullifiers } = receipt.debugInfo!; + expect(newNullifiers.length).toBe(2); + + const blockNumber = await pxe.getBlockNumber(); + const nullifier = newNullifiers[1]; + // Note: getLowNullifierMembershipWitness returns the membership witness of the nullifier itself and not + // the low nullifier when the nullifier already exists in the tree and for this reason the execution fails + // on low_nullifier.value < nullifier.value check. + await expect( + contract.methods.proveNullifierNonInclusion(owner, blockNumber, nullifier).send().wait(), + ).rejects.toThrowError(/Proving that low_nullifier.value < nullifier.value failed/); } }); 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 9fc487a0353..f7e1ee601a9 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 @@ -138,6 +138,7 @@ contract InclusionProofs { fn proveNullifierNonInclusion( owner: AztecAddress, block_number: Field, // 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_data // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage @@ -151,10 +152,15 @@ contract InclusionProofs { let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = private_values.get_notes(options); - let note = notes[0].unwrap(); + let maybe_note = notes[0]; // 3) Compute the nullifier from the note - let nullifier = note_utils::compute_siloed_nullifier(ValueNoteMethods, note); + let nullifier = if maybe_note.is_some() { + note_utils::compute_siloed_nullifier(ValueNoteMethods, maybe_note.unwrap_unchecked()) + } else { + // Note was not found so we will use the spare nullifier + spare_nullifier + }; // 4) Get the membership witness of a low nullifier of the nullifier let witness = get_low_nullifier_membership_witness(block_number, nullifier); @@ -171,20 +177,32 @@ contract InclusionProofs { // 5.b) Prove that the low nullifier is smaller than the nullifier assert( full_field_less_than(witness.leaf_data.value, nullifier), - "Proving that low_nullifier.value < nullifier failed" + "Proving that low_nullifier.value < nullifier.value failed" ); // 5.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree assert( full_field_greater_than(witness.leaf_data.next_value, nullifier), - "Proving that low_nullifier.next_value > nullifier failed" + "Proving that low_nullifier.next_value > nullifier.value failed" ); // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier // was not yet included in the nullifier tree. } + #[aztec(private)] + fn nullifyNote( + owner: AztecAddress, + ) { + let private_values = storage.private_values.at(owner.address); + let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); + let notes = private_values.get_notes(options); + let note = notes[0].unwrap(); + + private_values.remove(note); + } + // Proves nullifier existed at block `block_number`. // Note: I am not getting a nullifier of the note that was created in this contract in this function because it is // currently not possible to obtain a nullified note from PXE. From 1745153d940a4d2001bad6b6452df3a78ee8f872 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 12:49:10 +0000 Subject: [PATCH 65/69] feat: PrivateContext::get_block_data(...) --- yarn-project/aztec-nr/aztec/src/context.nr | 5 +++++ .../src/contracts/inclusion_proofs_contract/src/main.nr | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index a2f34867c6c..f66dc2cc34d 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -46,6 +46,7 @@ use crate::oracle::{ public_call::call_public_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, context::get_portal_address, + get_block_data::get_block_data, }; use dep::std::option::Option; @@ -128,6 +129,10 @@ impl PrivateContext { self.inputs.call_context.function_selector } + pub fn get_block_data(self, block_number: Field) -> HistoricBlockData { + get_block_data(block_number, self) + } + pub fn finish(self) -> abi::PrivateCircuitPublicInputs { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; 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 f7e1ee601a9..338b7bb934f 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 @@ -108,7 +108,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context); + let block_data = context.get_block_data(block_number); // 2) Get the note from PXE. let private_values = storage.private_values.at(owner.address); @@ -146,7 +146,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context); + let block_data = context.get_block_data(block_number); // 2) Get the note from PXE let private_values = storage.private_values.at(owner.address); @@ -217,7 +217,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context); + let block_data = context.get_block_data(block_number); // 2) Get the membership witness of the nullifier let witness = get_nullifier_membership_witness(block_number, nullifier); @@ -249,7 +249,7 @@ contract InclusionProofs { // 1) Get historic block data from oracle and ensure that the block hash is included in the current blocks tree // root. - let block_data = get_block_data(block_number, context); + let block_data = context.get_block_data(block_number); // 2) Compute the public value leaf index. // We have to compute the leaf index here because unlike in the case of note commitments, public values are From 138c7ef084da56e1d5d2e77eb68332959d020b15 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 13:31:30 +0000 Subject: [PATCH 66/69] test: more failure cases --- .../src/e2e_inclusion_proofs_contract.test.ts | 38 +++++++++++++++++-- .../inclusion_proofs_contract/src/main.nr | 10 ++++- 2 files changed, 42 insertions(+), 6 deletions(-) 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 f87736eb4be..e18da5fcd18 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 @@ -1,4 +1,4 @@ -import { AccountWallet, CompleteAddress, PXE } from '@aztec/aztec.js'; +import { AccountWallet, AztecAddress, CompleteAddress, Fr, PXE } from '@aztec/aztec.js'; import { InclusionProofsContract } from '@aztec/noir-contracts/types'; import { jest } from '@jest/globals'; @@ -29,7 +29,7 @@ describe('e2e_inclusion_proofs_contract', () => { afterAll(() => teardown()); - it('creates a note and proves its existence', async () => { + 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; { @@ -48,14 +48,16 @@ describe('e2e_inclusion_proofs_contract', () => { // Prove note inclusion in a given block. // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - await contract.methods.proveNoteInclusion(owner, blockNumber).send().wait(); + const ignoredCommitment = 0; // Not ignored only when the note doesn't exist + await contract.methods.proveNoteInclusion(owner, blockNumber, ignoredCommitment).send().wait(); } { // Prove that the note has not been nullified // We prove the note existence at current block number because we don't currently have historical data const blockNumber = await pxe.getBlockNumber(); - await contract.methods.proveNullifierNonInclusion(owner, blockNumber, 0).send().wait(); + const ignoredNullifier = 0; // Not ignored only when the note doesn't exist + await contract.methods.proveNullifierNonInclusion(owner, blockNumber, ignoredNullifier).send().wait(); } { @@ -75,11 +77,30 @@ describe('e2e_inclusion_proofs_contract', () => { } }); + it('note existence failure case', async () => { + // Owner of a note + const owner = AztecAddress.random(); + + const blockNumber = await pxe.getBlockNumber(); + const randomNoteCommitment = Fr.random(); + await expect( + contract.methods.proveNoteInclusion(owner, blockNumber, randomNoteCommitment).send().wait(), + ).rejects.toThrow(/Leaf value: 0x[0-9a-fA-F]+ not found in tree/); + }); + it('proves an existence of a public value in private context', async () => { const blockNumber = await pxe.getBlockNumber(); await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); }); + it('public value existence failure case', async () => { + const blockNumber = await pxe.getBlockNumber(); + const randomPublicValue = Fr.random(); + await expect( + contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), + ).rejects.toThrow(/Proving membership of a value in public data tree failed/); + }); + it('proves existence of a nullifier in private context', async () => { const blockNumber = await pxe.getBlockNumber(); const block = await pxe.getBlock(blockNumber); @@ -87,4 +108,13 @@ describe('e2e_inclusion_proofs_contract', () => { await contract.methods.proveNullifierInclusion(nullifier!, blockNumber).send().wait(); }); + + it('nullifier existence failure case', async () => { + const blockNumber = await pxe.getBlockNumber(); + const randomNullifier = Fr.random(); + + await expect(contract.methods.proveNullifierInclusion(randomNullifier, blockNumber).send().wait()).rejects.toThrow( + /Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/, + ); + }); }); 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 338b7bb934f..fc0d901ea18 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 @@ -101,6 +101,7 @@ contract InclusionProofs { fn proveNoteInclusion( owner: AztecAddress, block_number: Field, // 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_data // --> This will either require a new oracle method that returns block_data.global_variables_hash preimage @@ -114,10 +115,15 @@ contract InclusionProofs { let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = private_values.get_notes(options); - let note = notes[0].unwrap(); + let maybe_note = notes[0]; // 3) Compute the commitment from the note - let note_commitment = note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, note); + let note_commitment = if maybe_note.is_some() { + note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, maybe_note.unwrap_unchecked()) + } else { + // Note was not found so we will use the spare commitment + spare_commitment + }; // 4) Get the membership witness of the note in the note hash tree let note_hash_tree_id = 2; // TODO(#3443) From b8a1e6c168214ab3849616e42ddd95a6c1c983eb Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 13:45:24 +0000 Subject: [PATCH 67/69] TODOs --- .../aztec/src/oracle/get_nullifier_membership_witness.nr | 2 +- .../src/contracts/inclusion_proofs_contract/src/utils.nr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 a18f49c57ee..64d073d42cb 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 @@ -7,7 +7,7 @@ global LEAF_DATA_LENGTH: Field = 3; global NULLIFIER_MEMBERSHIP_WITNESS: Field = 24; // Noir version of LeafData interface from indexed merkle tree. -// replace with /mnt/user-data/jan/aztec-packages/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/nullifier_leaf_preimage.nr +// TODO(#3470) replace with /mnt/user-data/jan/aztec-packages/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/abis/nullifier_leaf_preimage.nr struct LeafData { value: Field, next_index: Field, diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr index 85c7b144fe6..e67cf2fd776 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr @@ -1,5 +1,5 @@ -// Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599 -// TODO: move to a shared place? +// TODO(#3470): Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599 +// move to a shared place? // TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports pub fn full_field_less_than(lhs: Field, rhs: Field) -> bool { From edf4628c48a81c94e2e9f6c2d1f8065cf37ce496 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 14:43:38 +0000 Subject: [PATCH 68/69] clearer errors --- .../acir-simulator/src/client/view_data_oracle.ts | 2 +- .../src/e2e_inclusion_proofs_contract.test.ts | 8 +++++--- .../contracts/inclusion_proofs_contract/src/main.nr | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) 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 60c927545ed..fe124f853d2 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -45,7 +45,7 @@ export class ViewDataOracle extends TypedOracle { public async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { const index = await this.db.findLeafIndex(blockNumber, treeId, leafValue); if (!index) { - throw new Error(`Leaf value: ${leafValue} not found in tree ${treeId}`); + 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]; 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 e18da5fcd18..d2b5d7aac34 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 @@ -73,7 +73,9 @@ describe('e2e_inclusion_proofs_contract', () => { // on low_nullifier.value < nullifier.value check. await expect( contract.methods.proveNullifierNonInclusion(owner, blockNumber, nullifier).send().wait(), - ).rejects.toThrowError(/Proving that low_nullifier.value < nullifier.value failed/); + ).rejects.toThrowError( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); } }); @@ -85,7 +87,7 @@ describe('e2e_inclusion_proofs_contract', () => { const randomNoteCommitment = Fr.random(); await expect( contract.methods.proveNoteInclusion(owner, blockNumber, randomNoteCommitment).send().wait(), - ).rejects.toThrow(/Leaf value: 0x[0-9a-fA-F]+ not found in tree/); + ).rejects.toThrow(/Leaf value: 0x[0-9a-fA-F]+ not found in NOTE_HASH_TREE/); }); it('proves an existence of a public value in private context', async () => { @@ -98,7 +100,7 @@ describe('e2e_inclusion_proofs_contract', () => { const randomPublicValue = Fr.random(); await expect( contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), - ).rejects.toThrow(/Proving membership of a value in public data tree failed/); + ).rejects.toThrow(/Proving public value inclusion failed/); }); it('proves existence of a nullifier in private context', async () => { 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 fc0d901ea18..7a8871b7218 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 @@ -133,7 +133,7 @@ contract InclusionProofs { // 5) Prove that the commitment is in the note hash tree assert( block_data.note_hash_tree_root == compute_merkle_root(note_commitment, witness.index, witness.path), - "Proving membership of a note in note hash tree failed" + "Proving note inclusion failed" ); // --> Now we have traversed the trees all the way up to blocks tree root. @@ -177,20 +177,20 @@ contract InclusionProofs { let low_nullifier_leaf = witness.leaf_data.hash(); assert( block_data.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), - "Proving membership of a low nullifier in the nullifier tree failed" + "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" ); // 5.b) Prove that the low nullifier is smaller than the nullifier assert( full_field_less_than(witness.leaf_data.value, nullifier), - "Proving that low_nullifier.value < nullifier.value failed" + "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" ); // 5.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not // included in the nullifier tree assert( full_field_greater_than(witness.leaf_data.next_value, nullifier), - "Proving that low_nullifier.next_value > nullifier.value failed" + "Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" ); // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier @@ -237,7 +237,7 @@ contract InclusionProofs { // 5) Prove that the nullifier is in the nullifier tree assert( block_data.nullifier_tree_root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), - "Proving membership of a nullifier in nullifier tree failed" + "Proving nullifier inclusion failed" ); // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier @@ -274,7 +274,7 @@ contract InclusionProofs { // 4) Prove that the public value provided on input is in the public data tree assert( block_data.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), - "Proving membership of a value in public data tree failed" + "Proving public value inclusion failed" ); // --> Now we have traversed the trees all the way up to blocks tree root and that way verified that From da730d909b18f1694e55f106fb82b3046397cc3a Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 29 Nov 2023 15:51:20 +0000 Subject: [PATCH 69/69] using Promise.all --- yarn-project/aztec-node/src/aztec-node/server.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index eb43c1962ff..817b1a6b206 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -400,14 +400,19 @@ export class AztecNodeService implements AztecNode { if (!index) { return undefined; } - const leafData = await committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, Number(index)); - if (!leafData) { - return undefined; - } - const siblingPath = await committedDb.getSiblingPath( + + const leafDataPromise = committedDb.getLeafData(MerkleTreeId.NULLIFIER_TREE, Number(index)); + const siblingPathPromise = committedDb.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); + + const [leafData, siblingPath] = await Promise.all([leafDataPromise, siblingPathPromise]); + + if (!leafData) { + return undefined; + } + return new NullifierMembershipWitness(BigInt(index), leafData, siblingPath); }