Skip to content

Commit

Permalink
fix: unexpose get note nonces on pxe (#7889)
Browse files Browse the repository at this point in the history
This PR unexposes `getNoteNonces` on the PXE by making notes returned
from `getIncomingNotes` and `getOutgoingNotes` include a nonce.

This is done by extending `ExtendedNote` to create
`ExtendedNoteWithNonce`, a new type that adds a required property,
nonce, to the extended note.

I chose this approach instead of another e.g. adding an optional
property on `ExtendedNote` because these data structures are
fundamentally different—used in different places, for different reasons.
It didn't make sense to me to try and shoehorn both uses to one struct,
for the gain of slight de-duplication.

Because `ExtendedNote` is only used for notes added to or retrieved from
the PXE, there were not that many required changes.

Note: I'm definitely not a fan with the naming but cannot currently
think of anything better. Open to suggestions from the reviewer.

---------

Co-authored-by: Jan Beneš <janbenes1234@gmail.com>
  • Loading branch information
sklppy88 and benesjan authored Aug 13, 2024
1 parent b1c7374 commit 163c3a6
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 49 deletions.
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export {
EncryptedLogOutgoingBody,
EventType,
ExtendedNote,
UniqueNote,
FunctionCall,
L1Actor,
L1ToL2Message,
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/src/rpc_clients/pxe_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
TxHash,
TxReceipt,
UnencryptedL2BlockL2Logs,
UniqueNote,
} from '@aztec/circuit-types';
import {
AztecAddress,
Expand Down Expand Up @@ -45,6 +46,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
FunctionSelector,
EthAddress,
ExtendedNote,
UniqueNote,
ExtendedUnencryptedL2Log,
Fr,
GrumpkinScalar,
Expand Down
9 changes: 3 additions & 6 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
type TxExecutionRequest,
type TxHash,
type TxReceipt,
type UniqueNote,
} from '@aztec/circuit-types';
import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
import {
Expand Down Expand Up @@ -120,16 +121,12 @@ export abstract class BaseWallet implements Wallet {
getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
return this.pxe.getTxReceipt(txHash);
}
getIncomingNotes(filter: IncomingNotesFilter): Promise<ExtendedNote[]> {
getIncomingNotes(filter: IncomingNotesFilter): Promise<UniqueNote[]> {
return this.pxe.getIncomingNotes(filter);
}
getOutgoingNotes(filter: OutgoingNotesFilter): Promise<ExtendedNote[]> {
getOutgoingNotes(filter: OutgoingNotesFilter): Promise<UniqueNote[]> {
return this.pxe.getOutgoingNotes(filter);
}
// TODO(#4956): Un-expose this
getNoteNonces(note: ExtendedNote): Promise<Fr[]> {
return this.pxe.getNoteNonces(note);
}
getPublicStorageAt(contract: AztecAddress, storageSlot: Fr): Promise<any> {
return this.pxe.getPublicStorageAt(contract, storageSlot);
}
Expand Down
15 changes: 3 additions & 12 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { type AuthWitness } from '../auth_witness.js';
import { type L2Block } from '../l2_block.js';
import { type GetUnencryptedLogsResponse, type L1EventPayload, type LogFilter } from '../logs/index.js';
import { type IncomingNotesFilter } from '../notes/incoming_notes_filter.js';
import { type ExtendedNote, type OutgoingNotesFilter } from '../notes/index.js';
import { type ExtendedNote, type OutgoingNotesFilter, type UniqueNote } from '../notes/index.js';
import { type NoteProcessorStats } from '../stats/stats.js';
import { type SimulatedTx, type Tx, type TxHash, type TxReceipt } from '../tx/index.js';
import { type TxEffect } from '../tx_effect.js';
Expand Down Expand Up @@ -235,23 +235,14 @@ export interface PXE {
* @param filter - The filter to apply to the notes.
* @returns The requested notes.
*/
getIncomingNotes(filter: IncomingNotesFilter): Promise<ExtendedNote[]>;
getIncomingNotes(filter: IncomingNotesFilter): Promise<UniqueNote[]>;

/**
* Gets outgoing notes of accounts registered in this PXE based on the provided filter.
* @param filter - The filter to apply to the notes.
* @returns The requested notes.
*/
getOutgoingNotes(filter: OutgoingNotesFilter): Promise<ExtendedNote[]>;

/**
* Finds the nonce(s) for a given note.
* @param note - The note to find the nonces for.
* @returns The nonces of the note.
* @remarks More than a single nonce may be returned since there might be more than one nonce for a given note.
* TODO(#4956): Un-expose this
*/
getNoteNonces(note: ExtendedNote): Promise<Fr[]>;
getOutgoingNotes(filter: OutgoingNotesFilter): Promise<UniqueNote[]>;

/**
* Adds a note to the database.
Expand Down
14 changes: 13 additions & 1 deletion yarn-project/circuit-types/src/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { Fr } from '@aztec/foundation/fields';
import { type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts';

import { EncryptedNoteTxL2Logs, EncryptedTxL2Logs, Note, UnencryptedTxL2Logs } from './logs/index.js';
import { ExtendedNote } from './notes/index.js';
import { ExtendedNote, UniqueNote } from './notes/index.js';
import { PublicExecutionRequest } from './public_execution_request.js';
import { NestedProcessReturnValues, PublicSimulationOutput, SimulatedTx, Tx, TxHash } from './tx/index.js';

Expand Down Expand Up @@ -255,3 +255,15 @@ export const randomExtendedNote = ({
}: Partial<ExtendedNote> = {}) => {
return new ExtendedNote(note, owner, contractAddress, storageSlot, noteTypeId, txHash);
};

export const randomUniqueNote = ({
note = Note.random(),
owner = AztecAddress.random(),
contractAddress = AztecAddress.random(),
txHash = randomTxHash(),
storageSlot = Fr.random(),
noteTypeId = NoteSelector.random(),
nonce = Fr.random(),
}: Partial<UniqueNote> = {}) => {
return new UniqueNote(note, owner, contractAddress, storageSlot, noteTypeId, txHash, nonce);
};
12 changes: 10 additions & 2 deletions yarn-project/circuit-types/src/notes/extended_note.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { randomExtendedNote } from '../mocks.js';
import { ExtendedNote } from './extended_note.js';
import { randomExtendedNote, randomUniqueNote } from '../mocks.js';
import { ExtendedNote, UniqueNote } from './extended_note.js';

describe('Extended Note', () => {
it('convert to and from buffer', () => {
Expand All @@ -8,3 +8,11 @@ describe('Extended Note', () => {
expect(ExtendedNote.fromBuffer(buf)).toEqual(extendedNote);
});
});

describe('Unique Note', () => {
it('convert to and from buffer', () => {
const uniqueNote = randomUniqueNote();
const buf = uniqueNote.toBuffer();
expect(UniqueNote.fromBuffer(buf)).toEqual(uniqueNote);
});
});
52 changes: 52 additions & 0 deletions yarn-project/circuit-types/src/notes/extended_note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,55 @@ export class ExtendedNote {
return ExtendedNote.fromBuffer(Buffer.from(hex, 'hex'));
}
}

export class UniqueNote extends ExtendedNote {
constructor(
/** The note as emitted from the Noir contract. */
note: Note,
/** The owner whose public key was used to encrypt the note. */
owner: AztecAddress,
/** The contract address this note is created in. */
contractAddress: AztecAddress,
/** The specific storage location of the note on the contract. */
storageSlot: Fr,
/** The type identifier of the note on the contract. */
noteTypeId: NoteSelector,
/** The hash of the tx the note was created in. */
txHash: TxHash,
/** The nonce of the note. */
public nonce: Fr,
) {
super(note, owner, contractAddress, storageSlot, noteTypeId, txHash);
}

override toBuffer(): Buffer {
return Buffer.concat([
this.note.toBuffer(),
this.owner.toBuffer(),
this.contractAddress.toBuffer(),
this.storageSlot.toBuffer(),
this.noteTypeId.toBuffer(),
this.txHash.buffer,
this.nonce.toBuffer(),
]);
}

static override fromBuffer(buffer: Buffer | BufferReader) {
const reader = BufferReader.asReader(buffer);

const note = Note.fromBuffer(reader);
const owner = AztecAddress.fromBuffer(reader);
const contractAddress = AztecAddress.fromBuffer(reader);
const storageSlot = Fr.fromBuffer(reader);
const noteTypeId = reader.readObject(NoteSelector);
const txHash = new TxHash(reader.readBytes(TxHash.SIZE));
const nonce = Fr.fromBuffer(reader);

return new this(note, owner, contractAddress, storageSlot, noteTypeId, txHash, nonce);
}

static override fromString(str: string) {
const hex = str.replace(/^0x/, '');
return UniqueNote.fromBuffer(Buffer.from(hex, 'hex'));
}
}
6 changes: 3 additions & 3 deletions yarn-project/circuit-types/src/tx/tx_receipt.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RevertCode } from '@aztec/circuits.js';
import { type Fr } from '@aztec/foundation/fields';

import { type ExtendedNote } from '../notes/extended_note.js';
import { type UniqueNote } from '../notes/extended_note.js';
import { type PublicDataWrite } from '../public_data_write.js';
import { TxHash } from './tx_hash.js';

Expand Down Expand Up @@ -126,11 +126,11 @@ interface DebugInfo {
* in the PXE which was used to submit the tx. You will not get notes of accounts which are not registered in
* the PXE here even though they were created in this tx.
*/
visibleIncomingNotes: ExtendedNote[];
visibleIncomingNotes: UniqueNote[];
/**
* Notes created in this tx which were successfully decoded with the outgoing keys of accounts which are registered
* in the PXE which was used to submit the tx. You will not get notes of accounts which are not registered in
* the PXE here even though they were created in this tx.
*/
visibleOutgoingNotes: ExtendedNote[];
visibleOutgoingNotes: UniqueNote[];
}
28 changes: 12 additions & 16 deletions yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
PackedValues,
TxExecutionRequest,
type TxHash,
type UniqueNote,
computeSecretHash,
deriveKeys,
} from '@aztec/aztec.js';
Expand Down Expand Up @@ -180,27 +181,22 @@ describe('e2e_crowdfunding_and_claim', () => {
]);
};

