Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gaztecrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.0-devnet.2
3.0.0-devnet.20251212
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

Prove Aztec note inclusion in plain Noir. Generate verifiable proofs for verification in JS or Solidity.

**Supports exactly Aztec 3.0.0-devnet.2**
**Supports exactly Aztec 3.0.0-devnet.20251212**

Install in JS:

```sh
npm add @nemi-fi/[email protected].2
npm add @nemi-fi/[email protected].20251212
```

Install in Noir (Nargo.toml):

```toml
[dependencies]
storage_proofs = { git = "https://github.com/nemi-fi/aztec_storage_proofs", tag = "v3.0.0-devnet.2", directory = "lib" }
storage_proofs = { git = "https://github.com/nemi-fi/aztec_storage_proofs", tag = "v3.0.0-devnet.20251212", directory = "lib" }
```

For an end to end example, see [lib.test.ts](lib.test.ts).
4 changes: 2 additions & 2 deletions example_circuit/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ name = "example_circuit"
type = "bin"

[dependencies]
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.2", directory = "noir-projects/aztec-nr/aztec" }
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.2", directory = "noir-projects/aztec-nr/uint-note" }
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.20251212", directory = "noir-projects/aztec-nr/aztec" }
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.20251212", directory = "noir-projects/aztec-nr/uint-note" }
storage_proofs = { path = "../lib" }
18 changes: 10 additions & 8 deletions example_circuit/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use aztec::protocol_types::{
address::AztecAddress,
constants::NOTE_HASH_TREE_HEIGHT,
merkle_tree::membership::MembershipWitness,
storage::map::derive_storage_slot_in_map,
traits::{FromField, ToField},
address::AztecAddress, constants::NOTE_HASH_TREE_HEIGHT,
merkle_tree::membership::MembershipWitness, traits::FromField,
};
use uint_note::uint_note::UintNote;

