Skip to content

Commit

Permalink
feat!: PXE.getNotes(...) + refactor of note types (#3051)
Browse files Browse the repository at this point in the history
Fixes #2908 
Fixes
[#2909](#2909)
Fixes
[#2910](#2910)
Fixes #3061 

**Note 1**: This PR became huge because it made sense to refactor
`PXE.addNotes` here as well since I now use the ExtendedNote type there.
**Note 2**: Since this PR is already too big I decided to finish
renaming preimage as serialized_note in Noir in a separate PR.
**Note 3**: Note processor now stores a `CompleteAddress` instead of
just a public key because I need the address from `CompleteAddress` to
construct the `ExtendedNote`.
**Note 4**: This is a breaking change because of the `PXE.getNotes`
change and the `PXE.getPrivateStorageAt` removal.
  • Loading branch information
benesjan authored Oct 30, 2023
1 parent a6786ae commit 16abb5a
Show file tree
Hide file tree
Showing 75 changed files with 985 additions and 834 deletions.
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"barretenberg",
"bbfree",
"bbmalloc",
"benesjan",
"bodyparser",
"bootnode",
"Brillig",
Expand Down
24 changes: 12 additions & 12 deletions yarn-project/acir-simulator/src/acvm/oracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class Oracle {
[offset]: ACVMField[],
[returnSize]: ACVMField[],
): Promise<ACVMField[]> {
const notes = await this.typedOracle.getNotes(
const noteDatas = await this.typedOracle.getNotes(
fromACVMField(storageSlot),
+numSelects,
selectBy.map(s => +s),
Expand All @@ -75,26 +75,26 @@ export class Oracle {
+offset,
);

const preimageLength = notes?.[0]?.preimage.length ?? 0;
if (!notes.every(({ preimage }) => preimageLength === preimage.length)) {
throw new Error('Preimages for a particular note type should all be the same length.');
const noteLength = noteDatas?.[0]?.note.items.length ?? 0;
if (!noteDatas.every(({ note }) => noteLength === note.items.length)) {
throw new Error('Notes should all be the same length.');
}

const contractAddress = notes[0]?.contractAddress ?? Fr.ZERO;
const contractAddress = noteDatas[0]?.contractAddress ?? Fr.ZERO;

// Values indicates whether the note is settled or transient.
const noteTypes = {
isSettled: new Fr(0),
isTransient: new Fr(1),
};
const flattenData = notes.flatMap(({ nonce, preimage, index }) => [
const flattenData = noteDatas.flatMap(({ nonce, note, index }) => [
nonce,
index === undefined ? noteTypes.isTransient : noteTypes.isSettled,
...preimage,
...note.items,
]);

const returnFieldSize = +returnSize;
const returnData = [notes.length, contractAddress, ...flattenData].map(v => toACVMField(v));
const returnData = [noteDatas.length, contractAddress, ...flattenData].map(v => toACVMField(v));
if (returnData.length > returnFieldSize) {
throw new Error(`Return data size too big. Maximum ${returnFieldSize} fields. Got ${flattenData.length}.`);
}
Expand All @@ -103,10 +103,10 @@ export class Oracle {
return returnData.concat(paddedZeros);
}

notifyCreatedNote([storageSlot]: ACVMField[], preimage: ACVMField[], [innerNoteHash]: ACVMField[]): ACVMField {
notifyCreatedNote([storageSlot]: ACVMField[], note: ACVMField[], [innerNoteHash]: ACVMField[]): ACVMField {
this.typedOracle.notifyCreatedNote(
fromACVMField(storageSlot),
preimage.map(fromACVMField),
note.map(fromACVMField),
fromACVMField(innerNoteHash),
);
return toACVMField(0);
Expand Down Expand Up @@ -148,14 +148,14 @@ export class Oracle {
[storageSlot]: ACVMField[],
[publicKeyX]: ACVMField[],
[publicKeyY]: ACVMField[],
preimage: ACVMField[],
log: ACVMField[],
): ACVMField {
const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY));
this.typedOracle.emitEncryptedLog(
AztecAddress.fromString(contractAddress),
Fr.fromString(storageSlot),
publicKey,
preimage.map(fromACVMField),
log.map(fromACVMField),
);
return toACVMField(0);
}
Expand Down
10 changes: 5 additions & 5 deletions yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ 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, PublicKey, UnencryptedL2Log } from '@aztec/types';
import { CompleteAddress, Note, PublicKey, UnencryptedL2Log } from '@aztec/types';

/**
* Information about a note needed during execution.
*/
export interface NoteData {
/** The note. */
note: Note;
/** The contract address of the note. */
contractAddress: AztecAddress;
/** The storage slot of the note. */
storageSlot: Fr;
/** The nonce of the note. */
nonce: Fr;
/** The preimage of the note */
preimage: Fr[];
/** The inner note hash of the note. */
innerNoteHash: Fr;
/** The corresponding nullifier of the note. Undefined for pending notes. */
Expand Down Expand Up @@ -93,7 +93,7 @@ export abstract class TypedOracle {
throw new Error('Not available.');
}

notifyCreatedNote(_storageSlot: Fr, _preimage: Fr[], _innerNoteHash: Fr): void {
notifyCreatedNote(_storageSlot: Fr, _note: Fr[], _innerNoteHash: Fr): void {
throw new Error('Not available.');
}

Expand Down Expand Up @@ -121,7 +121,7 @@ export abstract class TypedOracle {
throw new Error('Not available.');
}

emitEncryptedLog(_contractAddress: AztecAddress, _storageSlot: Fr, _publicKey: PublicKey, _preimage: Fr[]): void {
emitEncryptedLog(_contractAddress: AztecAddress, _storageSlot: Fr, _publicKey: PublicKey, _log: Fr[]): void {
throw new Error('Not available.');
}

Expand Down
45 changes: 19 additions & 26 deletions yarn-project/acir-simulator/src/client/client_execution_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { FunctionAbi, FunctionArtifact, countArgumentsSize } from '@aztec/founda
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr, Point } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { AuthWitness, FunctionL2Logs, NotePreimage, NoteSpendingInfo, UnencryptedL2Log } from '@aztec/types';
import { AuthWitness, FunctionL2Logs, L1NotePayload, Note, UnencryptedL2Log } from '@aztec/types';

import {
NoteData,
Expand All @@ -28,7 +28,7 @@ import { SideEffectCounter } from '../common/index.js';
import { PackedArgsCache } from '../common/packed_args_cache.js';
import { DBOracle } from './db_oracle.js';
import { ExecutionNoteCache } from './execution_note_cache.js';
import { ExecutionResult, NewNoteData } from './execution_result.js';
import { ExecutionResult, NoteAndSlot } from './execution_result.js';
import { pickNotes } from './pick_notes.js';
import { executePrivateFunction } from './private_execution.js';
import { ViewDataOracle } from './view_data_oracle.js';
Expand All @@ -44,7 +44,7 @@ export class ClientExecutionContext extends ViewDataOracle {
* This information is only for references (currently used for tests), and is not used for any sort of constrains.
* Users can also use this to get a clearer idea of what's happened during a simulation.
*/
private newNotes: NewNoteData[] = [];
private newNotes: NoteAndSlot[] = [];
/**
* Notes from previous transactions that are returned to the oracle call `getNotes` during this execution.
* The mapping maps from the unique siloed note hash to the index for notes created in private executions.
Expand Down Expand Up @@ -135,7 +135,7 @@ export class ClientExecutionContext extends ViewDataOracle {
* Get the data for the newly created notes.
* @param innerNoteHashes - Inner note hashes for the notes.
*/
public getNewNotes(): NewNoteData[] {
public getNewNotes(): NoteAndSlot[] {
return this.newNotes;
}

Expand Down Expand Up @@ -176,16 +176,13 @@ export class ClientExecutionContext extends ViewDataOracle {
}

/**
* Gets some notes for a contract address and storage slot.
* Returns a flattened array containing real-note-count and note preimages.
* Gets some notes for a storage slot.
*
* @remarks
*
* Check for pending notes with matching address/slot.
* Check for pending notes with matching slot.
* Real notes coming from DB will have a leafIndex which
* represents their index in the note hash tree.
*
* @param contractAddress - The contract address.
* @param storageSlot - The storage slot.
* @param numSelects - The number of valid selects in selectBy and selectValues.
* @param selectBy - An array of indices of the fields to selects.
Expand All @@ -194,12 +191,7 @@ export class ClientExecutionContext extends ViewDataOracle {
* @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.
* @param offset - The starting index for pagination.
* @param returnSize - The return size.
* @returns Flattened array of ACVMFields (format expected by Noir/ACVM) containing:
* count - number of real (non-padding) notes retrieved,
* contractAddress - the contract address,
* preimages - the real note preimages retrieved, and
* paddedZeros - zeros to ensure an array with length returnSize expected by Noir circuit
* @returns Array of note data.
*/
public async getNotes(
storageSlot: Fr,
Expand Down Expand Up @@ -227,7 +219,7 @@ export class ClientExecutionContext extends ViewDataOracle {

this.log(
`Returning ${notes.length} notes for ${this.contractAddress} at ${storageSlot}: ${notes
.map(n => `${n.nonce.toString()}:[${n.preimage.map(i => i.toString()).join(',')}]`)
.map(n => `${n.nonce.toString()}:[${n.note.items.map(i => i.toString()).join(',')}]`)
.join(', ')}`,
);

Expand All @@ -251,22 +243,23 @@ export class ClientExecutionContext extends ViewDataOracle {
* It can be used in subsequent calls (or transactions when chaining txs is possible).
* @param contractAddress - The contract address.
* @param storageSlot - The storage slot.
* @param preimage - The preimage of the new note.
* @param noteItems - The items to be included in a Note.
* @param innerNoteHash - The inner note hash of the new note.
* @returns
*/
public notifyCreatedNote(storageSlot: Fr, preimage: Fr[], innerNoteHash: Fr) {
public notifyCreatedNote(storageSlot: Fr, noteItems: Fr[], innerNoteHash: Fr) {
const note = new Note(noteItems);
this.noteCache.addNewNote({
contractAddress: this.contractAddress,
storageSlot,
nonce: Fr.ZERO, // Nonce cannot be known during private execution.
preimage,
note,
siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note.
innerNoteHash,
});
this.newNotes.push({
storageSlot,
preimage,
note,
});
}

Expand All @@ -285,13 +278,13 @@ export class ClientExecutionContext extends ViewDataOracle {
* @param contractAddress - The contract address of the note.
* @param storageSlot - The storage slot the note is at.
* @param publicKey - The public key of the account that can decrypt the log.
* @param preimage - The preimage of the note.
* @param log - The log contents.
*/
public emitEncryptedLog(contractAddress: AztecAddress, storageSlot: Fr, publicKey: Point, preimage: Fr[]) {
const notePreimage = new NotePreimage(preimage);
const noteSpendingInfo = new NoteSpendingInfo(notePreimage, contractAddress, storageSlot);
const encryptedNotePreimage = noteSpendingInfo.toEncryptedBuffer(publicKey, this.curve);
this.encryptedLogs.push(encryptedNotePreimage);
public emitEncryptedLog(contractAddress: AztecAddress, storageSlot: Fr, publicKey: Point, log: Fr[]) {
const note = new Note(log);
const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot);
const encryptedNote = l1NotePayload.toEncryptedBuffer(publicKey, this.curve);
this.encryptedLogs.push(encryptedNote);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/acir-simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface DBOracle extends CommitmentsDB {
/**
* Retrieves a set of notes stored in the database for a given contract address and storage slot.
* The query result is paginated using 'limit' and 'offset' values.
* Returns an object containing an array of note data, including preimage, nonce, and index for each note.
* Returns an object containing an array of note data.
*
* @param contractAddress - The AztecAddress instance representing the contract address.
* @param storageSlot - The Fr instance representing the storage slot of the notes.
Expand Down
10 changes: 5 additions & 5 deletions yarn-project/acir-simulator/src/client/execution_result.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { PrivateCallStackItem, PublicCallRequest, ReadRequestMembershipWitness } from '@aztec/circuits.js';
import { DecodedReturn } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { FunctionL2Logs } from '@aztec/types';
import { FunctionL2Logs, Note } from '@aztec/types';

import { ACVMField } from '../acvm/index.js';

/**
* The contents of a new note.
*/
export interface NewNoteData {
/** The preimage of the note. */
preimage: Fr[];
export interface NoteAndSlot {
/** The note. */
note: Note;
/** The storage slot of the note. */
storageSlot: Fr;
}
Expand All @@ -33,7 +33,7 @@ export interface ExecutionResult {
readRequestPartialWitnesses: ReadRequestMembershipWitness[];
// Needed when we enable chained txs. The new notes can be cached and used in a later transaction.
/** The notes created in the executed function. */
newNotes: NewNoteData[];
newNotes: NoteAndSlot[];
/** The decoded return values of the executed function. */
returnValues: DecodedReturn;
/** The nested executions. */
Expand Down
13 changes: 7 additions & 6 deletions yarn-project/acir-simulator/src/client/pick_notes.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Fr } from '@aztec/foundation/fields';
import { Note } from '@aztec/types';

import { SortOrder, pickNotes } from './pick_notes.js';

describe('getNotes', () => {
const expectNotesFields = (notes: { preimage: Fr[] }[], ...expected: [number, bigint[]][]) => {
const expectNotesFields = (notes: { note: Note }[], ...expected: [number, bigint[]][]) => {
expect(notes.length).toBe(expected[0][1].length);
expected.forEach(([fieldIndex, fields]) => {
for (let i = 0; i < notes.length; ++i) {
expect(notes[i].preimage[fieldIndex].value).toBe(fields[i]);
expect(notes[i].note.items[fieldIndex].value).toBe(fields[i]);
}
});
};

const expectNotes = (notes: { preimage: Fr[] }[], expected: bigint[][]) => {
const expectNotes = (notes: { note: Note }[], expected: bigint[][]) => {
expect(notes.length).toBe(expected.length);
notes.forEach((note, i) => {
expect(note.preimage.map(p => p.value)).toEqual(expected[i]);
expect(note.note.items.map(p => p.value)).toEqual(expected[i]);
});
};

const createNote = (preimage: bigint[]) => ({
preimage: preimage.map(f => new Fr(f)),
const createNote = (items: bigint[]) => ({
note: new Note(items.map(f => new Fr(f))),
});

it('should get sorted notes', () => {
Expand Down
21 changes: 11 additions & 10 deletions yarn-project/acir-simulator/src/client/pick_notes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Fr } from '@aztec/foundation/fields';
import { Note } from '@aztec/types';

/**
* Configuration for selecting values.
Expand Down Expand Up @@ -64,17 +65,17 @@ interface GetOptions {
}

/**
* Basic data needed from a note to perform sort.
* Data needed from to perform sort.
*/
interface BasicNoteData {
interface ContainsNote {
/**
* Preimage of a note.
* The note.
*/
preimage: Fr[];
note: Note;
}

const selectNotes = <T extends BasicNoteData>(notes: T[], selects: Select[]): T[] =>
notes.filter(note => selects.every(({ index, value }) => note.preimage[index]?.equals(value)));
const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]): T[] =>
noteDatas.filter(noteData => selects.every(({ index, value }) => noteData.note.items[index]?.equals(value)));

const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => {
if (sorts[level] === undefined) return 0;
Expand All @@ -93,11 +94,11 @@ const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => {
/**
* Pick from a note array a number of notes that meet the criteria.
*/
export function pickNotes<T extends BasicNoteData>(
notes: T[],
export function pickNotes<T extends ContainsNote>(
noteDatas: T[],
{ selects = [], sorts = [], limit = 0, offset = 0 }: GetOptions,
): T[] {
return selectNotes(notes, selects)
.sort((a, b) => sortNotes(a.preimage, b.preimage, sorts))
return selectNotes(noteDatas, selects)
.sort((a, b) => sortNotes(a.note.items, b.note.items, sorts))
.slice(offset, limit ? offset + limit : undefined);
}
Loading

0 comments on commit 16abb5a

Please sign in to comment.