Skip to content
Merged
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
4 changes: 2 additions & 2 deletions yarn-project/circuit-types/src/logs/function_l2_logs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sha256 } from '@aztec/foundation/crypto';
import { Point } from '@aztec/foundation/fields';
import { Fr, Point } from '@aztec/foundation/fields';
import { BufferReader, prefixBufferWithLength } from '@aztec/foundation/serialize';

import { randomBytes } from 'crypto';
Expand Down Expand Up @@ -77,7 +77,7 @@ export class FunctionL2Logs {
if (logType === LogType.ENCRYPTED) {
const randomEphPubKey = Point.random();
const randomLogContent = randomBytes(144 - Point.SIZE_IN_BYTES);
logs.push(Buffer.concat([randomLogContent, randomEphPubKey.toBuffer()]));
logs.push(Buffer.concat([Fr.random().toBuffer(), randomLogContent, randomEphPubKey.toBuffer()]));
} else {
logs.push(UnencryptedL2Log.random().toBuffer());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './encrypt_buffer.js';
export * from './note.js';
export * from './l1_note_payload.js';
export * from './tagged_note.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { GrumpkinScalar, Point } from '@aztec/foundation/fields';

import { L1NotePayload } from './l1_note_payload.js';
import { TaggedNote } from './tagged_note.js';

describe('L1 Note Payload', () => {
let grumpkin: Grumpkin;

beforeAll(() => {
grumpkin = new Grumpkin();
});

it('convert to and from buffer', () => {
const payload = L1NotePayload.random();
const taggedNote = new TaggedNote(payload);
const buf = taggedNote.toBuffer();
expect(TaggedNote.fromBuffer(buf).notePayload).toEqual(taggedNote.notePayload);
});

it('convert to and from encrypted buffer', () => {
const payload = L1NotePayload.random();
const taggedNote = new TaggedNote(payload);
const ownerPrivKey = GrumpkinScalar.random();
const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey);
const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey, grumpkin);
const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, ownerPrivKey, grumpkin);
expect(decrypted).not.toBeUndefined();
expect(decrypted?.notePayload).toEqual(payload);
});

it('return undefined if unable to decrypt the encrypted buffer', () => {
const payload = L1NotePayload.random();
const taggedNote = new TaggedNote(payload);
const ownerPubKey = Point.random();
const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey, grumpkin);
const randomPrivKey = GrumpkinScalar.random();
const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, randomPrivKey, grumpkin);
expect(decrypted).toBeUndefined();
});
});
71 changes: 71 additions & 0 deletions yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { GrumpkinPrivateKey, PublicKey } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { Fr } from '@aztec/foundation/fields';
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';

import { L1NotePayload } from './l1_note_payload.js';

// placeholder value until tagging is implemented
const PLACEHOLDER_TAG = new Fr(33);

