Skip to content

Commit bd10595

Browse files
authored
feat: enforce note hash read requests to read within own contract (#6310)
Please read [contributing guidelines](CONTRIBUTING.md) and remove this line.
1 parent 84b9fcd commit bd10595

8 files changed

Lines changed: 44 additions & 46 deletions

File tree

noir-projects/aztec-nr/aztec/src/note/note_getter.nr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::note::{
44
constants::{GET_NOTE_ORACLE_RETURN_LENGTH, MAX_NOTES_PER_PAGE, VIEW_NOTE_ORACLE_RETURN_LENGTH},
55
note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder, Comparator, NoteStatus, PropertySelector},
66
note_interface::NoteInterface, note_viewer_options::NoteViewerOptions,
7-
utils::compute_note_hash_for_consumption
7+
utils::compute_note_hash_for_read_request
88
};
99
use crate::oracle;
1010

@@ -92,7 +92,7 @@ pub fn get_note<Note, N>(
9292

9393
check_note_header(*context, storage_slot, note);
9494

95-
let note_hash_for_read_request = compute_note_hash_for_consumption(note);
95+
let note_hash_for_read_request = compute_note_hash_for_read_request(note);
9696

9797
context.push_note_hash_read_request(note_hash_for_read_request);
9898
note
@@ -130,7 +130,7 @@ pub fn _get_notes_constrain_get_notes_internal<Note, N, FILTER_ARGS>(
130130
}
131131
prev_fields = fields;
132132

133-
let note_hash_for_read_request = compute_note_hash_for_consumption(note);
133+
let note_hash_for_read_request = compute_note_hash_for_read_request(note);
134134
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1410): test to ensure
135135
// failure if malicious oracle injects 0 nonce here for a "pre-existing" note.
136136
context.push_note_hash_read_request(note_hash_for_read_request);

noir-projects/aztec-nr/aztec/src/note/utils.nr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ pub fn compute_note_hash_for_insertion<Note, N>(note: Note) -> Field where Note:
6767
compute_inner_note_hash(note)
6868
}
6969

