@aztec/pxe 0.0.1-commit.d431d1c → 0.0.1-commit.d6f2b3f94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/bin/check_oracle_version.js +1 -1
- package/dest/block_synchronizer/block_synchronizer.d.ts +9 -5
- package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
- package/dest/block_synchronizer/block_synchronizer.js +62 -16
- package/dest/config/index.d.ts +3 -1
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +17 -0
- package/dest/contract_function_simulator/contract_function_simulator.d.ts +5 -5
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +12 -15
- package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +1 -1
- package/dest/contract_function_simulator/noir-structs/event_validation_request.js +1 -1
- package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +1 -1
- package/dest/contract_function_simulator/noir-structs/note_validation_request.js +1 -1
- package/dest/contract_function_simulator/oracle/interfaces.d.ts +15 -12
- package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.d.ts +6 -4
- package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.js +51 -29
- package/dest/contract_function_simulator/oracle/private_execution.d.ts +2 -22
- package/dest/contract_function_simulator/oracle/private_execution.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution.js +0 -37
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +12 -3
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +20 -9
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +38 -24
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +84 -65
- package/dest/contract_sync/contract_sync_service.d.ts +41 -0
- package/dest/contract_sync/contract_sync_service.d.ts.map +1 -0
- package/dest/contract_sync/contract_sync_service.js +82 -0
- package/dest/contract_sync/helpers.d.ts +28 -0
- package/dest/contract_sync/helpers.d.ts.map +1 -0
- package/dest/contract_sync/helpers.js +55 -0
- package/dest/debug/pxe_debug_utils.d.ts +22 -9
- package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
- package/dest/debug/pxe_debug_utils.js +28 -17
- package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
- package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
- package/dest/entrypoints/client/bundle/utils.js +12 -6
- package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
- package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
- package/dest/entrypoints/client/lazy/utils.js +13 -7
- package/dest/entrypoints/pxe_creation_options.d.ts +3 -2
- package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
- package/dest/entrypoints/server/index.d.ts +2 -1
- package/dest/entrypoints/server/index.d.ts.map +1 -1
- package/dest/entrypoints/server/index.js +1 -0
- package/dest/entrypoints/server/utils.d.ts +1 -1
- package/dest/entrypoints/server/utils.d.ts.map +1 -1
- package/dest/entrypoints/server/utils.js +19 -8
- package/dest/events/event_service.d.ts +5 -6
- package/dest/events/event_service.d.ts.map +1 -1
- package/dest/events/event_service.js +6 -7
- package/dest/job_coordinator/job_coordinator.d.ts +3 -2
- package/dest/job_coordinator/job_coordinator.d.ts.map +1 -1
- package/dest/job_coordinator/job_coordinator.js +3 -2
- package/dest/logs/log_service.d.ts +6 -5
- package/dest/logs/log_service.d.ts.map +1 -1
- package/dest/logs/log_service.js +20 -19
- package/dest/notes/note_service.d.ts +6 -6
- package/dest/notes/note_service.d.ts.map +1 -1
- package/dest/notes/note_service.js +14 -14
- package/dest/oracle_version.d.ts +3 -3
- package/dest/oracle_version.d.ts.map +1 -1
- package/dest/oracle_version.js +4 -3
- package/dest/private_kernel/private_kernel_execution_prover.d.ts +3 -2
- package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.js +2 -2
- package/dest/private_kernel/private_kernel_oracle.d.ts +3 -3
- package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
- package/dest/pxe.d.ts +13 -2
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +45 -20
- package/dest/storage/address_store/address_store.d.ts +1 -1
- package/dest/storage/address_store/address_store.d.ts.map +1 -1
- package/dest/storage/address_store/address_store.js +12 -11
- package/dest/storage/anchor_block_store/anchor_block_store.d.ts +9 -1
- package/dest/storage/anchor_block_store/anchor_block_store.d.ts.map +1 -1
- package/dest/storage/anchor_block_store/anchor_block_store.js +8 -1
- package/dest/storage/capsule_store/capsule_store.js +6 -8
- package/dest/storage/contract_store/contract_store.d.ts +1 -2
- package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
- package/dest/storage/contract_store/contract_store.js +27 -30
- package/dest/storage/metadata.d.ts +1 -1
- package/dest/storage/metadata.js +1 -1
- package/dest/storage/note_store/note_store.d.ts +48 -50
- package/dest/storage/note_store/note_store.d.ts.map +1 -1
- package/dest/storage/note_store/note_store.js +279 -252
- package/dest/storage/note_store/stored_note.d.ts +16 -0
- package/dest/storage/note_store/stored_note.d.ts.map +1 -0
- package/dest/storage/note_store/stored_note.js +43 -0
- package/dest/storage/private_event_store/private_event_store.d.ts +17 -4
- package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
- package/dest/storage/private_event_store/private_event_store.js +198 -147
- package/dest/storage/private_event_store/stored_private_event.d.ts +23 -0
- package/dest/storage/private_event_store/stored_private_event.d.ts.map +1 -0
- package/dest/storage/private_event_store/stored_private_event.js +56 -0
- package/dest/storage/tagging_store/recipient_tagging_store.d.ts +1 -1
- package/dest/storage/tagging_store/recipient_tagging_store.d.ts.map +1 -1
- package/dest/storage/tagging_store/recipient_tagging_store.js +31 -19
- package/dest/storage/tagging_store/sender_address_book_store.d.ts +1 -1
- package/dest/storage/tagging_store/sender_address_book_store.d.ts.map +1 -1
- package/dest/storage/tagging_store/sender_address_book_store.js +20 -14
- package/dest/storage/tagging_store/sender_tagging_store.d.ts +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.js +183 -113
- package/dest/tagging/get_all_logs_by_tags.d.ts +24 -0
- package/dest/tagging/get_all_logs_by_tags.d.ts.map +1 -0
- package/dest/tagging/get_all_logs_by_tags.js +46 -0
- package/dest/tagging/index.d.ts +2 -1
- package/dest/tagging/index.d.ts.map +1 -1
- package/dest/tagging/index.js +1 -0
- package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +3 -2
- package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
- package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +2 -2
- package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts +3 -2
- package/dest/tagging/recipient_sync/utils/load_logs_for_range.d.ts.map +1 -1
- package/dest/tagging/recipient_sync/utils/load_logs_for_range.js +5 -2
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +3 -2
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +2 -2
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +1 -1
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +5 -8
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +3 -2
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +7 -4
- package/package.json +25 -16
- package/src/bin/check_oracle_version.ts +1 -0
- package/src/block_synchronizer/block_synchronizer.ts +76 -33
- package/src/config/index.ts +14 -0
- package/src/contract_function_simulator/contract_function_simulator.ts +12 -17
- package/src/contract_function_simulator/noir-structs/event_validation_request.ts +1 -1
- package/src/contract_function_simulator/noir-structs/note_validation_request.ts +1 -1
- package/src/contract_function_simulator/oracle/interfaces.ts +20 -11
- package/src/contract_function_simulator/oracle/oracle.ts +56 -26
- package/src/contract_function_simulator/oracle/private_execution.ts +1 -60
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +34 -11
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +107 -74
- package/src/contract_sync/contract_sync_service.ts +129 -0
- package/src/contract_sync/helpers.ts +93 -0
- package/src/debug/pxe_debug_utils.ts +60 -17
- package/src/entrypoints/client/bundle/utils.ts +7 -14
- package/src/entrypoints/client/lazy/utils.ts +8 -14
- package/src/entrypoints/pxe_creation_options.ts +2 -1
- package/src/entrypoints/server/index.ts +1 -0
- package/src/entrypoints/server/utils.ts +15 -19
- package/src/events/event_service.ts +5 -7
- package/src/job_coordinator/job_coordinator.ts +4 -3
- package/src/logs/log_service.ts +31 -15
- package/src/notes/note_service.ts +21 -20
- package/src/oracle_version.ts +4 -3
- package/src/private_kernel/private_kernel_execution_prover.ts +6 -3
- package/src/private_kernel/private_kernel_oracle.ts +2 -2
- package/src/pxe.ts +84 -19
- package/src/storage/address_store/address_store.ts +15 -15
- package/src/storage/anchor_block_store/anchor_block_store.ts +8 -0
- package/src/storage/capsule_store/capsule_store.ts +8 -8
- package/src/storage/contract_store/contract_store.ts +26 -35
- package/src/storage/metadata.ts +1 -1
- package/src/storage/note_store/note_store.ts +318 -318
- package/src/storage/note_store/stored_note.ts +48 -0
- package/src/storage/private_event_store/private_event_store.ts +250 -190
- package/src/storage/private_event_store/stored_private_event.ts +73 -0
- package/src/storage/tagging_store/recipient_tagging_store.ts +31 -21
- package/src/storage/tagging_store/sender_address_book_store.ts +20 -14
- package/src/storage/tagging_store/sender_tagging_store.ts +210 -126
- package/src/tagging/get_all_logs_by_tags.ts +68 -0
- package/src/tagging/index.ts +1 -0
- package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +11 -1
- package/src/tagging/recipient_sync/utils/load_logs_for_range.ts +7 -1
- package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +3 -1
- package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +5 -17
- package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +12 -3
- package/dest/tree_membership/tree_membership_service.d.ts +0 -50
- package/dest/tree_membership/tree_membership_service.d.ts.map +0 -1
- package/dest/tree_membership/tree_membership_service.js +0 -75
- package/src/tree_membership/tree_membership_service.ts +0 -97
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
3
|
+
import { NoteDao } from '@aztec/stdlib/note';
|
|
4
|
+
|
|
5
|
+
export class StoredNote {
|
|
6
|
+
constructor(
|
|
7
|
+
readonly noteDao: NoteDao,
|
|
8
|
+
readonly scopes: Set<string>,
|
|
9
|
+
private _nullifiedAt: BlockNumber | undefined = undefined,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
static fromBuffer(buffer: Buffer) {
|
|
13
|
+
const reader = BufferReader.asReader(buffer);
|
|
14
|
+
|
|
15
|
+
const noteDao = NoteDao.fromBuffer(reader);
|
|
16
|
+
const scopes = reader.readVector({ fromBuffer: (r: BufferReader) => r.readString() });
|
|
17
|
+
|
|
18
|
+
const nullifiedAtRaw = reader.readNumber();
|
|
19
|
+
const nullifiedAt = nullifiedAtRaw === 0 ? undefined : (nullifiedAtRaw as BlockNumber);
|
|
20
|
+
|
|
21
|
+
return new StoredNote(noteDao, new Set(scopes), nullifiedAt);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toBuffer(): Buffer {
|
|
25
|
+
const scopesArray = [...this.scopes];
|
|
26
|
+
return serializeToBuffer(this.noteDao, scopesArray.length, ...scopesArray, this._nullifiedAt ?? 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
addScope(scope: string) {
|
|
30
|
+
this.scopes.add(scope);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
markAsNullified(blockNumber: BlockNumber) {
|
|
34
|
+
this._nullifiedAt = blockNumber;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
markAsActive() {
|
|
38
|
+
this._nullifiedAt = undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
isNullified() {
|
|
42
|
+
return this._nullifiedAt !== undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get nullifiedAt() {
|
|
46
|
+
return this._nullifiedAt;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
4
|
+
import { Semaphore } from '@aztec/foundation/queue';
|
|
5
5
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
6
6
|
import type { EventSelector } from '@aztec/stdlib/abi';
|
|
7
7
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
|
-
import {
|
|
9
|
-
import { type InTx, TxHash } from '@aztec/stdlib/tx';
|
|
8
|
+
import type { InTx, TxHash } from '@aztec/stdlib/tx';
|
|
10
9
|
|
|
11
10
|
import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
|
|
12
11
|
import type { PackedPrivateEvent } from '../../pxe.js';
|
|
12
|
+
import { StoredPrivateEvent } from './stored_private_event.js';
|
|
13
13
|
|
|
14
14
|
export type PrivateEventStoreFilter = {
|
|
15
15
|
contractAddress: AztecAddress;
|
|
@@ -19,20 +19,6 @@ export type PrivateEventStoreFilter = {
|
|
|
19
19
|
txHash?: TxHash;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
type PrivateEventEntry = {
|
|
23
|
-
randomness: Fr; // Note that this value is currently not being returned on queries and is therefore temporarily unused
|
|
24
|
-
msgContent: Buffer;
|
|
25
|
-
l2BlockNumber: number;
|
|
26
|
-
l2BlockHash: Buffer;
|
|
27
|
-
txHash: Buffer;
|
|
28
|
-
/** The index of the tx within the block, used for ordering events */
|
|
29
|
-
txIndexInBlock: number;
|
|
30
|
-
/** The index of the event within the tx (based on nullifier position), used for ordering events */
|
|
31
|
-
eventIndexInTx: number;
|
|
32
|
-
/** The lookup key for #eventsByContractScopeSelector, used for cleanup during rollback */
|
|
33
|
-
lookupKey: string;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
22
|
type PrivateEventMetadata = InTx & {
|
|
37
23
|
contractAddress: AztecAddress;
|
|
38
24
|
scope: AztecAddress;
|
|
@@ -49,104 +35,29 @@ export class PrivateEventStore implements StagedStore {
|
|
|
49
35
|
readonly storeName: string = 'private_event';
|
|
50
36
|
|
|
51
37
|
#store: AztecAsyncKVStore;
|
|
52
|
-
/**
|
|
53
|
-
#
|
|
54
|
-
/** Multi-map from
|
|
55
|
-
#
|
|
38
|
+
/** Actual private event log entries, keyed by siloedEventCommitment */
|
|
39
|
+
#events: AztecAsyncMap<string, Buffer>;
|
|
40
|
+
/** Multi-map from contractAddress_eventSelector to siloedEventCommitment for efficient lookup */
|
|
41
|
+
#eventsByContractAndEventSelector: AztecAsyncMultiMap<string, string>;
|
|
56
42
|
/** Multi-map from block number to siloedEventCommitment for rollback support */
|
|
57
43
|
#eventsByBlockNumber: AztecAsyncMultiMap<number, string>;
|
|
58
|
-
/** Map from siloedEventCommitment to boolean indicating if log has been seen. */
|
|
59
|
-
#seenLogs: AztecAsyncMap<string, boolean>;
|
|
60
44
|
|
|
61
|
-
/** jobId => eventId (event siloed nullifier)
|
|
62
|
-
#
|
|
45
|
+
/** jobId => eventId (event siloed nullifier) => StoredPrivateEvent */
|
|
46
|
+
#eventsForJob: Map<string, Map<string, StoredPrivateEvent>>;
|
|
47
|
+
|
|
48
|
+
/** Per-job locks to prevent concurrent writes from affecting each other. */
|
|
49
|
+
#jobLocks: Map<string, Semaphore>;
|
|
63
50
|
|
|
64
51
|
logger = createLogger('private_event_store');
|
|
65
52
|
|
|
66
53
|
constructor(store: AztecAsyncKVStore) {
|
|
67
54
|
this.#store = store;
|
|
68
|
-
this.#
|
|
69
|
-
this.#
|
|
70
|
-
this.#seenLogs = this.#store.openMap('seen_logs');
|
|
55
|
+
this.#events = this.#store.openMap('private_event_logs');
|
|
56
|
+
this.#eventsByContractAndEventSelector = this.#store.openMultiMap('events_by_contract_selector');
|
|
71
57
|
this.#eventsByBlockNumber = this.#store.openMultiMap('events_by_block_number');
|
|
72
58
|
|
|
73
|
-
this.#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
#keyFor(contractAddress: AztecAddress, scope: AztecAddress, eventSelector: EventSelector): string {
|
|
77
|
-
return `${contractAddress.toString()}_${scope.toString()}_${eventSelector.toString()}`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async commit(jobId: string): Promise<void> {
|
|
81
|
-
await Promise.all(
|
|
82
|
-
[...this.#getEventLogsInJobStage(jobId).entries()].map(async ([eventId, eventEntry]) => {
|
|
83
|
-
this.logger.verbose('storing private event log (KV store)', eventEntry);
|
|
84
|
-
|
|
85
|
-
await Promise.all([
|
|
86
|
-
this.#eventLogs.set(eventId, eventEntry),
|
|
87
|
-
this.#eventsByContractScopeSelector.set(eventEntry.lookupKey, eventId),
|
|
88
|
-
this.#eventsByBlockNumber.set(eventEntry.l2BlockNumber, eventId),
|
|
89
|
-
this.#seenLogs.set(eventId, true),
|
|
90
|
-
]);
|
|
91
|
-
}),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
return this.discardStaged(jobId);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
discardStaged(jobId: string): Promise<void> {
|
|
98
|
-
this.#eventLogsInJobStage.delete(jobId);
|
|
99
|
-
return Promise.resolve();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
#getEventLogsInJobStage(jobId: string): Map<string, PrivateEventEntry> {
|
|
103
|
-
let jobStage = this.#eventLogsInJobStage.get(jobId);
|
|
104
|
-
if (jobStage === undefined) {
|
|
105
|
-
jobStage = new Map();
|
|
106
|
-
this.#eventLogsInJobStage.set(jobId, jobStage);
|
|
107
|
-
}
|
|
108
|
-
return jobStage;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async #isSeenLog(jobId: string, eventId: string): Promise<boolean> {
|
|
112
|
-
const eventLogsInJobStage = this.#getEventLogsInJobStage(jobId).get(eventId);
|
|
113
|
-
return !!eventLogsInJobStage || !!(await this.#seenLogs.getAsync(eventId));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
#addEventLogToStage(jobId: string, eventId: string, eventEntry: PrivateEventEntry) {
|
|
117
|
-
this.#getEventLogsInJobStage(jobId).set(eventId, eventEntry);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async #getEventSiloedNullifiers(
|
|
121
|
-
contractAddress: AztecAddress,
|
|
122
|
-
scope: AztecAddress,
|
|
123
|
-
eventSelector: EventSelector,
|
|
124
|
-
jobId?: string,
|
|
125
|
-
): Promise<string[]> {
|
|
126
|
-
const key = this.#keyFor(contractAddress, scope, eventSelector);
|
|
127
|
-
const eventSiloedNullifiersInStorage: string[] = [];
|
|
128
|
-
for await (const eventId of this.#eventsByContractScopeSelector.getValuesAsync(key)) {
|
|
129
|
-
eventSiloedNullifiersInStorage.push(eventId);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (!jobId) {
|
|
133
|
-
return eventSiloedNullifiersInStorage;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const eventSiloedNullifiersInJobStage = new Set(
|
|
137
|
-
[...this.#getEventLogsInJobStage(jobId).entries()]
|
|
138
|
-
.filter(([_, entry]) => entry.lookupKey === key)
|
|
139
|
-
.map(([idx, _]) => idx),
|
|
140
|
-
);
|
|
141
|
-
return [...new Set(eventSiloedNullifiersInStorage).union(eventSiloedNullifiersInJobStage)];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async #getEventLogBySiloedNullifier(eventId: string, jobId?: string): Promise<PrivateEventEntry | undefined> {
|
|
145
|
-
if (jobId) {
|
|
146
|
-
return this.#getEventLogsInJobStage(jobId).get(eventId) ?? (await this.#eventLogs.getAsync(eventId));
|
|
147
|
-
} else {
|
|
148
|
-
return await this.#eventLogs.getAsync(eventId);
|
|
149
|
-
}
|
|
59
|
+
this.#eventsForJob = new Map();
|
|
60
|
+
this.#jobLocks = new Map();
|
|
150
61
|
}
|
|
151
62
|
|
|
152
63
|
/**
|
|
@@ -168,40 +79,46 @@ export class PrivateEventStore implements StagedStore {
|
|
|
168
79
|
siloedEventCommitment: Fr,
|
|
169
80
|
metadata: PrivateEventMetadata,
|
|
170
81
|
jobId: string,
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
this.logger.verbose('storing private event log (job stage)', {
|
|
188
|
-
contractAddress,
|
|
189
|
-
scope,
|
|
190
|
-
msgContent,
|
|
191
|
-
l2BlockNumber,
|
|
192
|
-
});
|
|
82
|
+
) {
|
|
83
|
+
return this.#withJobLock(jobId, () =>
|
|
84
|
+
this.#store.transactionAsync(async () => {
|
|
85
|
+
const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
|
|
86
|
+
const eventId = siloedEventCommitment.toString();
|
|
87
|
+
|
|
88
|
+
this.logger.verbose('storing private event log (job stage)', {
|
|
89
|
+
eventId,
|
|
90
|
+
contractAddress,
|
|
91
|
+
scope,
|
|
92
|
+
msgContent,
|
|
93
|
+
l2BlockNumber,
|
|
94
|
+
});
|
|
193
95
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
96
|
+
const existing = await this.#readEvent(eventId, jobId);
|
|
97
|
+
|
|
98
|
+
if (existing) {
|
|
99
|
+
// If we already stored this event, we still want to make sure to track it for the given scope
|
|
100
|
+
existing.addScope(scope.toString());
|
|
101
|
+
this.#writeEvent(eventId, existing, jobId);
|
|
102
|
+
} else {
|
|
103
|
+
this.#writeEvent(
|
|
104
|
+
eventId,
|
|
105
|
+
new StoredPrivateEvent(
|
|
106
|
+
randomness,
|
|
107
|
+
msgContent,
|
|
108
|
+
l2BlockNumber,
|
|
109
|
+
l2BlockHash,
|
|
110
|
+
txHash,
|
|
111
|
+
txIndexInBlock,
|
|
112
|
+
eventIndexInTx,
|
|
113
|
+
contractAddress,
|
|
114
|
+
eventSelector,
|
|
115
|
+
new Set([scope.toString()]),
|
|
116
|
+
),
|
|
117
|
+
jobId,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
205
122
|
}
|
|
206
123
|
|
|
207
124
|
/**
|
|
@@ -215,64 +132,88 @@ export class PrivateEventStore implements StagedStore {
|
|
|
215
132
|
* @returns - The event log contents, augmented with metadata about the transaction and block in which the event was
|
|
216
133
|
* included.
|
|
217
134
|
*/
|
|
218
|
-
public
|
|
135
|
+
public getPrivateEvents(
|
|
219
136
|
eventSelector: EventSelector,
|
|
220
137
|
filter: PrivateEventStoreFilter,
|
|
221
138
|
): Promise<PackedPrivateEvent[]> {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
event
|
|
227
|
-
|
|
139
|
+
return this.#store.transactionAsync(async () => {
|
|
140
|
+
const key = this.#keyFor(filter.contractAddress, eventSelector);
|
|
141
|
+
const targetScopes = new Set(filter.scopes.map(s => s.toString()));
|
|
142
|
+
|
|
143
|
+
// Map from eventId to the promise that reads the event buffer.
|
|
144
|
+
// We start reads during iteration to keep DB requests pending and avoid IndexedDB auto-commit.
|
|
145
|
+
const eventReadPromises: Map<string, Promise<Buffer | undefined>> = new Map();
|
|
146
|
+
|
|
147
|
+
for await (const eventId of this.#eventsByContractAndEventSelector.getValuesAsync(key)) {
|
|
148
|
+
eventReadPromises.set(eventId, this.#events.getAsync(eventId));
|
|
149
|
+
}
|
|
228
150
|
|
|
229
|
-
|
|
230
|
-
const
|
|
151
|
+
const eventIds = [...eventReadPromises.keys()];
|
|
152
|
+
const eventBuffers = await Promise.all(eventReadPromises.values());
|
|
153
|
+
|
|
154
|
+
const events: Array<{
|
|
155
|
+
l2BlockNumber: number;
|
|
156
|
+
txIndexInBlock: number;
|
|
157
|
+
eventIndexInTx: number;
|
|
158
|
+
event: PackedPrivateEvent;
|
|
159
|
+
}> = [];
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < eventIds.length; i++) {
|
|
162
|
+
const eventId = eventIds[i];
|
|
163
|
+
const eventBuffer = eventBuffers[i];
|
|
164
|
+
|
|
165
|
+
// Defensive, if it happens, there's a problem with how we're handling #eventsByContractAndEventSelector
|
|
166
|
+
if (!eventBuffer) {
|
|
167
|
+
this.logger.verbose(
|
|
168
|
+
`EventId ${eventId} does not exist in main index but it is referenced from contract event selector index`,
|
|
169
|
+
);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
231
172
|
|
|
232
|
-
|
|
233
|
-
const entry = await this.#getEventLogBySiloedNullifier(eventId);
|
|
173
|
+
const storedPrivateEvent = StoredPrivateEvent.fromBuffer(eventBuffer);
|
|
234
174
|
|
|
235
|
-
|
|
175
|
+
// Filter by block range
|
|
176
|
+
if (storedPrivateEvent.l2BlockNumber < filter.fromBlock || storedPrivateEvent.l2BlockNumber >= filter.toBlock) {
|
|
236
177
|
continue;
|
|
237
178
|
}
|
|
238
179
|
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const txHash = TxHash.fromBuffer(entry.txHash);
|
|
244
|
-
const l2BlockHash = L2BlockHash.fromBuffer(entry.l2BlockHash);
|
|
180
|
+
// Filter by scopes
|
|
181
|
+
if (storedPrivateEvent.scopes.intersection(targetScopes).size === 0) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
245
184
|
|
|
246
|
-
|
|
185
|
+
// Filter by txHash
|
|
186
|
+
if (filter.txHash && !storedPrivateEvent.txHash.equals(filter.txHash)) {
|
|
247
187
|
continue;
|
|
248
188
|
}
|
|
249
189
|
|
|
250
190
|
events.push({
|
|
251
|
-
l2BlockNumber:
|
|
252
|
-
txIndexInBlock:
|
|
253
|
-
eventIndexInTx:
|
|
191
|
+
l2BlockNumber: storedPrivateEvent.l2BlockNumber,
|
|
192
|
+
txIndexInBlock: storedPrivateEvent.txIndexInBlock,
|
|
193
|
+
eventIndexInTx: storedPrivateEvent.eventIndexInTx,
|
|
254
194
|
event: {
|
|
255
|
-
packedEvent: msgContent,
|
|
256
|
-
l2BlockNumber: BlockNumber(
|
|
257
|
-
txHash,
|
|
258
|
-
l2BlockHash,
|
|
195
|
+
packedEvent: storedPrivateEvent.msgContent,
|
|
196
|
+
l2BlockNumber: BlockNumber(storedPrivateEvent.l2BlockNumber),
|
|
197
|
+
txHash: storedPrivateEvent.txHash,
|
|
198
|
+
l2BlockHash: storedPrivateEvent.l2BlockHash,
|
|
259
199
|
eventSelector,
|
|
260
200
|
},
|
|
261
201
|
});
|
|
262
202
|
}
|
|
263
|
-
}
|
|
264
203
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
204
|
+
// Sort by block number, then by tx index within block, then by event index within tx
|
|
205
|
+
events.sort((a, b) => {
|
|
206
|
+
if (a.l2BlockNumber !== b.l2BlockNumber) {
|
|
207
|
+
return a.l2BlockNumber - b.l2BlockNumber;
|
|
208
|
+
}
|
|
209
|
+
if (a.txIndexInBlock !== b.txIndexInBlock) {
|
|
210
|
+
return a.txIndexInBlock - b.txIndexInBlock;
|
|
211
|
+
}
|
|
212
|
+
return a.eventIndexInTx - b.eventIndexInTx;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return events.map(ev => ev.event);
|
|
274
216
|
});
|
|
275
|
-
return events.map(ev => ev.event);
|
|
276
217
|
}
|
|
277
218
|
|
|
278
219
|
/**
|
|
@@ -293,32 +234,151 @@ export class PrivateEventStore implements StagedStore {
|
|
|
293
234
|
* IMPORTANT: This method must be called within a transaction to ensure atomicity.
|
|
294
235
|
*/
|
|
295
236
|
public async rollback(blockNumber: number, synchedBlockNumber: number): Promise<void> {
|
|
296
|
-
|
|
237
|
+
// First pass: collect all event IDs for all blocks, starting reads during iteration to keep tx alive.
|
|
238
|
+
const eventsByBlock: Map<number, { eventId: string; eventReadPromise: Promise<Buffer | undefined> }[]> = new Map();
|
|
297
239
|
|
|
298
240
|
for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
|
|
299
|
-
const
|
|
241
|
+
const blockEvents: { eventId: string; eventReadPromise: Promise<Buffer | undefined> }[] = [];
|
|
300
242
|
for await (const eventId of this.#eventsByBlockNumber.getValuesAsync(block)) {
|
|
301
|
-
|
|
243
|
+
// Start read immediately during iteration to keep IndexedDB transaction alive
|
|
244
|
+
blockEvents.push({ eventId, eventReadPromise: this.#events.getAsync(eventId) });
|
|
302
245
|
}
|
|
246
|
+
if (blockEvents.length > 0) {
|
|
247
|
+
eventsByBlock.set(block, blockEvents);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
303
250
|
|
|
304
|
-
|
|
305
|
-
|
|
251
|
+
// Second pass: await reads and perform deletes
|
|
252
|
+
let removedCount = 0;
|
|
253
|
+
for (const [block, events] of eventsByBlock) {
|
|
254
|
+
await this.#eventsByBlockNumber.delete(block);
|
|
306
255
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
256
|
+
for (const { eventId, eventReadPromise } of events) {
|
|
257
|
+
const buffer = await eventReadPromise;
|
|
258
|
+
if (!buffer) {
|
|
259
|
+
throw new Error(`Event not found for eventId ${eventId}`);
|
|
260
|
+
}
|
|
312
261
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
262
|
+
const entry = StoredPrivateEvent.fromBuffer(buffer);
|
|
263
|
+
await this.#events.delete(eventId);
|
|
264
|
+
await this.#eventsByContractAndEventSelector.deleteValue(
|
|
265
|
+
this.#keyFor(entry.contractAddress, entry.eventSelector),
|
|
266
|
+
eventId,
|
|
267
|
+
);
|
|
316
268
|
|
|
317
|
-
|
|
318
|
-
}
|
|
269
|
+
removedCount++;
|
|
319
270
|
}
|
|
320
271
|
}
|
|
321
272
|
|
|
322
273
|
this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
|
|
323
274
|
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Commits in memory job data to persistent storage.
|
|
278
|
+
*
|
|
279
|
+
* Called by JobCoordinator when a job completes successfully.
|
|
280
|
+
*
|
|
281
|
+
* Note: JobCoordinator wraps all commits in a single transaction, so we don't need our own transactionAsync here
|
|
282
|
+
* (and using one would throw on IndexedDB as it does not support nested txs).
|
|
283
|
+
*
|
|
284
|
+
* @param jobId - The jobId identifying which staged data to commit
|
|
285
|
+
*/
|
|
286
|
+
async commit(jobId: string): Promise<void> {
|
|
287
|
+
// Note: Don't use #withJobLock here - commit runs within JobCoordinator's transactionAsync,
|
|
288
|
+
// and awaiting the lock would create a microtask boundary with no pending DB request,
|
|
289
|
+
// causing IndexedDB to auto-commit the transaction.
|
|
290
|
+
for (const [eventId, entry] of this.#getEventsForJob(jobId).entries()) {
|
|
291
|
+
const lookupKey = this.#keyFor(entry.contractAddress, entry.eventSelector);
|
|
292
|
+
this.logger.verbose('storing private event log', { eventId, lookupKey });
|
|
293
|
+
|
|
294
|
+
await Promise.all([
|
|
295
|
+
this.#events.set(eventId, entry.toBuffer()),
|
|
296
|
+
this.#eventsByContractAndEventSelector.set(lookupKey, eventId),
|
|
297
|
+
this.#eventsByBlockNumber.set(entry.l2BlockNumber, eventId),
|
|
298
|
+
]);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.#clearJobData(jobId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Discards in memory job data without persisting it.
|
|
306
|
+
*/
|
|
307
|
+
discardStaged(jobId: string): Promise<void> {
|
|
308
|
+
this.#clearJobData(jobId);
|
|
309
|
+
return Promise.resolve();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Reads an event from in-memory job data first, falling back to persistent storage if not found.
|
|
314
|
+
*
|
|
315
|
+
* Returns undefined if the event does not exist in the store overall.
|
|
316
|
+
*/
|
|
317
|
+
async #readEvent(eventId: string, jobId: string): Promise<StoredPrivateEvent | undefined> {
|
|
318
|
+
// Always issue DB read to keep IndexedDB transaction alive (they auto-commit when a new micro-task starts and there
|
|
319
|
+
// are no pending read requests). The staged value still takes precedence if it exists.
|
|
320
|
+
const buffer = await this.#events.getAsync(eventId);
|
|
321
|
+
const eventForJob = this.#getEventsForJob(jobId).get(eventId);
|
|
322
|
+
return eventForJob ?? (buffer ? StoredPrivateEvent.fromBuffer(buffer) : undefined);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Writes an event to in-memory job data.
|
|
327
|
+
*
|
|
328
|
+
* Writes are only allowed in a job context. Events modified during a job will only be persisted when `commit` is
|
|
329
|
+
* called.
|
|
330
|
+
*/
|
|
331
|
+
#writeEvent(eventId: string, entry: StoredPrivateEvent, jobId: string) {
|
|
332
|
+
this.#getEventsForJob(jobId).set(eventId, entry);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get in-memory data only visible to @param jobId
|
|
337
|
+
*/
|
|
338
|
+
#getEventsForJob(jobId: string): Map<string, StoredPrivateEvent> {
|
|
339
|
+
let eventsForJob = this.#eventsForJob.get(jobId);
|
|
340
|
+
if (eventsForJob === undefined) {
|
|
341
|
+
eventsForJob = new Map();
|
|
342
|
+
this.#eventsForJob.set(jobId, eventsForJob);
|
|
343
|
+
}
|
|
344
|
+
return eventsForJob;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Clear data structures supporting a specific job.
|
|
349
|
+
*/
|
|
350
|
+
#clearJobData(jobId: string) {
|
|
351
|
+
this.#eventsForJob.delete(jobId);
|
|
352
|
+
this.#jobLocks.delete(jobId);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Ensures a function can only run once it acquires a unique per-job lock, and handles proper lock release after it
|
|
357
|
+
* runs.
|
|
358
|
+
*
|
|
359
|
+
* This primitive allows concurrent writes on this store without risking data corruption due to unsound write
|
|
360
|
+
* interleaving.
|
|
361
|
+
*/
|
|
362
|
+
async #withJobLock<T>(jobId: string, fn: () => Promise<T>): Promise<T> {
|
|
363
|
+
let lock = this.#jobLocks.get(jobId);
|
|
364
|
+
if (!lock) {
|
|
365
|
+
lock = new Semaphore(1);
|
|
366
|
+
this.#jobLocks.set(jobId, lock);
|
|
367
|
+
}
|
|
368
|
+
await lock.acquire();
|
|
369
|
+
try {
|
|
370
|
+
return await fn();
|
|
371
|
+
} finally {
|
|
372
|
+
lock.release();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Returns a string key based on @param contractAddress and @param eventSelector.
|
|
378
|
+
*
|
|
379
|
+
* The returned key is meant to be used when interacting with index #eventsByContractAndEventSelector.
|
|
380
|
+
*/
|
|
381
|
+
#keyFor(contractAddress: AztecAddress, eventSelector: EventSelector): string {
|
|
382
|
+
return `${contractAddress.toString()}_${eventSelector.toString()}`;
|
|
383
|
+
}
|
|
324
384
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
3
|
+
import { EventSelector } from '@aztec/stdlib/abi';
|
|
4
|
+
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
6
|
+
import { TxHash } from '@aztec/stdlib/tx';
|
|
7
|
+
|
|
8
|
+
/** Serializable private event entry with scope tracking. */
|
|
9
|
+
export class StoredPrivateEvent {
|
|
10
|
+
constructor(
|
|
11
|
+
readonly randomness: Fr,
|
|
12
|
+
readonly msgContent: Fr[],
|
|
13
|
+
readonly l2BlockNumber: number,
|
|
14
|
+
readonly l2BlockHash: BlockHash,
|
|
15
|
+
readonly txHash: TxHash,
|
|
16
|
+
readonly txIndexInBlock: number,
|
|
17
|
+
readonly eventIndexInTx: number,
|
|
18
|
+
readonly contractAddress: AztecAddress,
|
|
19
|
+
readonly eventSelector: EventSelector,
|
|
20
|
+
readonly scopes: Set<string>,
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
addScope(scope: string) {
|
|
24
|
+
this.scopes.add(scope);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toBuffer(): Buffer {
|
|
28
|
+
const scopesArray = [...this.scopes];
|
|
29
|
+
return serializeToBuffer(
|
|
30
|
+
this.randomness,
|
|
31
|
+
this.msgContent.length,
|
|
32
|
+
...this.msgContent,
|
|
33
|
+
this.l2BlockNumber,
|
|
34
|
+
this.l2BlockHash,
|
|
35
|
+
this.txHash,
|
|
36
|
+
this.txIndexInBlock,
|
|
37
|
+
this.eventIndexInTx,
|
|
38
|
+
this.contractAddress,
|
|
39
|
+
this.eventSelector.toBuffer(),
|
|
40
|
+
scopesArray.length,
|
|
41
|
+
...scopesArray,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static fromBuffer(buffer: Buffer): StoredPrivateEvent {
|
|
46
|
+
const reader = BufferReader.asReader(buffer);
|
|
47
|
+
|
|
48
|
+
const randomness = Fr.fromBuffer(reader);
|
|
49
|
+
const msgContentLength = reader.readNumber();
|
|
50
|
+
const msgContent = reader.readArray(msgContentLength, Fr);
|
|
51
|
+
const l2BlockNumber = reader.readNumber();
|
|
52
|
+
const l2BlockHash = new BlockHash(Fr.fromBuffer(reader));
|
|
53
|
+
const txHash = TxHash.fromBuffer(reader);
|
|
54
|
+
const txIndexInBlock = reader.readNumber();
|
|
55
|
+
const eventIndexInTx = reader.readNumber();
|
|
56
|
+
const contractAddress = AztecAddress.fromBuffer(reader);
|
|
57
|
+
const eventSelector = EventSelector.fromBuffer(reader);
|
|
58
|
+
const scopes = reader.readVector({ fromBuffer: (r: BufferReader) => r.readString() });
|
|
59
|
+
|
|
60
|
+
return new StoredPrivateEvent(
|
|
61
|
+
randomness,
|
|
62
|
+
msgContent,
|
|
63
|
+
l2BlockNumber,
|
|
64
|
+
l2BlockHash,
|
|
65
|
+
txHash,
|
|
66
|
+
txIndexInBlock,
|
|
67
|
+
eventIndexInTx,
|
|
68
|
+
contractAddress,
|
|
69
|
+
eventSelector,
|
|
70
|
+
new Set(scopes),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|