@aztec/pxe 0.0.1-commit.3469e52 → 0.0.1-commit.54489865
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 +7 -5
- package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
- package/dest/block_synchronizer/block_synchronizer.js +56 -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 +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +5 -8
- 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 +13 -10
- package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.d.ts +5 -3
- package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.js +27 -17
- 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 +9 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +16 -5
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +20 -12
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +36 -38
- package/dest/contract_sync/index.d.ts +23 -0
- package/dest/contract_sync/index.d.ts.map +1 -0
- package/dest/contract_sync/index.js +54 -0
- package/dest/debug/pxe_debug_utils.d.ts +16 -6
- package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
- package/dest/debug/pxe_debug_utils.js +20 -10
- 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 +10 -5
- 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 +11 -6
- 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 +2 -2
- package/dest/events/event_service.d.ts.map +1 -1
- package/dest/events/event_service.js +1 -1
- 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 +3 -2
- package/dest/logs/log_service.d.ts.map +1 -1
- package/dest/logs/log_service.js +19 -13
- package/dest/notes/note_service.d.ts +4 -3
- package/dest/notes/note_service.d.ts.map +1 -1
- package/dest/notes/note_service.js +11 -10
- 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 +1 -1
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +18 -14
- 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 +0 -12
- package/dest/storage/note_store/note_store.d.ts +43 -55
- package/dest/storage/note_store/note_store.d.ts.map +1 -1
- package/dest/storage/note_store/note_store.js +235 -251
- 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 +158 -132
- 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/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 +16 -16
- package/src/bin/check_oracle_version.ts +1 -0
- package/src/block_synchronizer/block_synchronizer.ts +70 -33
- package/src/config/index.ts +14 -0
- package/src/contract_function_simulator/contract_function_simulator.ts +7 -11
- 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 +18 -9
- package/src/contract_function_simulator/oracle/oracle.ts +34 -21
- package/src/contract_function_simulator/oracle/private_execution.ts +1 -60
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +32 -6
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +55 -45
- package/src/contract_sync/index.ts +98 -0
- package/src/debug/pxe_debug_utils.ts +26 -11
- package/src/entrypoints/client/bundle/utils.ts +4 -13
- package/src/entrypoints/client/lazy/utils.ts +5 -13
- 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 +1 -1
- package/src/job_coordinator/job_coordinator.ts +4 -3
- package/src/logs/log_service.ts +30 -11
- package/src/notes/note_service.ts +18 -15
- 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 +48 -16
- package/src/storage/contract_store/contract_store.ts +0 -20
- package/src/storage/note_store/note_store.ts +273 -312
- package/src/storage/note_store/stored_note.ts +48 -0
- package/src/storage/private_event_store/private_event_store.ts +204 -167
- package/src/storage/private_event_store/stored_private_event.ts +73 -0
- 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,16 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { toArray } from '@aztec/foundation/iterable';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
5
|
+
import { Semaphore } from '@aztec/foundation/queue';
|
|
5
6
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
|
|
6
7
|
import type { EventSelector } from '@aztec/stdlib/abi';
|
|
7
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
8
|
-
import {
|
|
9
|
-
import { type InTx, TxHash } from '@aztec/stdlib/tx';
|
|
9
|
+
import type { InTx, TxHash } from '@aztec/stdlib/tx';
|
|
10
10
|
|
|
11
11
|
import type { StagedStore } from '../../job_coordinator/job_coordinator.js';
|
|
12
12
|
import type { PackedPrivateEvent } from '../../pxe.js';
|
|
13
|
+
import { StoredPrivateEvent } from './stored_private_event.js';
|
|
13
14
|
|
|
14
15
|
export type PrivateEventStoreFilter = {
|
|
15
16
|
contractAddress: AztecAddress;
|
|
@@ -19,20 +20,6 @@ export type PrivateEventStoreFilter = {
|
|
|
19
20
|
txHash?: TxHash;
|
|
20
21
|
};
|
|
21
22
|
|
|
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
23
|
type PrivateEventMetadata = InTx & {
|
|
37
24
|
contractAddress: AztecAddress;
|
|
38
25
|
scope: AztecAddress;
|
|
@@ -49,104 +36,29 @@ export class PrivateEventStore implements StagedStore {
|
|
|
49
36
|
readonly storeName: string = 'private_event';
|
|
50
37
|
|
|
51
38
|
#store: AztecAsyncKVStore;
|
|
52
|
-
/**
|
|
53
|
-
#
|
|
54
|
-
/** Multi-map from
|
|
55
|
-
#
|
|
39
|
+
/** Actual private event log entries, keyed by siloedEventCommitment */
|
|
40
|
+
#events: AztecAsyncMap<string, Buffer>;
|
|
41
|
+
/** Multi-map from contractAddress_eventSelector to siloedEventCommitment for efficient lookup */
|
|
42
|
+
#eventsByContractAndEventSelector: AztecAsyncMultiMap<string, string>;
|
|
56
43
|
/** Multi-map from block number to siloedEventCommitment for rollback support */
|
|
57
44
|
#eventsByBlockNumber: AztecAsyncMultiMap<number, string>;
|
|
58
|
-
/** Map from siloedEventCommitment to boolean indicating if log has been seen. */
|
|
59
|
-
#seenLogs: AztecAsyncMap<string, boolean>;
|
|
60
45
|
|
|
61
|
-
/** jobId => eventId (event siloed nullifier)
|
|
62
|
-
#
|
|
46
|
+
/** jobId => eventId (event siloed nullifier) => StoredPrivateEvent */
|
|
47
|
+
#eventsForJob: Map<string, Map<string, StoredPrivateEvent>>;
|
|
48
|
+
|
|
49
|
+
/** Per-job locks to prevent concurrent writes from affecting each other. */
|
|
50
|
+
#jobLocks: Map<string, Semaphore>;
|
|
63
51
|
|
|
64
52
|
logger = createLogger('private_event_store');
|
|
65
53
|
|
|
66
54
|
constructor(store: AztecAsyncKVStore) {
|
|
67
55
|
this.#store = store;
|
|
68
|
-
this.#
|
|
69
|
-
this.#
|
|
70
|
-
this.#seenLogs = this.#store.openMap('seen_logs');
|
|
56
|
+
this.#events = this.#store.openMap('private_event_logs');
|
|
57
|
+
this.#eventsByContractAndEventSelector = this.#store.openMultiMap('events_by_contract_selector');
|
|
71
58
|
this.#eventsByBlockNumber = this.#store.openMultiMap('events_by_block_number');
|
|
72
59
|
|
|
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
|
-
}
|
|
60
|
+
this.#eventsForJob = new Map();
|
|
61
|
+
this.#jobLocks = new Map();
|
|
150
62
|
}
|
|
151
63
|
|
|
152
64
|
/**
|
|
@@ -168,39 +80,43 @@ export class PrivateEventStore implements StagedStore {
|
|
|
168
80
|
siloedEventCommitment: Fr,
|
|
169
81
|
metadata: PrivateEventMetadata,
|
|
170
82
|
jobId: string,
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return this.#store.transactionAsync(async () => {
|
|
175
|
-
const key = this.#keyFor(contractAddress, scope, eventSelector);
|
|
176
|
-
|
|
177
|
-
// The siloed event commitment is guaranteed to be unique as it's inserted into the nullifier tree. For this
|
|
178
|
-
// reason we use it as id.
|
|
83
|
+
) {
|
|
84
|
+
return this.#withJobLock(jobId, async () => {
|
|
85
|
+
const { contractAddress, scope, txHash, l2BlockNumber, l2BlockHash, txIndexInBlock, eventIndexInTx } = metadata;
|
|
179
86
|
const eventId = siloedEventCommitment.toString();
|
|
180
87
|
|
|
181
|
-
const hasBeenSeen = await this.#isSeenLog(jobId, eventId);
|
|
182
|
-
if (hasBeenSeen) {
|
|
183
|
-
this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), siloedEventCommitment });
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
88
|
this.logger.verbose('storing private event log (job stage)', {
|
|
89
|
+
eventId,
|
|
188
90
|
contractAddress,
|
|
189
91
|
scope,
|
|
190
92
|
msgContent,
|
|
191
93
|
l2BlockNumber,
|
|
192
94
|
});
|
|
193
95
|
|
|
194
|
-
this.#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
}
|
|
204
120
|
});
|
|
205
121
|
}
|
|
206
122
|
|
|
@@ -226,40 +142,51 @@ export class PrivateEventStore implements StagedStore {
|
|
|
226
142
|
event: PackedPrivateEvent;
|
|
227
143
|
}> = [];
|
|
228
144
|
|
|
229
|
-
|
|
230
|
-
|
|
145
|
+
const key = this.#keyFor(filter.contractAddress, eventSelector);
|
|
146
|
+
const targetScopes = new Set(filter.scopes.map(s => s.toString()));
|
|
231
147
|
|
|
232
|
-
|
|
233
|
-
const entry = await this.#getEventLogBySiloedNullifier(eventId);
|
|
148
|
+
const eventIds: string[] = await toArray(this.#eventsByContractAndEventSelector.getValuesAsync(key));
|
|
234
149
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
150
|
+
for (const eventId of eventIds) {
|
|
151
|
+
const eventBuffer = await this.#events.getAsync(eventId);
|
|
238
152
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
153
|
+
// Defensive, if it happens, there's a problem with how we're handling #eventsByContractAndEventSelector
|
|
154
|
+
if (!eventBuffer) {
|
|
155
|
+
this.logger.verbose(
|
|
156
|
+
`EventId ${eventId} does not exist in main index but it is referenced from contract event selector index`,
|
|
157
|
+
);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
245
160
|
|
|
246
|
-
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
161
|
+
const storedPrivateEvent = StoredPrivateEvent.fromBuffer(eventBuffer);
|
|
249
162
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
l2BlockHash,
|
|
259
|
-
eventSelector,
|
|
260
|
-
},
|
|
261
|
-
});
|
|
163
|
+
// Filter by block range
|
|
164
|
+
if (storedPrivateEvent.l2BlockNumber < filter.fromBlock || storedPrivateEvent.l2BlockNumber >= filter.toBlock) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Filter by scopes
|
|
169
|
+
if (storedPrivateEvent.scopes.intersection(targetScopes).size === 0) {
|
|
170
|
+
continue;
|
|
262
171
|
}
|
|
172
|
+
|
|
173
|
+
// Filter by txHash
|
|
174
|
+
if (filter.txHash && !storedPrivateEvent.txHash.equals(filter.txHash)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
events.push({
|
|
179
|
+
l2BlockNumber: storedPrivateEvent.l2BlockNumber,
|
|
180
|
+
txIndexInBlock: storedPrivateEvent.txIndexInBlock,
|
|
181
|
+
eventIndexInTx: storedPrivateEvent.eventIndexInTx,
|
|
182
|
+
event: {
|
|
183
|
+
packedEvent: storedPrivateEvent.msgContent,
|
|
184
|
+
l2BlockNumber: BlockNumber(storedPrivateEvent.l2BlockNumber),
|
|
185
|
+
txHash: storedPrivateEvent.txHash,
|
|
186
|
+
l2BlockHash: storedPrivateEvent.l2BlockHash,
|
|
187
|
+
eventSelector,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
263
190
|
}
|
|
264
191
|
|
|
265
192
|
// Sort by block number, then by tx index within block, then by event index within tx
|
|
@@ -272,6 +199,7 @@ export class PrivateEventStore implements StagedStore {
|
|
|
272
199
|
}
|
|
273
200
|
return a.eventIndexInTx - b.eventIndexInTx;
|
|
274
201
|
});
|
|
202
|
+
|
|
275
203
|
return events.map(ev => ev.event);
|
|
276
204
|
}
|
|
277
205
|
|
|
@@ -296,23 +224,23 @@ export class PrivateEventStore implements StagedStore {
|
|
|
296
224
|
let removedCount = 0;
|
|
297
225
|
|
|
298
226
|
for (let block = blockNumber + 1; block <= synchedBlockNumber; block++) {
|
|
299
|
-
const eventIds: string[] =
|
|
300
|
-
for await (const eventId of this.#eventsByBlockNumber.getValuesAsync(block)) {
|
|
301
|
-
eventIds.push(eventId);
|
|
302
|
-
}
|
|
227
|
+
const eventIds: string[] = await toArray(this.#eventsByBlockNumber.getValuesAsync(block));
|
|
303
228
|
|
|
304
229
|
if (eventIds.length > 0) {
|
|
305
230
|
await this.#eventsByBlockNumber.delete(block);
|
|
306
231
|
|
|
307
232
|
for (const eventId of eventIds) {
|
|
308
|
-
const
|
|
309
|
-
if (!
|
|
310
|
-
throw new Error(`Event
|
|
233
|
+
const buffer = await this.#events.getAsync(eventId);
|
|
234
|
+
if (!buffer) {
|
|
235
|
+
throw new Error(`Event not found for eventId ${eventId}`);
|
|
311
236
|
}
|
|
312
237
|
|
|
313
|
-
|
|
314
|
-
await this.#
|
|
315
|
-
await this.#
|
|
238
|
+
const entry = StoredPrivateEvent.fromBuffer(buffer);
|
|
239
|
+
await this.#events.delete(eventId);
|
|
240
|
+
await this.#eventsByContractAndEventSelector.deleteValue(
|
|
241
|
+
this.#keyFor(entry.contractAddress, entry.eventSelector),
|
|
242
|
+
eventId,
|
|
243
|
+
);
|
|
316
244
|
|
|
317
245
|
removedCount++;
|
|
318
246
|
}
|
|
@@ -321,4 +249,113 @@ export class PrivateEventStore implements StagedStore {
|
|
|
321
249
|
|
|
322
250
|
this.logger.verbose(`Rolled back ${removedCount} private events after block ${blockNumber}`);
|
|
323
251
|
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Commits in memory job data to persistent storage.
|
|
255
|
+
*
|
|
256
|
+
* Called by JobCoordinator when a job completes successfully.
|
|
257
|
+
*
|
|
258
|
+
* Note: JobCoordinator wraps all commits in a single transaction, so we don't need our own transactionAsync here
|
|
259
|
+
* (and using one would throw on IndexedDB as it does not support nested txs).
|
|
260
|
+
*
|
|
261
|
+
* @param jobId - The jobId identifying which staged data to commit
|
|
262
|
+
*/
|
|
263
|
+
commit(jobId: string): Promise<void> {
|
|
264
|
+
return this.#withJobLock(jobId, async () => {
|
|
265
|
+
for (const [eventId, entry] of this.#getEventsForJob(jobId).entries()) {
|
|
266
|
+
const lookupKey = this.#keyFor(entry.contractAddress, entry.eventSelector);
|
|
267
|
+
this.logger.verbose('storing private event log', { eventId, lookupKey });
|
|
268
|
+
|
|
269
|
+
await Promise.all([
|
|
270
|
+
this.#events.set(eventId, entry.toBuffer()),
|
|
271
|
+
this.#eventsByContractAndEventSelector.set(lookupKey, eventId),
|
|
272
|
+
this.#eventsByBlockNumber.set(entry.l2BlockNumber, eventId),
|
|
273
|
+
]);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.#clearJobData(jobId);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Discards in memory job data without persisting it.
|
|
282
|
+
*/
|
|
283
|
+
discardStaged(jobId: string): Promise<void> {
|
|
284
|
+
return this.#withJobLock(jobId, () => Promise.resolve(this.#clearJobData(jobId)));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Reads an event from in-memory job data first, falling back to persistent storage if not found.
|
|
289
|
+
*
|
|
290
|
+
* Returns undefined if the event does not exist in the store overall.
|
|
291
|
+
*/
|
|
292
|
+
async #readEvent(eventId: string, jobId: string): Promise<StoredPrivateEvent | undefined> {
|
|
293
|
+
const eventForJob = this.#getEventsForJob(jobId).get(eventId);
|
|
294
|
+
if (eventForJob) {
|
|
295
|
+
return eventForJob;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const buffer = await this.#events.getAsync(eventId);
|
|
299
|
+
return buffer ? StoredPrivateEvent.fromBuffer(buffer) : undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Writes an event to in-memory job data.
|
|
304
|
+
*
|
|
305
|
+
* Writes are only allowed in a job context. Events modified during a job will only be persisted when `commit` is
|
|
306
|
+
* called.
|
|
307
|
+
*/
|
|
308
|
+
#writeEvent(eventId: string, entry: StoredPrivateEvent, jobId: string) {
|
|
309
|
+
this.#getEventsForJob(jobId).set(eventId, entry);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get in-memory data only visible to @param jobId
|
|
314
|
+
*/
|
|
315
|
+
#getEventsForJob(jobId: string): Map<string, StoredPrivateEvent> {
|
|
316
|
+
let eventsForJob = this.#eventsForJob.get(jobId);
|
|
317
|
+
if (eventsForJob === undefined) {
|
|
318
|
+
eventsForJob = new Map();
|
|
319
|
+
this.#eventsForJob.set(jobId, eventsForJob);
|
|
320
|
+
}
|
|
321
|
+
return eventsForJob;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Clear data structures supporting a specific job.
|
|
326
|
+
*/
|
|
327
|
+
#clearJobData(jobId: string) {
|
|
328
|
+
this.#eventsForJob.delete(jobId);
|
|
329
|
+
this.#jobLocks.delete(jobId);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Ensures a function can only run once it acquires a unique per-job lock, and handles proper lock release after it
|
|
334
|
+
* runs.
|
|
335
|
+
*
|
|
336
|
+
* This primitive allows concurrent writes on this store without risking data corruption due to unsound write
|
|
337
|
+
* interleaving.
|
|
338
|
+
*/
|
|
339
|
+
async #withJobLock<T>(jobId: string, fn: () => Promise<T>): Promise<T> {
|
|
340
|
+
let lock = this.#jobLocks.get(jobId);
|
|
341
|
+
if (!lock) {
|
|
342
|
+
lock = new Semaphore(1);
|
|
343
|
+
this.#jobLocks.set(jobId, lock);
|
|
344
|
+
}
|
|
345
|
+
await lock.acquire();
|
|
346
|
+
try {
|
|
347
|
+
return await fn();
|
|
348
|
+
} finally {
|
|
349
|
+
lock.release();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Returns a string key based on @param contractAddress and @param eventSelector.
|
|
355
|
+
*
|
|
356
|
+
* The returned key is meant to be used when interacting with index #eventsByContractAndEventSelector.
|
|
357
|
+
*/
|
|
358
|
+
#keyFor(contractAddress: AztecAddress, eventSelector: EventSelector): string {
|
|
359
|
+
return `${contractAddress.toString()}_${eventSelector.toString()}`;
|
|
360
|
+
}
|
|
324
361
|
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
2
|
+
import type { BlockHash } from '@aztec/stdlib/block';
|
|
3
|
+
import { MAX_LOGS_PER_TAG } from '@aztec/stdlib/interfaces/api-limit';
|
|
4
|
+
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
5
|
+
import type { SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generic pagination helper that fetches all pages of results.
|
|
9
|
+
* @param numTags - The number of tags being queried (determines result array size).
|
|
10
|
+
* @param fetchPage - Function that fetches a single page of results given a page number.
|
|
11
|
+
* @returns An array of arrays, one per tag, containing all results across all pages.
|
|
12
|
+
*/
|
|
13
|
+
async function getAllPages<T>(numTags: number, fetchPage: (page: number) => Promise<T[][]>): Promise<T[][]> {
|
|
14
|
+
const allResultsPerTag: T[][] = Array.from({ length: numTags }, () => []);
|
|
15
|
+
let page = 0;
|
|
16
|
+
let hasMore = true;
|
|
17
|
+
|
|
18
|
+
while (hasMore) {
|
|
19
|
+
const resultsPage = await fetchPage(page);
|
|
20
|
+
hasMore = false;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < resultsPage.length; i++) {
|
|
23
|
+
allResultsPerTag[i].push(...resultsPage[i]);
|
|
24
|
+
if (resultsPage[i].length === MAX_LOGS_PER_TAG) {
|
|
25
|
+
hasMore = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
page++;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return allResultsPerTag;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetches all private logs for the given tags, automatically paginating through all pages.
|
|
36
|
+
* @param aztecNode - The Aztec node to query.
|
|
37
|
+
* @param tags - The siloed tags to search for.
|
|
38
|
+
* @param anchorBlockHash - reference block for the Aztec node query, throws if block is not found there (typically
|
|
39
|
+
* because of reorgs).
|
|
40
|
+
* @returns An array of log arrays, one per tag, containing all logs across all pages.
|
|
41
|
+
*/
|
|
42
|
+
export function getAllPrivateLogsByTags(
|
|
43
|
+
aztecNode: AztecNode,
|
|
44
|
+
tags: SiloedTag[],
|
|
45
|
+
anchorBlockHash: BlockHash,
|
|
46
|
+
): Promise<TxScopedL2Log[][]> {
|
|
47
|
+
return getAllPages(tags.length, page => aztecNode.getPrivateLogsByTags(tags, page, anchorBlockHash));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fetches all public logs for the given tags from a contract, automatically paginating through all pages.
|
|
52
|
+
* @param aztecNode - The Aztec node to query.
|
|
53
|
+
* @param contractAddress - The contract address to search logs for.
|
|
54
|
+
* @param tags - The tags to search for.
|
|
55
|
+
* @param anchorBlockHash - reference block for the Aztec node query, throws if block is not found there (typically
|
|
56
|
+
* because of reorgs).
|
|
57
|
+
* @returns An array of log arrays, one per tag, containing all logs across all pages.
|
|
58
|
+
*/
|
|
59
|
+
export function getAllPublicLogsByTagsFromContract(
|
|
60
|
+
aztecNode: AztecNode,
|
|
61
|
+
contractAddress: AztecAddress,
|
|
62
|
+
tags: Tag[],
|
|
63
|
+
anchorBlockHash: BlockHash,
|
|
64
|
+
): Promise<TxScopedL2Log[][]> {
|
|
65
|
+
return getAllPages(tags.length, page =>
|
|
66
|
+
aztecNode.getPublicLogsByTagsFromContract(contractAddress, tags, page, anchorBlockHash),
|
|
67
|
+
);
|
|
68
|
+
}
|
package/src/tagging/index.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
export { loadPrivateLogsForSenderRecipientPair } from './recipient_sync/load_private_logs_for_sender_recipient_pair.js';
|
|
13
13
|
export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
|
|
14
14
|
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
|
|
15
|
+
export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
|
|
15
16
|
|
|
16
17
|
// Re-export tagging-related types from stdlib
|
|
17
18
|
export { DirectionalAppTaggingSecret, Tag, SiloedTag } from '@aztec/stdlib/logs';
|