70+
pub fn compute_note_hash_for_read_request<Note, N>(note: Note) -> Field where Note: NoteInterface<N> {
71+
let header = note.get_header();
72+
73+
if (header.nonce != 0) {
74+
compute_unique_note_hash(note)
75+
} else {
76+
compute_inner_note_hash(note)
77+
}
78+
}
79+
7080
pub fn compute_note_hash_for_consumption<Note, N>(note: Note) -> Field where Note: NoteInterface<N> {
7181
let header = note.get_header();
7282
// There are 3 cases for reading a note intended for consumption:

noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::note::{
44
constants::MAX_NOTES_PER_PAGE, lifecycle::{create_note, create_note_hash_from_public, destroy_note},
55
note_getter::{get_notes, view_notes}, note_getter_options::NoteGetterOptions,
66
note_header::NoteHeader, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions,
7-
utils::compute_note_hash_for_consumption
7+
utils::compute_note_hash_for_read_request
88
};
99
use crate::state_vars::storage::Storage;
1010

@@ -58,7 +58,7 @@ impl<Note> PrivateSet<Note> {
5858
// docs:start:remove
5959
pub fn remove<N>(self, note: Note) where Note: NoteInterface<N> {
6060
let context = self.context.private.unwrap();
61-
let note_hash = compute_note_hash_for_consumption(note);
61+
let note_hash = compute_note_hash_for_read_request(note);
6262
let has_been_read = context.note_hash_read_requests.any(|r: ReadRequest| r.value == note_hash);
6363
assert(has_been_read, "Can only remove a note that has been read from the set.");
6464

noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,11 @@ mod tests {
5959
global note_hashes = inner_note_hashes.map(|n| silo_note_hash(contract_address, n));
6060

6161
// Create 5 read requests. 0 and 3 are reading settled note hashes. 1, 2 and 4 are reading pending note hashes.
62-
// TODO(#2847): Read request values for settled note hashes shouldn't have been siloed by apps.
6362
global read_requests = [
64-
ReadRequest { value: note_hashes[1], counter: 11 }.scope(contract_address), // settled
63+
ReadRequest { value: inner_note_hashes[1], counter: 11 }.scope(contract_address), // settled
6564
ReadRequest { value: inner_note_hashes[3], counter: 13 }.scope(contract_address), // pending
6665
ReadRequest { value: inner_note_hashes[2], counter: 39 }.scope(contract_address), // pending
67-
ReadRequest { value: note_hashes[0], counter: 46 }.scope(contract_address), // settled
66+
ReadRequest { value: inner_note_hashes[0], counter: 46 }.scope(contract_address), // settled
6867
ReadRequest { value: inner_note_hashes[3], counter: 78 }.scope(contract_address), // pending
6968
];
7069

noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash_leaf_preimage.nr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@ impl LeafPreimage for NoteHashLeafPreimage {
2929

3030
impl Readable for NoteHashLeafPreimage {
3131
fn assert_match_read_request(self, read_request: ScopedReadRequest) {
32-
// TODO(#2847): Read request value shouldn't have been siloed by apps.
33-
// let siloed_value = silo_note_hash(read_request.contract_address, read_request.value);
34-
// assert_eq(self.value, siloed_value, "Value of the note hash leaf does not match read request");
35-
assert_eq(self.value, read_request.value(), "Value of the note hash leaf does not match read request");
32+
let siloed_value = silo_note_hash(read_request.contract_address, read_request.value());
33+
assert_eq(self.value, siloed_value, "Value of the note hash leaf does not match read request");
3634
}
3735
}
3836

yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.test.ts

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,32 +42,22 @@ describe('buildNoteHashReadRequestHints', () => {
4242

4343
const makeNoteHash = (value: number, counter = 1) => new NoteHash(new Fr(value), counter).scope(0, contractAddress);
4444

45-
const readPendingNoteHash = ({
46-
noteHashIndex,
47-
readRequestIndex = numReadRequests,
48-
hintIndex = numPendingReads,
49-
}: {
50-
noteHashIndex: number;
51-
readRequestIndex?: number;
52-
hintIndex?: number;
53-
}) => {
45+
const readPendingNoteHash = (noteHashIndex: number) => {
46+
const readRequestIndex = numReadRequests;
47+
const hintIndex = numPendingReads;
5448
noteHashReadRequests[readRequestIndex] = makeReadRequest(innerNoteHash(noteHashIndex));
5549
expectedHints.readRequestStatuses[readRequestIndex] = ReadRequestStatus.pending(hintIndex);
5650
expectedHints.pendingReadHints[hintIndex] = new PendingReadHint(readRequestIndex, noteHashIndex);
5751
numReadRequests++;
5852
numPendingReads++;
5953
};
6054

61-
const readSettledNoteHash = ({
62-
readRequestIndex = numReadRequests,
63-
hintIndex = numSettledReads,
64-
}: {
65-
readRequestIndex?: number;
66-
hintIndex?: number;
67-
} = {}) => {
68-
const value = settledNoteHashes[hintIndex];
69-
noteHashLeafIndexMap.set(value.toBigInt(), settledLeafIndexes[hintIndex]);
70-
noteHashReadRequests[readRequestIndex] = new ReadRequest(value, 1).scope(contractAddress);
55+
const readSettledNoteHash = (noteHashIndex: number) => {
56+
const readRequestIndex = numReadRequests;
57+
const hintIndex = numSettledReads;
58+
const value = settledNoteHashes[noteHashIndex];
59+
noteHashLeafIndexMap.set(value.toBigInt(), settledLeafIndexes[noteHashIndex]);
60+
noteHashReadRequests[readRequestIndex] = makeReadRequest(settledNoteHashInnerValues[noteHashIndex]);
7161
expectedHints.readRequestStatuses[readRequestIndex] = ReadRequestStatus.settled(hintIndex);
7262
expectedHints.settledReadHints[hintIndex] = new SettledReadHint(readRequestIndex, {} as any, value);
7363
numReadRequests++;
@@ -93,32 +83,32 @@ describe('buildNoteHashReadRequestHints', () => {
9383
});
9484

9585
it('builds hints for pending note hash read requests', async () => {
96-
readPendingNoteHash({ noteHashIndex: 2 });
97-
readPendingNoteHash({ noteHashIndex: 1 });
86+
readPendingNoteHash(2);
87+
readPendingNoteHash(1);
9888
const hints = await buildHints();
9989
expect(hints).toEqual(expectedHints);
10090
});
10191

10292
it('builds hints for settled note hash read requests', async () => {
103-
readSettledNoteHash();
104-
readSettledNoteHash();
93+
readSettledNoteHash(0);
94+
readSettledNoteHash(1);
10595
const hints = await buildHints();
10696
expect(hints).toEqual(expectedHints);
10797
});
10898

10999
it('builds hints for mixed pending and settled note hash read requests', async () => {
110-
readPendingNoteHash({ noteHashIndex: 2 });
111-
readSettledNoteHash();
112-
readSettledNoteHash();
113-
readPendingNoteHash({ noteHashIndex: 1 });
114-
readPendingNoteHash({ noteHashIndex: 1 });
115-
readSettledNoteHash();
100+
readPendingNoteHash(2);
101+
readSettledNoteHash(2);
102+
readSettledNoteHash(0);
103+
readPendingNoteHash(1);
104+
readPendingNoteHash(1);
105+
readSettledNoteHash(2);
116106
const hints = await buildHints();
117107
expect(hints).toEqual(expectedHints);
118108
});
119109

120110
it('throws if cannot find a match in pending set and in the tree', async () => {
121-
readPendingNoteHash({ noteHashIndex: 2 });
111+
readPendingNoteHash(2);
122112
// Tweak the value of the read request.
123113
noteHashReadRequests[0].readRequest.value = new Fr(123);
124114
await expect(() => buildHints()).rejects.toThrow('Read request is reading an unknown note hash.');

yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
66
type NOTE_HASH_TREE_HEIGHT,
77
} from '../constants.gen.js';
8+
import { siloNoteHash } from '../hash/index.js';
89
import {
910
type MembershipWitness,
1011
NoteHashReadRequestHintsBuilder,
@@ -51,13 +52,13 @@ export async function buildNoteHashReadRequestHints(
5152
if (pendingNoteHash !== undefined) {
5253
builder.addPendingReadRequest(i, pendingNoteHash.index);
5354
} else {
54-
// TODO(#2847): Read request value for settled note hash shouldn't have been siloed by apps.
55-
const leafIndex = noteHashLeafIndexMap.get(value.toBigInt());
55+
const siloedValue = siloNoteHash(readRequest.contractAddress, value);
56+
const leafIndex = noteHashLeafIndexMap.get(siloedValue.toBigInt());
5657
if (leafIndex === undefined) {
5758
throw new Error('Read request is reading an unknown note hash.');
5859
}
5960
const membershipWitness = await oracle.getNoteHashMembershipWitness(leafIndex);
60-
builder.addSettledReadRequest(i, membershipWitness, value);
61+
builder.addSettledReadRequest(i, membershipWitness, siloedValue);
6162
}
6263
}
6364
return builder.toHints();

yarn-project/simulator/src/client/private_execution.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ describe('Private Execution test suite', () => {
438438

439439
const readRequests = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests).map(r => r.value);
440440
expect(readRequests).toHaveLength(consumedNotes.length);
441-
expect(readRequests).toEqual(expect.arrayContaining(consumedNotes.map(n => n.siloedNoteHash)));
441+
expect(readRequests).toEqual(expect.arrayContaining(consumedNotes.map(n => n.uniqueNoteHash)));
442442
});
443443

444444
it('should be able to destroy_and_create with dummy notes', async () => {

0 commit comments

Comments
 (0)