fn main(
note: UintNote,
// The owner of the note.
owner: Field,
// The membership witness for the note hash in the note hash tree.
membership_witness: MembershipWitness<NOTE_HASH_TREE_HEIGHT>,
// The nonce of the note for the note hash.
note_nonce: Field,
// The randomness of the note.
randomness: Field,
// The Aztec smart contract address.
contract_address: pub Field,
// The storage slot of the mapping in the contract.
Expand All @@ -22,16 +23,17 @@ fn main(
// The real root of the note hash tree.
note_hash_tree_root: pub Field,
) {
let real_value = note.get_value();
let real_value = note.value;
assert(real_value == expected_value, f"value mismatch: {real_value} != {expected_value}");

let storage_slot = derive_storage_slot_in_map(map_storage_slot, note.get_owner().to_field());
storage_proofs::assert_note_inclusion(
note,
note_nonce,
randomness,
membership_witness,
AztecAddress::from_field(contract_address),
storage_slot,
map_storage_slot,
AztecAddress::from_field(owner),
note_hash_tree_root,
);
}
4 changes: 2 additions & 2 deletions example_contract/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ name = "example_contract"
type = "contract"

[dependencies]
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.2", directory = "noir-projects/aztec-nr/aztec" }
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.2", directory = "noir-projects/aztec-nr/uint-note" }
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.20251212", directory = "noir-projects/aztec-nr/aztec" }
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.20251212", directory = "noir-projects/aztec-nr/uint-note" }
storage_proofs = { path = "../lib" }
20 changes: 10 additions & 10 deletions example_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,37 @@ contract StorageProof {
use aztec::{
macros::{functions::{external, view}, storage::storage},
messages::message_delivery::MessageDelivery,
note::note_getter_options::NoteGetterOptions,
protocol_types::address::AztecAddress,
state_vars::{Map, PrivateMutable},
state_vars::{Owned, PrivateMutable, StateVariable},
};

use uint_note::uint_note::UintNote;

#[storage]
struct Storage<C> {
value: Map<AztecAddress, PrivateMutable<UintNote, C>, C>,
value: Owned<PrivateMutable<UintNote, C>, C>,
}

#[external("private")]
fn set_value(value: u128) {
let sender = context.msg_sender().expect(f"sender is not set");
storage.value.at(sender).initialize_or_replace(|_| UintNote::new(value, sender)).emit(
sender,
let sender = self.msg_sender().expect(f"sender is not set");
self.storage.value.at(sender).initialize_or_replace(|_| UintNote { value }).deliver(
MessageDelivery.CONSTRAINED_ONCHAIN,
);
}

#[external("utility")]
unconstrained fn get_value(owner: AztecAddress) -> u128 {
storage.value.at(owner).view_note().get_value()
self.storage.value.at(owner).view_note().value
}

#[external("private")]
#[view]
fn get_note(owner: AztecAddress) -> pub storage_proofs::NoteInclusionData<UintNote> {
let mut options = NoteGetterOptions::new();
options = options.set_limit(1);
storage_proofs::get_note_inclusion_data(&mut context, storage.value.at(owner), options)
storage_proofs::get_note_inclusion_data(
self.context,
self.storage.value.get_storage_slot(),
Option::some(owner),
)
}
}
8 changes: 5 additions & 3 deletions js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { AztecAddress, EthAddress } from "@aztec/aztec.js/addresses";
import { Fr } from "@aztec/aztec.js/fields";
import type { AztecNode } from "@aztec/aztec.js/node";
import { MerkleTreeId } from "@aztec/aztec.js/trees";
import type { BlockNumber } from "@aztec/foundation/branded-types";
import type { MembershipWitness } from "@aztec/foundation/trees";
import type { L2BlockNumber } from "@aztec/stdlib/block";
import type { BlockParameter } from "@aztec/stdlib/block";
import { mapValues } from "lodash-es";

export class NoteInclusionData {
Expand All @@ -15,7 +16,7 @@ export class NoteInclusionData {
this.note_hash = fields.note_hash;
}

async toNoirInput(node: AztecNode, blockNumber: L2BlockNumber = "latest") {
async toNoirInput(node: AztecNode, blockNumber: BlockParameter = "latest") {
if (blockNumber === "latest") {
blockNumber = await node.getBlockNumber();
}
Expand Down Expand Up @@ -59,6 +60,7 @@ export class NoteInclusionData {
note: noteForNoir,
note_nonce,
contract_address,
randomness: this.note.randomness.toString(),
membership_witness: {
leaf_index: membershipWitness.leafIndex.toString(),
sibling_path: membershipWitness.siblingPath.map((p) => p.toString()),
Expand All @@ -85,7 +87,7 @@ export class NoteInclusionData {

async function getNoteHashTreeMembershipWitness(
node: AztecNode,
blockNumber: number,
blockNumber: BlockNumber,
noteHash: Fr,
): Promise<Pick<MembershipWitness<number>, "leafIndex" | "siblingPath">> {
const [indexData] = await node.findLeavesIndexes(
Expand Down
13 changes: 4 additions & 9 deletions lib.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import "fake-indexeddb/auto";
// @ts-ignore
globalThis.self = globalThis; // needed by pxe https://github.com/AztecProtocol/aztec-packages/issues/14135

import { getInitialTestAccountsData } from "@aztec/accounts/testing";
import { createAztecNodeClient } from "@aztec/aztec.js/node";
import { UltraHonkBackend } from "@aztec/bb.js";
import { Barretenberg, UltraHonkBackend } from "@aztec/bb.js";
import { Noir, type CompiledCircuit, type InputMap } from "@aztec/noir-noir_js";
import { getPXEConfig } from "@aztec/pxe/client/bundle";
import { TestWallet } from "@aztec/test-wallet/server";
import { expect, test } from "vitest";
import { NoteInclusionData } from "./js/index.js";
Expand All @@ -15,8 +10,6 @@ import example_circuit from "./target_circuits/example_circuit.json" with { type

test("flow", async () => {
const node = createAztecNodeClient("http://localhost:8080");
const config = getPXEConfig();
config.proverEnabled = false;
const accounts = await getInitialTestAccountsData();
const wallet = await TestWallet.create(node);
const alice = await (
Expand Down Expand Up @@ -50,6 +43,7 @@ test("flow", async () => {
const proof = await generateProof(example_circuit as CompiledCircuit, {
...input,
map_storage_slot: 1, // position in `struct Storage` (1-based indexing)
owner: alice.getAddress().toString(),
expected_value: noteInclusionData.note.note.value.toString(),
});
console.log("proof", proof.proof.length);
Expand All @@ -58,7 +52,8 @@ test("flow", async () => {

async function generateProof(circuit: CompiledCircuit, input: InputMap) {
const noir = new Noir(circuit);
const backend = new UltraHonkBackend(circuit.bytecode);
const api = await Barretenberg.initSingleton();
const backend = new UltraHonkBackend(circuit.bytecode, api);

const { witness } = await noir.execute(input);

Expand Down
2 changes: 1 addition & 1 deletion lib/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ type = "lib"
authors = ["nemi.fi"]

[dependencies]
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.2", directory = "noir-projects/aztec-nr/aztec" }
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.20251212", directory = "noir-projects/aztec-nr/aztec" }
30 changes: 15 additions & 15 deletions lib/src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use aztec::{
context::PrivateContext,
note::{
note_getter::get_notes,
note_getter_options::NoteGetterOptions,
note_getter::get_note,
note_interface::{NoteHash, NoteType},
note_metadata::{NoteMetadata, SettledNoteMetadata},
retrieved_note::RetrievedNote,
Expand All @@ -12,17 +11,18 @@ use aztec::{
address::AztecAddress,
constants::NOTE_HASH_TREE_HEIGHT,
merkle_tree::{membership::MembershipWitness, root::root_from_sibling_path},
traits::{Packable, Serialize},
traits::{Deserialize, Packable, Serialize},
},
state_vars::storage::HasStorageSlot,
};

pub fn assert_note_inclusion<NOTE>(
note: NOTE,
note_nonce: Field,
randomness: Field,
membership_witness: MembershipWitness<NOTE_HASH_TREE_HEIGHT>,
contract_address: AztecAddress,
storage_slot: Field,
owner: AztecAddress,
note_hash_tree_root: Field,
)
where
Expand All @@ -32,9 +32,12 @@ where
let retrieved_note = RetrievedNote {
note,
contract_address,
storage_slot,
owner,
metadata: NoteMetadata::from(SettledNoteMetadata::new(note_nonce)),
randomness,
};
let note_hash = compute_note_hash_for_nullification(retrieved_note, storage_slot);
let note_hash = compute_note_hash_for_nullification(retrieved_note);
let computed_root = root_from_sibling_path(
note_hash,
membership_witness.leaf_index,
Expand All @@ -46,23 +49,20 @@ where
);
}

pub fn get_note_inclusion_data<NOTE, let N: u32, let M: u32, PREPROCESSOR_ARGS, FILTER_ARGS>(
pub fn get_note_inclusion_data<NOTE>(
context: &mut PrivateContext,
storage_var: impl HasStorageSlot<N>,
options: NoteGetterOptions<NOTE, M, PREPROCESSOR_ARGS, FILTER_ARGS>,
storage_slot: Field,
owner: Option<AztecAddress>,
) -> NoteInclusionData<NOTE>
where
NOTE: NoteType + NoteHash + Packable<N = M> + Eq,
NOTE: NoteType + NoteHash + Packable,
{
let storage_slot = storage_var.get_storage_slot();
let (retrieved_notes, _): (BoundedVec<RetrievedNote<NOTE>, _>, _) =
get_notes::<NOTE, _, PREPROCESSOR_ARGS, FILTER_ARGS>(context, storage_slot, options);
let note = retrieved_notes.get(0);
let note_hash = compute_note_hash_for_nullification(note, storage_slot);
let (note, _) = get_note(context, owner, storage_slot);
let note_hash = compute_note_hash_for_nullification(note);
NoteInclusionData { note, note_hash }
}

#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct NoteInclusionData<Note> {
pub note: RetrievedNote<Note>,
pub note_hash: Field,
Expand Down
22 changes: 10 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nemi-fi/aztec-storage-proofs",
"type": "module",
"version": "3.0.0-devnet.2",
"version": "3.0.0-devnet.20251212",
"files": [
"js",
"dist"
Expand All @@ -19,27 +19,25 @@
}
},
"scripts": {
"compile": "rm -rf target_circuits && gaztec-nargo compile && mkdir -p target_circuits && mv target/*_circuit.json target_circuits && gaztec-postprocess-contract && gaztec codegen target -o target --force",
"compile": "npx gaztec compile && mkdir -p target_circuits && mv target/*_circuit.json target_circuits && npx gaztec codegen target -o target --force",
"build": "rm -rf dist && tsc",
"test": "vitest run",
"prepublishOnly": "pnpm build && pnpm test"
},
"packageManager": "[email protected]",
"dependencies": {
"@aztec/aztec.js": "3.0.0-devnet.2",
"@aztec/foundation": "3.0.0-devnet.2",
"@aztec/stdlib": "3.0.0-devnet.2",
"@aztec/aztec.js": "3.0.0-devnet.20251212",
"@aztec/foundation": "3.0.0-devnet.20251212",
"@aztec/stdlib": "3.0.0-devnet.20251212",
"lodash-es": "^4.17.21"
},
"devDependencies": {
"@aztec/accounts": "3.0.0-devnet.2",
"@aztec/bb.js": "3.0.0-devnet.2",
"@aztec/noir-noir_js": "3.0.0-devnet.2",
"@aztec/pxe": "3.0.0-devnet.2",
"@aztec/test-wallet": "3.0.0-devnet.2",
"@aztec/accounts": "3.0.0-devnet.20251212",
"@aztec/bb.js": "3.0.0-devnet.20251212",
"@aztec/noir-noir_js": "3.0.0-devnet.20251212",
"@aztec/test-wallet": "3.0.0-devnet.20251212",
"@types/lodash-es": "^4.17.12",
"fake-indexeddb": "^6.0.0",
"gaztec": "^0.0.6",
"gaztec": "^0.1.0",
"prettier": "^3.5.3",
"tsx": "^4.19.4",
"typescript": "^5.8.3",
Expand Down
Loading