Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: EventMetadata class implementation for serialisation #9574

Merged
merged 7 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 7 additions & 6 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,22 @@ export {
Comparator,
CompleteAddress,
EncryptedL2BlockL2Logs,
EncryptedLogPayload,
EncryptedNoteL2BlockL2Logs,
EpochProofQuote,
EpochProofQuotePayload,
EventMetadata,
EventType,
ExtendedNote,
UniqueNote,
FunctionCall,
L1Actor,
L1EventPayload,
L1NotePayload,
L1ToL2Message,
L2Actor,
L2Block,
L2BlockL2Logs,
EncryptedNoteL2BlockL2Logs,
type LogFilter,
LogId,
LogType,
Expand All @@ -137,11 +143,6 @@ export {
merkleTreeIds,
mockTx,
mockEpochProofQuote,
EncryptedLogPayload,
L1NotePayload,
L1EventPayload,
EpochProofQuote,
EpochProofQuotePayload,
} from '@aztec/circuit-types';

// TODO: These kinds of things have no place on our public api.
Expand Down
5 changes: 4 additions & 1 deletion yarn-project/aztec.js/src/rpc_clients/pxe_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
EncryptedL2Log,
EncryptedL2NoteLog,
EncryptedNoteL2BlockL2Logs,
EventMetadata,
ExtendedNote,
ExtendedUnencryptedL2Log,
L2Block,
Expand Down Expand Up @@ -36,7 +37,7 @@ import {
PrivateCircuitPublicInputs,
PublicKeys,
} from '@aztec/circuits.js';
import { NoteSelector } from '@aztec/foundation/abi';
import { EventSelector, NoteSelector } from '@aztec/foundation/abi';
import { Buffer32 } from '@aztec/foundation/buffer';
import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client';

Expand All @@ -55,6 +56,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
CompleteAddress,
FunctionSelector,
EthAddress,
EventSelector,
ExtendedNote,
UniqueNote,
ExtendedUnencryptedL2Log,
Expand All @@ -75,6 +77,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
EncryptedNoteL2BlockL2Logs,
EncryptedL2NoteLog,
EncryptedL2Log,
EventMetadata,
UnencryptedL2Log,
NoteSelector,
NullifierMembershipWitness,
Expand Down
30 changes: 2 additions & 28 deletions yarn-project/builder/src/contract-interface-gen/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,12 @@ function generateEvents(events: any[] | undefined) {
`;

const fieldNames = event.fields.map((field: any) => `"${field.name}"`);
const eventType = `${eventName}: {decode: (payload: L1EventPayload | UnencryptedL2Log | undefined) => ${eventName} | undefined, eventSelector: EventSelector, fieldNames: string[] }`;
const eventType = `${eventName}: {abiType: AbiType, eventSelector: EventSelector, fieldNames: string[] }`;
// Reusing the decodeFunctionSignature
const eventSignature = decodeFunctionSignature(eventName, event.fields);
const eventSelector = `EventSelector.fromSignature('${eventSignature}')`;
const eventImpl = `${eventName}: {
decode: this.decodeEvent(${eventSelector}, ${JSON.stringify(event, null, 4)}),
abiType: ${JSON.stringify(event, null, 4)},
eventSelector: ${eventSelector},
fieldNames: [${fieldNames}],
}`;
Expand All @@ -277,32 +277,6 @@ function generateEvents(events: any[] | undefined) {
return {
eventDefs: eventsMetadata.map(({ eventDef }) => eventDef).join('\n'),
events: `
// Partial application is chosen is to avoid the duplication of so much codegen.
private static decodeEvent<T>(
eventSelector: EventSelector,
eventType: AbiType,
): (payload: L1EventPayload | UnencryptedL2Log | undefined) => T | undefined {
return (payload: L1EventPayload | UnencryptedL2Log | undefined): T | undefined => {
if (payload === undefined) {
return undefined;
}

if (payload instanceof L1EventPayload) {
if (!eventSelector.equals(payload.eventTypeId)) {
return undefined;
}
return decodeFromAbi([eventType], payload.event.items) as T;
} else {
let items = [];
for (let i = 0; i < payload.data.length; i += 32) {
items.push(new Fr(payload.data.subarray(i, i + 32)));
}

return decodeFromAbi([eventType], items) as T;
}
};
}

public static get events(): { ${eventsMetadata.map(({ eventType }) => eventType).join(', ')} } {
return {
${eventsMetadata.map(({ eventImpl }) => eventImpl).join(',\n')}
Expand Down
18 changes: 2 additions & 16 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@ import {
type Point,
type ProtocolContractAddresses,
} from '@aztec/circuits.js';
import { type ContractArtifact, type EventSelector } from '@aztec/foundation/abi';
import { type ContractArtifact } from '@aztec/foundation/abi';

import { type AuthWitness } from '../auth_witness.js';
import { type L2Block } from '../l2_block.js';
import {
type GetUnencryptedLogsResponse,
type L1EventPayload,
type LogFilter,
type UnencryptedL2Log,
} from '../logs/index.js';
import { type EventMetadata, type GetUnencryptedLogsResponse, type LogFilter } from '../logs/index.js';
import { type IncomingNotesFilter } from '../notes/incoming_notes_filter.js';
import { type ExtendedNote, type OutgoingNotesFilter, type UniqueNote } from '../notes/index.js';
import { type PrivateExecutionResult } from '../private_execution_result.js';
Expand Down Expand Up @@ -425,15 +420,6 @@ export interface PXE {
}
// docs:end:pxe-interface

/**
* The shape of the event generated on the Contract.
*/
export interface EventMetadata<T> {
decode(payload: L1EventPayload | UnencryptedL2Log): T | undefined;
eventSelector: EventSelector;
fieldNames: string[];
}

/**
* This is used in getting events via the filter
*/
Expand Down
63 changes: 63 additions & 0 deletions yarn-project/circuit-types/src/logs/event_metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { L1EventPayload, type UnencryptedL2Log } from '@aztec/circuit-types';
import { type AbiType } from '@aztec/foundation/abi';
import { EventSelector, decodeFromAbi } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';

/**
* Represents metadata for an event decoder, including all information needed to reconstruct it.
*/
export class EventMetadata<T> {
public readonly decode: (payload: L1EventPayload | UnencryptedL2Log) => T | undefined;

constructor(public eventSelector: EventSelector, private readonly eventType: AbiType, public fieldNames: string[]) {
this.decode = EventMetadata.decodeEvent<T>(eventSelector, eventType);
}

public static decodeEvent<T>(
eventSelector: EventSelector,
eventType: AbiType,
): (payload: L1EventPayload | UnencryptedL2Log | undefined) => T | undefined {
return (payload: L1EventPayload | UnencryptedL2Log | undefined): T | undefined => {
if (payload === undefined) {
return undefined;
}

if (payload instanceof L1EventPayload) {
if (!eventSelector.equals(payload.eventTypeId)) {
return undefined;
}
return decodeFromAbi([eventType], payload.event.items) as T;
} else {
const items = [];
for (let i = 0; i < payload.data.length; i += 32) {
items.push(new Fr(payload.data.subarray(i, i + 32)));
}

return decodeFromAbi([eventType], items) as T;
}
};
}

/**
* Serializes the metadata to a JSON-friendly format
*/
public toJSON() {
return {
type: 'event_metadata',
eventSelector: this.eventSelector.toString(),
eventType: this.eventType,
fieldNames: this.fieldNames,
};
}

/**
* Creates an EventMetadata instance from a JSON representation
*/
public static fromJSON(json: any): EventMetadata<any> {
if (json?.type !== 'event_metadata') {
throw new Error('Invalid event metadata format');
}

return new EventMetadata(EventSelector.fromString(json.eventSelector), json.eventType, json.fieldNames);
}
}
1 change: 1 addition & 0 deletions yarn-project/circuit-types/src/logs/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './encrypted_l2_note_log.js';
export * from './encrypted_l2_log.js';
export * from './event_metadata.js';
export * from './get_unencrypted_logs_response.js';
export * from './function_l2_logs.js';
export * from './l2_block_l2_logs.js';
Expand Down
58 changes: 44 additions & 14 deletions yarn-project/end-to-end/src/e2e_event_logs.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type AccountWalletWithSecretKey,
type AztecNode,
EventMetadata,
EventType,
Fr,
L1EventPayload,
Expand Down Expand Up @@ -113,33 +114,50 @@ describe('Logs', () => {
.wait();

// We get all the events we can decrypt with either our incoming or outgoing viewing keys
const collectedEvent0s = await wallets[0].getEvents(

const collectedEvent0s = await wallets[0].getEvents<ExampleEvent0>(
EventType.Encrypted,
TestLogContract.events.ExampleEvent0,
new EventMetadata(
sklppy88 marked this conversation as resolved.
Show resolved Hide resolved
TestLogContract.events.ExampleEvent0.eventSelector,
TestLogContract.events.ExampleEvent0.abiType,
TestLogContract.events.ExampleEvent0.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
);

const collectedEvent0sWithIncoming = await wallets[0].getEvents(
const collectedEvent0sWithIncoming = await wallets[0].getEvents<ExampleEvent0>(
EventType.Encrypted,
TestLogContract.events.ExampleEvent0,
new EventMetadata(
TestLogContract.events.ExampleEvent0.eventSelector,
TestLogContract.events.ExampleEvent0.abiType,
TestLogContract.events.ExampleEvent0.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
// This function can be called specifying the viewing public keys associated with the encrypted event.
[wallets[0].getCompleteAddress().publicKeys.masterIncomingViewingPublicKey],
);

const collectedEvent0sWithOutgoing = await wallets[0].getEvents(
const collectedEvent0sWithOutgoing = await wallets[0].getEvents<ExampleEvent0>(
EventType.Encrypted,
TestLogContract.events.ExampleEvent0,
new EventMetadata(
TestLogContract.events.ExampleEvent0.eventSelector,
TestLogContract.events.ExampleEvent0.abiType,
TestLogContract.events.ExampleEvent0.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
[wallets[0].getCompleteAddress().publicKeys.masterOutgoingViewingPublicKey],
);

const collectedEvent1s = await wallets[0].getEvents(
const collectedEvent1s = await wallets[0].getEvents<ExampleEvent1>(
EventType.Encrypted,
TestLogContract.events.ExampleEvent1,
new EventMetadata(
TestLogContract.events.ExampleEvent1.eventSelector,
TestLogContract.events.ExampleEvent1.abiType,
TestLogContract.events.ExampleEvent1.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
[wallets[0].getCompleteAddress().publicKeys.masterIncomingViewingPublicKey],
Expand All @@ -150,9 +168,13 @@ describe('Logs', () => {
expect(collectedEvent0s.length).toBe(10);
expect(collectedEvent1s.length).toBe(5);

const emptyEvent1s = await wallets[0].getEvents(
const emptyEvent1s = await wallets[0].getEvents<ExampleEvent1>(
EventType.Encrypted,
TestLogContract.events.ExampleEvent1,
new EventMetadata(
TestLogContract.events.ExampleEvent1.eventSelector,
TestLogContract.events.ExampleEvent1.abiType,
TestLogContract.events.ExampleEvent1.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
[wallets[0].getCompleteAddress().publicKeys.masterOutgoingViewingPublicKey],
Expand Down Expand Up @@ -199,16 +221,24 @@ describe('Logs', () => {
);
const lastTx = await testLogContract.methods.emit_unencrypted_events(preimage[++i]).send().wait();

const collectedEvent0s = await wallets[0].getEvents(
const collectedEvent0s = await wallets[0].getEvents<ExampleEvent0>(
EventType.Unencrypted,
TestLogContract.events.ExampleEvent0,
new EventMetadata(
TestLogContract.events.ExampleEvent0.eventSelector,
TestLogContract.events.ExampleEvent0.abiType,
TestLogContract.events.ExampleEvent0.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
);

const collectedEvent1s = await wallets[0].getEvents(
const collectedEvent1s = await wallets[0].getEvents<ExampleEvent1>(
EventType.Unencrypted,
TestLogContract.events.ExampleEvent1,
new EventMetadata(
TestLogContract.events.ExampleEvent1.eventSelector,
TestLogContract.events.ExampleEvent1.abiType,
TestLogContract.events.ExampleEvent1.fieldNames,
),
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BatchCall, EventType } from '@aztec/aztec.js';
import { TokenContract } from '@aztec/noir-contracts.js';
import { BatchCall, EventMetadata, EventType } from '@aztec/aztec.js';
import { TokenContract, type Transfer } from '@aztec/noir-contracts.js';

import { TokenContractTest } from './token_contract_test.js';

Expand Down Expand Up @@ -40,7 +40,16 @@ describe('e2e_token_contract private transfer recursion', () => {
// We should have created a single new note, for the recipient
expect(tx.debugInfo?.noteHashes.length).toBe(1);

const events = await wallets[1].getEvents(EventType.Encrypted, TokenContract.events.Transfer, tx.blockNumber!, 1);
const events = await wallets[1].getEvents<Transfer>(
EventType.Encrypted,
new EventMetadata(
TokenContract.events.Transfer.eventSelector,
TokenContract.events.Transfer.abiType,
TokenContract.events.Transfer.fieldNames,
),
tx.blockNumber!,
1,
);

expect(events[0]).toEqual({
from: accounts[0].address,
Expand All @@ -66,7 +75,16 @@ describe('e2e_token_contract private transfer recursion', () => {
const senderBalance = await asset.methods.balance_of_private(accounts[0].address).simulate();
expect(senderBalance).toEqual(expectedChange);

const events = await wallets[1].getEvents(EventType.Encrypted, TokenContract.events.Transfer, tx.blockNumber!, 1);
const events = await wallets[1].getEvents(
EventType.Encrypted,
new EventMetadata(
TokenContract.events.Transfer.eventSelector,
TokenContract.events.Transfer.abiType,
TokenContract.events.Transfer.fieldNames,
),
tx.blockNumber!,
1,
);

expect(events[0]).toEqual({
from: accounts[0].address,
Expand Down
Loading
Loading