Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
Initial
  • Loading branch information
sklppy88 committed Jun 11, 2024
1 parent fa8f12f commit d265434
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 25 deletions.
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/rpc_clients/pxe_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
false,
'pxe',
fetch,
);
) as PXE;
13 changes: 12 additions & 1 deletion yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
type AuthWitness,
type EventMetadata,
type ExtendedNote,
type FunctionCall,
type GetUnencryptedLogsResponse,
Expand All @@ -17,7 +18,14 @@ import {
type TxReceipt,
} from '@aztec/circuit-types';
import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
import { type AztecAddress, type CompleteAddress, type Fq, type Fr, type PartialAddress } from '@aztec/circuits.js';
import {
type AztecAddress,
type CompleteAddress,
type Fq,
type Fr,
type PartialAddress,
type Point,
} from '@aztec/circuits.js';
import { type ContractArtifact } from '@aztec/foundation/abi';
import { type ContractClassWithId, type ContractInstanceWithAddress } from '@aztec/types/contracts';
import { type NodeInfo } from '@aztec/types/interfaces';
Expand Down Expand Up @@ -181,4 +189,7 @@ export abstract class BaseWallet implements Wallet {
getPXEInfo(): Promise<PXEInfo> {
return this.pxe.getPXEInfo();
}
getEvents<T>(from: number, limit: number, eventMetadata: EventMetadata<T>, ivpk: Point): Promise<T[]> {
return this.pxe.getEvents(from, limit, eventMetadata, ivpk);
}
}
35 changes: 18 additions & 17 deletions yarn-project/builder/src/contract-interface-gen/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,50 +239,51 @@ function generateNotesGetter(input: ContractArtifact) {
`;
}

// This is of type AbiType
// events is of type AbiType
function generateEvents(events: any[] | undefined) {
if (events === undefined) {
return { events: '', eventDefs: '' };
}

const eventsStrings = events.map(event => {
const eventName = event.path.split('::')[1];
const eventsMetadata = events.map(event => {
const eventName = event.path.split('::').at(-1);

const eventDefProps = event.fields.map((field: any) => `${field.name}: Fr`);
const eventDefs = `
const eventDef = `
export type ${eventName} = {
${eventDefProps.join('\n')}
}
`;

const fieldNames = event.fields.map((field: any) => `"${field.name}"`);
const eventsType = `${eventName}: {decode: (payload: L1EventPayload | undefined) => ${eventName} | undefined }`;
const eventType = `${eventName}: {decode: (payload: L1EventPayload | undefined) => ${eventName} | undefined, functionSelector: FunctionSelector, fieldNames: string[] }`;

// Get the last item in path
const eventDecode = `${event.path.split('::').at(-1)}: {
decode: this.decodeEvent(${event.fields.length}, '${eventName}(${event.fields
const eventImpl = `${eventName}: {
decode: this.decodeEvent(${event.fields.length}, FunctionSelector.fromSignature('${eventName}(${event.fields
.map(() => 'Field')
.join(',')})', [${fieldNames}])
.join(',')})'), [${fieldNames}]),
functionSelector: FunctionSelector.fromSignature('${eventName}(${event.fields.map(() => 'Field').join(',')})'),
fieldNames: [${fieldNames}],
}`;

return {
eventDefs,
eventsType,
eventDecode,
eventDef,
eventType,
eventImpl,
};
});

return {
eventDefs: eventsStrings.map(({ eventDefs }) => eventDefs).join('\n'),
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>(fieldsLength: number, functionSignature: string, fields: string[]): (payload: L1EventPayload | undefined) => T | undefined {
private static decodeEvent<T>(fieldsLength: number, functionSelector: FunctionSelector, fields: string[]): (payload: L1EventPayload | undefined) => T | undefined {
return (payload: L1EventPayload | undefined): T | undefined => {
if (payload === undefined) {
return undefined;
}
if (
!FunctionSelector.fromSignature(functionSignature).equals(
!functionSelector.equals(
FunctionSelector.fromField(payload.eventTypeId),
)
) {
Expand All @@ -304,9 +305,9 @@ function generateEvents(events: any[] | undefined) {
};
}
public static get events(): { ${eventsStrings.map(({ eventsType }) => eventsType).join(', ')} } {
public static get events(): { ${eventsMetadata.map(({ eventType }) => eventType).join(', ')} } {
return {
${eventsStrings.map(({ eventDecode }) => eventDecode).join(',\n')}
${eventsMetadata.map(({ eventImpl }) => eventImpl).join(',\n')}
};
}
`,
Expand Down
32 changes: 29 additions & 3 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { type AztecAddress, type CompleteAddress, type Fq, type Fr, type PartialAddress } from '@aztec/circuits.js';
import { type ContractArtifact } from '@aztec/foundation/abi';
import {
type AztecAddress,
type CompleteAddress,
type Fq,
type Fr,
type PartialAddress,
type Point,
} from '@aztec/circuits.js';
import { type ContractArtifact, type FunctionSelector } from '@aztec/foundation/abi';
import {
type ContractClassWithId,
type ContractInstanceWithAddress,
Expand All @@ -9,7 +16,7 @@ import { type NodeInfo } from '@aztec/types/interfaces';

import { type AuthWitness } from '../auth_witness.js';
import { type L2Block } from '../l2_block.js';
import { type GetUnencryptedLogsResponse, type LogFilter } from '../logs/index.js';
import { type GetUnencryptedLogsResponse, type L1EventPayload, type LogFilter } from '../logs/index.js';
import { type ExtendedNote } from '../notes/index.js';
import { type NoteFilter } from '../notes/note_filter.js';
import { type NoteProcessorStats } from '../stats/stats.js';
Expand Down Expand Up @@ -355,9 +362,28 @@ export interface PXE {
* TODO(@spalladino): Same notes as above.
*/
isContractPubliclyDeployed(address: AztecAddress): Promise<boolean>;

/**
* Returns the events of a specified type.
* @param from - The block number to search from.
* @param limit - The amount of blocks to search.
* @param eventMetadata - Identifier of the event. This should be the class generated from the contract. e.g. Contract.events.Event
* @param ivpk - The incoming viewing public key that corresponds to the incoming viewing secret key that can decrypt the log.
* @returns - The deserialized events.
*/
getEvents<T>(from: number, limit: number, eventMetadata: EventMetadata<T>, ivpk: Point): Promise<T[]>;
}
// docs:end:pxe-interface

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

/**
* Provides basic information about the running PXE.
*/
Expand Down
53 changes: 51 additions & 2 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,14 @@
import { type AccountWalletWithSecretKey, type AztecNode, Fr, L1EventPayload, TaggedLog } from '@aztec/aztec.js';
import {
type AccountWalletWithSecretKey,
type AztecNode,
Fr,
L1EventPayload,
type PXE,
TaggedLog,
} from '@aztec/aztec.js';
import { deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js';
import { makeTuple } from '@aztec/foundation/array';
import { type Tuple } from '@aztec/foundation/serialize';
import { TestLogContract } from '@aztec/noir-contracts.js';

import { jest } from '@jest/globals';
Expand All @@ -15,11 +23,12 @@ describe('Logs', () => {

let wallets: AccountWalletWithSecretKey[];
let node: AztecNode;
let pxe: PXE;

let teardown: () => Promise<void>;

beforeAll(async () => {
({ teardown, wallets, aztecNode: node } = await setup(2));
({ teardown, wallets, aztecNode: node, pxe } = await setup(2));

await publicDeployAccounts(wallets[0], wallets.slice(0, 2));

Expand Down Expand Up @@ -110,5 +119,45 @@ describe('Logs', () => {
const badEvent1 = TestLogContract.events.ExampleEvent0.decode(decryptedLog1!.payload);
expect(badEvent1).toBe(undefined);
});

it('emits multiple events as encrypted logs and decodes them', async () => {
const randomness = makeTuple(5, makeTuple.bind(undefined, 2, Fr.random)) as Tuple<Tuple<Fr, 2>, 5>;
const preimage = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple<Tuple<Fr, 4>, 5>;

let i = 0;
const firstTx = await testLogContract.methods.emit_encrypted_events(randomness[i], preimage[i]).send().wait();
await Promise.all(
[...new Array(3)].map(() =>
testLogContract.methods.emit_encrypted_events(randomness[++i], preimage[i]).send().wait(),
),
);
const lastTx = await testLogContract.methods.emit_encrypted_events(randomness[++i], preimage[i]).send().wait();

const collectedEvent0s = await pxe.getEvents(
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
TestLogContract.events.ExampleEvent0,
wallets[0].getCompleteAddress().publicKeys.masterIncomingViewingPublicKey,
);

const collectedEvent1s = await pxe.getEvents(
firstTx.blockNumber!,
lastTx.blockNumber! - firstTx.blockNumber! + 1,
TestLogContract.events.ExampleEvent1,
wallets[0].getCompleteAddress().publicKeys.masterIncomingViewingPublicKey,
);

for (let i = 0; i < 5; ++i) {
expect(collectedEvent0s[i]).toStrictEqual({
value0: preimage[i][0],
value1: preimage[i][1],
});

expect(collectedEvent1s[i]).toStrictEqual({
value2: preimage[i][2],
value3: preimage[i][3],
});
}
});
});
});
46 changes: 45 additions & 1 deletion yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import {
type AztecNode,
EncryptedNoteTxL2Logs,
EncryptedTxL2Logs,
type EventMetadata,
ExtendedNote,
type FunctionCall,
type GetUnencryptedLogsResponse,
L1EventPayload,
type L2Block,
type LogFilter,
MerkleTreeId,
Expand All @@ -15,6 +17,7 @@ import {
type ProofCreator,
SimulatedTx,
SimulationError,
TaggedLog,
Tx,
type TxEffect,
type TxExecutionRequest,
Expand All @@ -32,7 +35,7 @@ import {
} from '@aztec/circuits.js';
import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
import { type Fq, Fr } from '@aztec/foundation/fields';
import { type Fq, Fr, type Point } from '@aztec/foundation/fields';
import { SerialQueue } from '@aztec/foundation/fifo';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { type KeyStore } from '@aztec/key-store';
Expand Down Expand Up @@ -756,4 +759,45 @@ export class PXEService implements PXE {
public async isContractPubliclyDeployed(address: AztecAddress): Promise<boolean> {
return !!(await this.node.getContract(address));
}

public async getEvents<T>(from: number, limit: number, eventMetadata: EventMetadata<T>, ivpk: Point): Promise<T[]> {
const blocks = await this.node.getBlocks(from, limit);

const txEffects = blocks.flatMap(block => block.body.txEffects);
const encryptedTxLogs = txEffects.flatMap(txEffect => txEffect.encryptedLogs);

const encryptedLogs = encryptedTxLogs.flatMap(encryptedTxLog => encryptedTxLog.unrollLogs());

const ivsk = await this.keyStore.getMasterSecretKey(ivpk);

const visibleEvents = encryptedLogs
.map(encryptedLog => TaggedLog.decryptAsIncoming(encryptedLog, ivsk, L1EventPayload))
.filter(item => item !== undefined) as TaggedLog<L1EventPayload>[];

const decodedEvents = visibleEvents
.map(visibleEvent => {
if (visibleEvent.payload === undefined) {
return undefined;
}
if (!FunctionSelector.fromField(visibleEvent.payload.eventTypeId).equals(eventMetadata.functionSelector)) {
return undefined;
}
if (visibleEvent.payload.event.items.length !== eventMetadata.fieldNames.length) {
throw new Error(
'Something is weird here, we have matching FunctionSelectors, but the actual payload has mismatched length',
);
}

return eventMetadata.fieldNames.reduce(
(acc, curr, i) => ({
...acc,
[curr]: visibleEvent.payload.event.items[i],
}),
{} as T,
);
})
.filter(visibleEvent => visibleEvent !== undefined) as T[];

return decodedEvents;
}
}

0 comments on commit d265434

Please sign in to comment.