Skip to content

Commit

Permalink
feat: add comparators to get/view note (#3136) (#4205)
Browse files Browse the repository at this point in the history
This adds a comparator in getting or viewing notes. Before we could only
get notes with strict equality, now we can use any of the 6 defined
comparators.

See below for more context
resolves #3136
  • Loading branch information
sklppy88 authored Jan 25, 2024
1 parent f8673ca commit 6de36b3
Show file tree
Hide file tree
Showing 22 changed files with 401 additions and 50 deletions.
8 changes: 4 additions & 4 deletions boxes/token/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract Token {
address::AztecAddress,
}
};

// docs:start:import_authwit
use dep::authwit::{
auth::{
Expand Down Expand Up @@ -246,9 +246,9 @@ contract Token {
fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) {
let pending_shields = storage.pending_shields;
let secret_hash = compute_secret_hash(secret);
// Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash
// stored in field with index 1 (select(1, secret_hash)).
let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1);
// Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount, Option::none())) and secret_hash
// stored in field with index 1 (select(1, secret_hash, Option::none())).
let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1);
let notes = pending_shields.get_notes(options);
let note = notes[0].unwrap_unchecked();
// Remove the note from the pending shields set
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/acir-simulator/src/acvm/oracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export class Oracle {
[numSelects]: ACVMField[],
selectBy: ACVMField[],
selectValues: ACVMField[],
selectComparators: ACVMField[],
sortBy: ACVMField[],
sortOrder: ACVMField[],
[limit]: ACVMField[],
Expand All @@ -178,6 +179,7 @@ export class Oracle {
+numSelects,
selectBy.map(s => +s),
selectValues.map(fromACVMField),
selectComparators.map(s => +s),
sortBy.map(s => +s),
sortOrder.map(s => +s),
+limit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export abstract class TypedOracle {
_numSelects: number,
_selectBy: number[],
_selectValues: Fr[],
_selectComparators: number[],
_sortBy: number[],
_sortOrder: number[],
_limit: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export class ClientExecutionContext extends ViewDataOracle {
* @param numSelects - The number of valid selects in selectBy and selectValues.
* @param selectBy - An array of indices of the fields to selects.
* @param selectValues - The values to match.
* @param selectComparators - The comparators to match by.
* @param sortBy - An array of indices of the fields to sort.
* @param sortOrder - The order of the corresponding index in sortBy. (1: DESC, 2: ASC, 0: Do nothing)
* @param limit - The number of notes to retrieve per query.
Expand All @@ -196,6 +197,7 @@ export class ClientExecutionContext extends ViewDataOracle {
numSelects: number,
selectBy: number[],
selectValues: Fr[],
selectComparators: number[],
sortBy: number[],
sortOrder: number[],
limit: number,
Expand All @@ -209,7 +211,9 @@ export class ClientExecutionContext extends ViewDataOracle {
const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value));

const notes = pickNotes<NoteData>([...dbNotesFiltered, ...pendingNotes], {
selects: selectBy.slice(0, numSelects).map((index, i) => ({ index, value: selectValues[i] })),
selects: selectBy
.slice(0, numSelects)
.map((index, i) => ({ index, value: selectValues[i], comparator: selectComparators[i] })),
sorts: sortBy.map((index, i) => ({ index, order: sortOrder[i] })),
limit,
offset,
Expand Down
121 changes: 111 additions & 10 deletions yarn-project/acir-simulator/src/client/pick_notes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Note } from '@aztec/circuit-types';
import { Comparator, Note } from '@aztec/circuit-types';
import { Fr } from '@aztec/foundation/fields';

import { SortOrder, pickNotes } from './pick_notes.js';
Expand Down Expand Up @@ -141,7 +141,7 @@ describe('getNotes', () => {
];

{
const options = { selects: [{ index: 0, value: new Fr(2n) }] };
const options = { selects: [{ index: 0, value: new Fr(2n), comparator: Comparator.EQ }] };
const result = pickNotes(notes, options);
expectNotes(result, [
[2n, 1n, 3n],
Expand All @@ -153,8 +153,8 @@ describe('getNotes', () => {
{
const options = {
selects: [
{ index: 0, value: new Fr(2n) },
{ index: 2, value: new Fr(3n) },
{ index: 0, value: new Fr(2n), comparator: Comparator.EQ },
{ index: 2, value: new Fr(3n), comparator: Comparator.EQ },
],
};
const result = pickNotes(notes, options);
Expand All @@ -167,25 +167,25 @@ describe('getNotes', () => {
{
const options = {
selects: [
{ index: 1, value: new Fr(2n) },
{ index: 2, value: new Fr(3n) },
{ index: 1, value: new Fr(2n), comparator: Comparator.EQ },
{ index: 2, value: new Fr(3n), comparator: Comparator.EQ },
],
};
const result = pickNotes(notes, options);
expectNotes(result, [[1n, 2n, 3n]]);
}

{
const options = { selects: [{ index: 1, value: new Fr(5n) }] };
const options = { selects: [{ index: 1, value: new Fr(5n), comparator: Comparator.EQ }] };
const result = pickNotes(notes, options);
expectNotes(result, []);
}

{
const options = {
selects: [
{ index: 0, value: new Fr(2n) },
{ index: 1, value: new Fr(5n) },
{ index: 0, value: new Fr(2n), comparator: Comparator.EQ },
{ index: 1, value: new Fr(5n), comparator: Comparator.EQ },
],
};
const result = pickNotes(notes, options);
Expand All @@ -203,7 +203,10 @@ describe('getNotes', () => {
createNote([6n, 5n, 8n]),
];

const options = { selects: [{ index: 2, value: new Fr(8n) }], sorts: [{ index: 1, order: SortOrder.ASC }] };
const options = {
selects: [{ index: 2, value: new Fr(8n), comparator: Comparator.EQ }],
sorts: [{ index: 1, order: SortOrder.ASC }],
};
const result = pickNotes(notes, options);
expectNotes(result, [
[0n, 0n, 8n],
Expand All @@ -212,4 +215,102 @@ describe('getNotes', () => {
[7n, 6n, 8n],
]);
});

it('should get sorted matching notes with GTE and LTE', () => {
const notes = [
createNote([2n, 1n, 3n]),
createNote([4n, 5n, 8n]),
createNote([7n, 6n, 8n]),
createNote([6n, 5n, 2n]),
createNote([0n, 0n, 8n]),
createNote([6n, 5n, 8n]),
];

const options = {
selects: [
{
index: 2,
value: new Fr(7n),
comparator: Comparator.GTE,
},
{
index: 2,
value: new Fr(8n),
comparator: Comparator.LTE,
},
],
sorts: [
{
index: 1,
order: SortOrder.ASC,
},
],
};
const result = pickNotes(notes, options);
expectNotes(result, [
[0n, 0n, 8n],
[4n, 5n, 8n],
[6n, 5n, 8n],
[7n, 6n, 8n],
]);
});

it('should get sorted matching notes with GTE and LTE', () => {
const notes = [
createNote([2n, 1n, 1n]),
createNote([4n, 5n, 2n]),
createNote([7n, 6n, 3n]),
createNote([6n, 5n, 4n]),
createNote([0n, 0n, 5n]),
createNote([6n, 5n, 6n]),
];

const options1 = {
selects: [
{
index: 2,
value: new Fr(3n),
comparator: Comparator.GT,
},
],
sorts: [
{
index: 1,
order: SortOrder.ASC,
},
],
};

const result1 = pickNotes(notes, options1);

expectNotes(result1, [
[0n, 0n, 5n],
[6n, 5n, 4n],
[6n, 5n, 6n],
]);

const options2 = {
selects: [
{
index: 2,
value: new Fr(4n),
comparator: Comparator.LT,
},
],
sorts: [
{
index: 1,
order: SortOrder.ASC,
},
],
};

const result2 = pickNotes(notes, options2);

expectNotes(result2, [
[2n, 1n, 1n],
[4n, 5n, 2n],
[7n, 6n, 3n],
]);
});
});
21 changes: 19 additions & 2 deletions yarn-project/acir-simulator/src/client/pick_notes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Note } from '@aztec/circuit-types';
import { Comparator, Note } from '@aztec/circuit-types';
import { Fr } from '@aztec/foundation/fields';

/**
Expand All @@ -13,6 +13,10 @@ export interface Select {
* Required value of the field.
*/
value: Fr;
/**
* The comparator to use
*/
comparator: Comparator;
}

/**
Expand Down Expand Up @@ -75,7 +79,20 @@ interface ContainsNote {
}

const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]): T[] =>
noteDatas.filter(noteData => selects.every(({ index, value }) => noteData.note.items[index]?.equals(value)));
noteDatas.filter(noteData =>
selects.every(({ index, value, comparator }) => {
const comparatorSelector = {
[Comparator.EQ]: () => noteData.note.items[index].equals(value),
[Comparator.NEQ]: () => !noteData.note.items[index].equals(value),
[Comparator.LT]: () => noteData.note.items[index].lt(value),
[Comparator.LTE]: () => noteData.note.items[index].lt(value) || noteData.note.items[index].equals(value),
[Comparator.GT]: () => !noteData.note.items[index].lt(value) && !noteData.note.items[index].equals(value),
[Comparator.GTE]: () => !noteData.note.items[index].lt(value),
};

return comparatorSelector[comparator]();
}),
);

const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => {
if (sorts[level] === undefined) {
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/acir-simulator/src/client/view_data_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export class ViewDataOracle extends TypedOracle {
* @param numSelects - The number of valid selects in selectBy and selectValues.
* @param selectBy - An array of indices of the fields to selects.
* @param selectValues - The values to match.
* @param selectComparators - The comparators to use to match values.
* @param sortBy - An array of indices of the fields to sort.
* @param sortOrder - The order of the corresponding index in sortBy. (1: DESC, 2: ASC, 0: Do nothing)
* @param limit - The number of notes to retrieve per query.
Expand All @@ -209,14 +210,17 @@ export class ViewDataOracle extends TypedOracle {
numSelects: number,
selectBy: number[],
selectValues: Fr[],
selectComparators: number[],
sortBy: number[],
sortOrder: number[],
limit: number,
offset: number,
): Promise<NoteData[]> {
const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot);
return pickNotes<NoteData>(dbNotes, {
selects: selectBy.slice(0, numSelects).map((index, i) => ({ index, value: selectValues[i] })),
selects: selectBy
.slice(0, numSelects)
.map((index, i) => ({ index, value: selectValues[i], comparator: selectComparators[i] })),
sorts: sortBy.map((index, i) => ({ index, order: sortOrder[i] })),
limit,
offset,
Expand Down
Loading

0 comments on commit 6de36b3

Please sign in to comment.