// Processes extended note such that it can be passed to a claim function of Claim contract
const processExtendedNote = async (extendedNote: ExtendedNote) => {
// TODO(#4956): Make fetching the nonce manually unnecessary
// To be able to perform the inclusion proof we need to fetch the nonce of the value note
const noteNonces = await pxe.getNoteNonces(extendedNote);
expect(noteNonces?.length).toEqual(1);

// Processes unique note such that it can be passed to a claim function of Claim contract
const processUniqueNote = (uniqueNote: UniqueNote) => {
return {
header: {
// eslint-disable-next-line camelcase
contract_address: extendedNote.contractAddress,
contract_address: uniqueNote.contractAddress,
// eslint-disable-next-line camelcase
storage_slot: extendedNote.storageSlot,
storage_slot: uniqueNote.storageSlot,
// eslint-disable-next-line camelcase
note_hash_counter: 0, // set as 0 as note is not transient
nonce: noteNonces[0],
nonce: uniqueNote.nonce,
},
value: extendedNote.note.items[0],
value: uniqueNote.note.items[0],
// eslint-disable-next-line camelcase
npk_m_hash: extendedNote.note.items[1],
randomness: extendedNote.note.items[2],
npk_m_hash: uniqueNote.note.items[1],
randomness: uniqueNote.note.items[2],
};
};

Expand Down Expand Up @@ -233,7 +229,7 @@ describe('e2e_crowdfunding_and_claim', () => {
expect(notes!.length).toEqual(1);

// Set the value note in a format which can be passed to claim function
valueNote = await processExtendedNote(notes![0]);
valueNote = processUniqueNote(notes![0]);
}

// 3) We claim the reward token via the Claim contract
Expand Down Expand Up @@ -304,7 +300,7 @@ describe('e2e_crowdfunding_and_claim', () => {
expect(notes!.length).toEqual(1);

// Set the value note in a format which can be passed to claim function
const anotherDonationNote = await processExtendedNote(notes![0]);
const anotherDonationNote = processUniqueNote(notes![0]);

// We create an unrelated pxe and wallet without access to the nsk_app that correlates to the npk_m specified in the proof note.
let unrelatedWallet: AccountWallet;
Expand Down Expand Up @@ -356,7 +352,7 @@ describe('e2e_crowdfunding_and_claim', () => {
const receipt = await inclusionsProofsContract.methods.create_note(owner, 5n).send().wait({ debug: true });
const { visibleIncomingNotes } = receipt.debugInfo!;
expect(visibleIncomingNotes.length).toEqual(1);
note = await processExtendedNote(visibleIncomingNotes![0]);
note = processUniqueNote(visibleIncomingNotes![0]);
}

// 3) Test the note was included
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/pxe/src/pxe_http/pxe_http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
TxHash,
TxReceipt,
UnencryptedL2BlockL2Logs,
UniqueNote,
} from '@aztec/circuit-types';
import { FunctionSelector } from '@aztec/circuits.js';
import { NoteSelector } from '@aztec/foundation/abi';
Expand Down Expand Up @@ -48,6 +49,7 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer {
GrumpkinScalar,
Note,
ExtendedNote,
UniqueNote,
AuthWitness,
L2Block,
TxEffect,
Expand Down
34 changes: 25 additions & 9 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
EncryptedTxL2Logs,
type EventMetadata,
EventType,
ExtendedNote,
type ExtendedNote,
type FunctionCall,
type GetUnencryptedLogsResponse,
type IncomingNotesFilter,
Expand All @@ -26,6 +26,7 @@ import {
type TxHash,
type TxReceipt,
UnencryptedTxL2Logs,
UniqueNote,
isNoirCallStackUnresolved,
} from '@aztec/circuit-types';
import {
Expand Down Expand Up @@ -296,7 +297,7 @@ export class PXEService implements PXE {
return await this.node.getPublicStorageAt(contract, slot, 'latest');
}

public async getIncomingNotes(filter: IncomingNotesFilter): Promise<ExtendedNote[]> {
public async getIncomingNotes(filter: IncomingNotesFilter): Promise<UniqueNote[]> {
const noteDaos = await this.db.getIncomingNotes(filter);

// TODO(#6531): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
Expand All @@ -312,12 +313,20 @@ export class PXEService implements PXE {
}
owner = completeAddresses.address;
}
return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.noteTypeId, dao.txHash);
return new UniqueNote(
dao.note,
owner,
dao.contractAddress,
dao.storageSlot,
dao.noteTypeId,
dao.txHash,
dao.nonce,
);
});
return Promise.all(extendedNotes);
}

public async getOutgoingNotes(filter: OutgoingNotesFilter): Promise<ExtendedNote[]> {
public async getOutgoingNotes(filter: OutgoingNotesFilter): Promise<UniqueNote[]> {
const noteDaos = await this.db.getOutgoingNotes(filter);

// TODO(#6532): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
Expand All @@ -333,7 +342,15 @@ export class PXEService implements PXE {
}
owner = completeAddresses.address;
}
return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.noteTypeId, dao.txHash);
return new UniqueNote(
dao.note,
owner,
dao.contractAddress,
dao.storageSlot,
dao.noteTypeId,
dao.txHash,
dao.nonce,
);
});
return Promise.all(extendedNotes);
}
Expand All @@ -344,7 +361,7 @@ export class PXEService implements PXE {
throw new Error(`Unknown account: ${note.owner.toString()}`);
}

const nonces = await this.getNoteNonces(note);
const nonces = await this.#getNoteNonces(note);
if (nonces.length === 0) {
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
}
Expand Down Expand Up @@ -394,7 +411,7 @@ export class PXEService implements PXE {
throw new Error(`Unknown account: ${note.owner.toString()}`);
}

const nonces = await this.getNoteNonces(note);
const nonces = await this.#getNoteNonces(note);
if (nonces.length === 0) {
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
}
Expand Down Expand Up @@ -440,9 +457,8 @@ export class PXEService implements PXE {
* @param note - The note to find the nonces for.
* @returns The nonces of the note.
* @remarks More than a single nonce may be returned since there might be more than one nonce for a given note.
* TODO(#4956): Un-expose this
*/
public async getNoteNonces(note: ExtendedNote): Promise<Fr[]> {
async #getNoteNonces(note: ExtendedNote): Promise<Fr[]> {
const tx = await this.node.getTxEffect(note.txHash);
if (!tx) {
throw new Error(`Unknown tx: ${note.txHash}`);
Expand Down

0 comments on commit 163c3a6

Please sign in to comment.