/**
* Encrypted note payload with a tag used for retrieval by clients.
*/
export class TaggedNote {
constructor(public notePayload: L1NotePayload, public tag = PLACEHOLDER_TAG) {}

/**
* Deserializes the TaggedNote object from a Buffer.
* @param buffer - Buffer or BufferReader object to deserialize.
* @returns An instance of TaggedNote.
*/
static fromBuffer(buffer: Buffer | BufferReader): TaggedNote {
const reader = BufferReader.asReader(buffer);
const tag = Fr.fromBuffer(reader);
const payload = L1NotePayload.fromBuffer(reader);
return new TaggedNote(payload, tag);
}

/**
* Serializes the TaggedNote object into a Buffer.
* @returns Buffer representation of the TaggedNote object (unencrypted).
*/
public toBuffer(): Buffer {
return serializeToBuffer(this.tag, this.notePayload);
}

/**
* Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key, then attach the tag.
* @param ownerPubKey - Public key of the owner of the TaggedNote object.
* @param curve - The curve instance to use.
* @returns The encrypted TaggedNote object.
*/
public toEncryptedBuffer(ownerPubKey: PublicKey, curve: Grumpkin): Buffer {
const encryptedL1NotePayload = this.notePayload.toEncryptedBuffer(ownerPubKey, curve);
return serializeToBuffer(this.tag, encryptedL1NotePayload);
}

/**
* Decrypts the L1NotePayload object using the owner's private key.
* @param data - Encrypted TaggedNote object.
* @param ownerPrivKey - Private key of the owner of the TaggedNote object.
* @param curve - The curve instance to use.
* @returns Instance of TaggedNote if the decryption was successful, undefined otherwise.
*/
static fromEncryptedBuffer(data: Buffer, ownerPrivKey: GrumpkinPrivateKey, curve: Grumpkin): TaggedNote | undefined {
const reader = BufferReader.asReader(data);
const tag = Fr.fromBuffer(reader);

const encryptedL1NotePayload = reader.readToEnd();

const payload = L1NotePayload.fromEncryptedBuffer(encryptedL1NotePayload, ownerPrivKey, curve);
if (!payload) {
return;
}
return new TaggedNote(payload, tag);
}

static random(): TaggedNote {
return new TaggedNote(L1NotePayload.random());
}
}
20 changes: 11 additions & 9 deletions yarn-project/pxe/src/note_processor/note_processor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
L2BlockContext,
L2BlockL2Logs,
Note,
TaggedNote,
TxL2Logs,
} from '@aztec/circuit-types';
import { Fr, MAX_NEW_NOTE_HASHES_PER_TX } from '@aztec/circuits.js';
Expand Down Expand Up @@ -47,8 +48,8 @@ describe('Note Processor', () => {
const computeMockNoteHash = (note: Note) => pedersenHash(note.items.map(i => i.toBuffer()));

// ownedData: [tx1, tx2, ...], the numbers in each tx represents the indices of the note hashes the account owns.
const createEncryptedLogsAndOwnedL1NotePayloads = (ownedData: number[][], ownedNotes: L1NotePayload[]) => {
const newNotes: L1NotePayload[] = [];
const createEncryptedLogsAndOwnedL1NotePayloads = (ownedData: number[][], ownedNotes: TaggedNote[]) => {
const newNotes: TaggedNote[] = [];
const ownedL1NotePayloads: L1NotePayload[] = [];
const txLogs: TxL2Logs[] = [];
let usedOwnedNote = 0;
Expand All @@ -62,12 +63,13 @@ describe('Note Processor', () => {
for (let noteIndex = 0; noteIndex < MAX_NEW_NOTE_HASHES_PER_TX; ++noteIndex) {
const isOwner = ownedDataIndices.includes(noteIndex);
const publicKey = isOwner ? owner.getPublicKey() : Point.random();
const note = (isOwner && ownedNotes[usedOwnedNote]) || L1NotePayload.random();
const note = (isOwner && ownedNotes[usedOwnedNote]) || TaggedNote.random();
usedOwnedNote += note === ownedNotes[usedOwnedNote] ? 1 : 0;
newNotes.push(note);
if (isOwner) {
ownedL1NotePayloads.push(note);
ownedL1NotePayloads.push(note.notePayload);
}
// const encryptedNote =
const log = note.toEncryptedBuffer(publicKey, grumpkin);
// 1 tx containing 1 function invocation containing 1 log
logs.push(new FunctionL2Logs([log]));
Expand All @@ -80,10 +82,10 @@ describe('Note Processor', () => {
};

const mockData = (
ownedData: number[][],
ownedData: number[][], // = [[2]]
prependedBlocks = 0,
appendedBlocks = 0,
ownedNotes: L1NotePayload[] = [],
ownedNotes: TaggedNote[] = [], // L1NotePayload[] = [],
) => {
if (ownedData.length > TXS_PER_BLOCK) {
throw new Error(`Tx size should be less than ${TXS_PER_BLOCK}.`);
Expand All @@ -108,7 +110,7 @@ describe('Note Processor', () => {
ownedL1NotePayloads.push(...payloads);
for (let i = 0; i < TXS_PER_BLOCK; i++) {
block.body.txEffects[i].newNoteHashes = newNotes
.map(n => computeMockNoteHash(n.note))
.map(n => computeMockNoteHash(n.notePayload.note))
.slice(i * MAX_NEW_NOTE_HASHES_PER_TX, (i + 1) * MAX_NEW_NOTE_HASHES_PER_TX) as Tuple<
Fr,
typeof MAX_NEW_NOTE_HASHES_PER_TX
Expand Down Expand Up @@ -208,8 +210,8 @@ describe('Note Processor', () => {
});

it('should be able to recover two note payloads with containing the same note', async () => {
const note = L1NotePayload.random();
const note2 = L1NotePayload.random();
const note = TaggedNote.random(); // L1NotePayload.random();
const note2 = TaggedNote.random(); // L1NotePayload.random();
// All note payloads except one have the same contract address, storage slot, and the actual note.
const notes = [note, note, note, note2, note];
const { blockContexts, encryptedLogsArr, ownedL1NotePayloads } = mockData([[0, 2], [], [0, 1, 3]], 0, 0, notes);
Expand Down
8 changes: 5 additions & 3 deletions yarn-project/pxe/src/note_processor/note_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
L1NotePayload,
L2BlockContext,
L2BlockL2Logs,
TaggedNote,
} from '@aztec/circuit-types';
import { NoteProcessorStats } from '@aztec/circuit-types/stats';
import { MAX_NEW_NOTE_HASHES_PER_TX, PublicKey } from '@aztec/circuits.js';
Expand Down Expand Up @@ -130,10 +131,11 @@ export class NoteProcessor {
const txFunctionLogs = txLogs[indexOfTxInABlock].functionLogs;
const excludedIndices: Set<number> = new Set();
for (const functionLogs of txFunctionLogs) {
for (const logs of functionLogs.logs) {
for (const log of functionLogs.logs) {
this.stats.seen++;
const payload = L1NotePayload.fromEncryptedBuffer(logs, privateKey, curve);
if (payload) {
const taggedNote = TaggedNote.fromEncryptedBuffer(log, privateKey, curve);
if (taggedNote?.notePayload) {
const { notePayload: payload } = taggedNote;
// We have successfully decrypted the data.
const txHash = blockContext.getTxHash(indexOfTxInABlock);
try {
Expand Down
10 changes: 7 additions & 3 deletions yarn-project/pxe/src/synchronizer/synchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,19 @@ export class Synchronizer {

// filter out note processors that are already caught up
// and sort them by the block number they are lagging behind in ascending order
this.noteProcessorsToCatchUp = this.noteProcessorsToCatchUp.filter(noteProcessor => {
const noteProcessorsToCatchUp: NoteProcessor[] = [];

this.noteProcessorsToCatchUp.forEach(noteProcessor => {
if (noteProcessor.status.syncedToBlock >= toBlockNumber) {
// Note processor is ahead of main sync, nothing to do
this.noteProcessors.push(noteProcessor);
return false;
} else {
noteProcessorsToCatchUp.push(noteProcessor);
}
return true;
});

this.noteProcessorsToCatchUp = noteProcessorsToCatchUp;

if (!this.noteProcessorsToCatchUp.length) {
// No note processors to catch up, nothing to do here,
// but we return true to continue with the normal flow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
L1NotePayload,
Note,
NoteStatus,
TaggedNote,
UnencryptedL2Log,
} from '@aztec/circuit-types';
import {
Expand Down Expand Up @@ -293,7 +294,8 @@ export class ClientExecutionContext extends ViewDataOracle {
public emitEncryptedLog(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, publicKey: Point, log: Fr[]) {
const note = new Note(log);
const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId);
const encryptedNote = l1NotePayload.toEncryptedBuffer(publicKey, this.curve);
const taggedNote = new TaggedNote(l1NotePayload);
const encryptedNote = taggedNote.toEncryptedBuffer(publicKey, this.curve);
this.encryptedLogs.push(encryptedNote);
}

Expand Down