@aztec/pxe 0.0.1-commit.dbf9cec → 0.0.1-commit.df81a97b5
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/config/index.d.ts +2 -2
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.d.ts +9 -3
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +28 -6
- package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts +5 -5
- package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts.map +1 -1
- package/dest/contract_function_simulator/execution_tagging_index_cache.js +17 -9
- package/dest/contract_function_simulator/index.d.ts +2 -1
- package/dest/contract_function_simulator/index.d.ts.map +1 -1
- package/dest/contract_function_simulator/index.js +1 -0
- package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +2 -3
- package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts.map +1 -1
- package/dest/contract_function_simulator/noir-structs/event_validation_request.js +5 -4
- package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
- package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts.map +1 -1
- package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -3
- package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts +16 -0
- package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts.map +1 -0
- package/dest/contract_function_simulator/noir-structs/message_tx_context.js +57 -0
- package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +2 -4
- package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts.map +1 -1
- package/dest/contract_function_simulator/noir-structs/note_validation_request.js +3 -5
- package/dest/contract_function_simulator/oracle/interfaces.d.ts +50 -45
- package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +9 -0
- package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -0
- package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +42 -0
- package/dest/contract_function_simulator/oracle/oracle.d.ts +45 -44
- package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.js +163 -94
- package/dest/contract_function_simulator/oracle/private_execution.js +5 -3
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +22 -47
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +30 -62
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +53 -35
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +101 -43
- package/dest/contract_sync/contract_sync_service.d.ts +5 -3
- package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
- package/dest/contract_sync/contract_sync_service.js +47 -30
- package/dest/messages/message_context_service.d.ts +17 -0
- package/dest/messages/message_context_service.d.ts.map +1 -0
- package/dest/messages/message_context_service.js +36 -0
- package/dest/oracle_version.d.ts +2 -2
- package/dest/oracle_version.js +3 -3
- package/dest/pxe.d.ts +8 -4
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +39 -22
- package/dest/storage/metadata.d.ts +1 -1
- package/dest/storage/metadata.js +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.d.ts +26 -25
- package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
- package/dest/storage/tagging_store/sender_tagging_store.js +141 -115
- package/dest/tagging/index.d.ts +2 -2
- package/dest/tagging/index.d.ts.map +1 -1
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +1 -1
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
- package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +10 -1
- package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +4 -3
- 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 +20 -10
- package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +2 -1
- 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 +24 -11
- package/package.json +16 -16
- package/src/config/index.ts +1 -1
- package/src/contract_function_simulator/contract_function_simulator.ts +39 -7
- package/src/contract_function_simulator/execution_tagging_index_cache.ts +16 -11
- package/src/contract_function_simulator/index.ts +1 -0
- package/src/contract_function_simulator/noir-structs/event_validation_request.ts +8 -5
- package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -4
- package/src/contract_function_simulator/noir-structs/message_tx_context.ts +55 -0
- package/src/contract_function_simulator/noir-structs/note_validation_request.ts +3 -6
- package/src/contract_function_simulator/oracle/interfaces.ts +54 -54
- package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +135 -0
- package/src/contract_function_simulator/oracle/oracle.ts +176 -138
- package/src/contract_function_simulator/oracle/private_execution.ts +4 -4
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +32 -80
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +136 -53
- package/src/contract_sync/contract_sync_service.ts +67 -38
- package/src/messages/message_context_service.ts +45 -0
- package/src/oracle_version.ts +3 -3
- package/src/pxe.ts +57 -21
- package/src/storage/metadata.ts +1 -1
- package/src/storage/tagging_store/sender_tagging_store.ts +182 -135
- package/src/tagging/index.ts +1 -1
- package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +19 -1
- package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +26 -11
- package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +19 -9
|
@@ -6,24 +6,27 @@ import { Point } from '@aztec/foundation/curves/grumpkin';
|
|
|
6
6
|
import { LogLevels, type Logger, createLogger } from '@aztec/foundation/log';
|
|
7
7
|
import type { MembershipWitness } from '@aztec/foundation/trees';
|
|
8
8
|
import type { KeyStore } from '@aztec/key-store';
|
|
9
|
+
import { isProtocolContract } from '@aztec/protocol-contracts';
|
|
9
10
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
10
11
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
11
12
|
import { BlockHash } from '@aztec/stdlib/block';
|
|
12
|
-
import type { CompleteAddress, ContractInstance } from '@aztec/stdlib/contract';
|
|
13
|
+
import type { CompleteAddress, ContractInstance, PartialAddress } from '@aztec/stdlib/contract';
|
|
13
14
|
import { siloNullifier } from '@aztec/stdlib/hash';
|
|
14
15
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
15
16
|
import type { KeyValidationRequest } from '@aztec/stdlib/kernel';
|
|
16
|
-
import { computeAddressSecret } from '@aztec/stdlib/keys';
|
|
17
|
+
import { type PublicKeys, computeAddressSecret } from '@aztec/stdlib/keys';
|
|
17
18
|
import { deriveEcdhSharedSecret } from '@aztec/stdlib/logs';
|
|
18
19
|
import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
|
|
19
20
|
import type { NoteStatus } from '@aztec/stdlib/note';
|
|
20
21
|
import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
21
|
-
import type { BlockHeader, Capsule } from '@aztec/stdlib/tx';
|
|
22
|
+
import type { BlockHeader, Capsule, OffchainEffect } from '@aztec/stdlib/tx';
|
|
22
23
|
|
|
23
24
|
import type { AccessScopes } from '../../access_scopes.js';
|
|
24
25
|
import { createContractLogger, logContractMessage } from '../../contract_logging.js';
|
|
26
|
+
import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
|
|
25
27
|
import { EventService } from '../../events/event_service.js';
|
|
26
28
|
import { LogService } from '../../logs/log_service.js';
|
|
29
|
+
import { MessageContextService } from '../../messages/message_context_service.js';
|
|
27
30
|
import { NoteService } from '../../notes/note_service.js';
|
|
28
31
|
import { ORACLE_VERSION } from '../../oracle_version.js';
|
|
29
32
|
import type { AddressStore } from '../../storage/address_store/address_store.js';
|
|
@@ -36,6 +39,7 @@ import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_
|
|
|
36
39
|
import { EventValidationRequest } from '../noir-structs/event_validation_request.js';
|
|
37
40
|
import { LogRetrievalRequest } from '../noir-structs/log_retrieval_request.js';
|
|
38
41
|
import { LogRetrievalResponse } from '../noir-structs/log_retrieval_response.js';
|
|
42
|
+
import { MessageTxContext } from '../noir-structs/message_tx_context.js';
|
|
39
43
|
import { NoteValidationRequest } from '../noir-structs/note_validation_request.js';
|
|
40
44
|
import { UtilityContext } from '../noir-structs/utility_context.js';
|
|
41
45
|
import { pickNotes } from '../pick_notes.js';
|
|
@@ -58,6 +62,8 @@ export type UtilityExecutionOracleArgs = {
|
|
|
58
62
|
senderAddressBookStore: SenderAddressBookStore;
|
|
59
63
|
capsuleStore: CapsuleStore;
|
|
60
64
|
privateEventStore: PrivateEventStore;
|
|
65
|
+
messageContextService: MessageContextService;
|
|
66
|
+
contractSyncService: ContractSyncService;
|
|
61
67
|
jobId: string;
|
|
62
68
|
log?: ReturnType<typeof createLogger>;
|
|
63
69
|
scopes: AccessScopes;
|
|
@@ -71,6 +77,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
71
77
|
isUtility = true as const;
|
|
72
78
|
|
|
73
79
|
private contractLogger: Logger | undefined;
|
|
80
|
+
private offchainEffects: OffchainEffect[] = [];
|
|
74
81
|
|
|
75
82
|
protected readonly contractAddress: AztecAddress;
|
|
76
83
|
protected readonly authWitnesses: AuthWitness[];
|
|
@@ -85,8 +92,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
85
92
|
protected readonly senderAddressBookStore: SenderAddressBookStore;
|
|
86
93
|
protected readonly capsuleStore: CapsuleStore;
|
|
87
94
|
protected readonly privateEventStore: PrivateEventStore;
|
|
95
|
+
protected readonly messageContextService: MessageContextService;
|
|
96
|
+
protected readonly contractSyncService: ContractSyncService;
|
|
88
97
|
protected readonly jobId: string;
|
|
89
|
-
protected
|
|
98
|
+
protected logger: ReturnType<typeof createLogger>;
|
|
90
99
|
protected readonly scopes: AccessScopes;
|
|
91
100
|
|
|
92
101
|
constructor(args: UtilityExecutionOracleArgs) {
|
|
@@ -103,22 +112,39 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
103
112
|
this.senderAddressBookStore = args.senderAddressBookStore;
|
|
104
113
|
this.capsuleStore = args.capsuleStore;
|
|
105
114
|
this.privateEventStore = args.privateEventStore;
|
|
115
|
+
this.messageContextService = args.messageContextService;
|
|
116
|
+
this.contractSyncService = args.contractSyncService;
|
|
106
117
|
this.jobId = args.jobId;
|
|
107
|
-
this.
|
|
118
|
+
this.logger = args.log ?? createLogger('simulator:client_view_context');
|
|
108
119
|
this.scopes = args.scopes;
|
|
109
120
|
}
|
|
110
121
|
|
|
111
|
-
public
|
|
122
|
+
public assertCompatibleOracleVersion(version: number): void {
|
|
123
|
+
// TODO(F-416): Remove this hack on v5 when protocol contracts are redeployed.
|
|
124
|
+
// Protocol contracts/canonical contracts shipped with committed bytecode that cannot be changed. Assert they use
|
|
125
|
+
// the expected pinned version or the current one. We want to allow for both the pinned and the current versions
|
|
126
|
+
// because we want this code to work with both the pinned and unpinned version since some branches do not have the
|
|
127
|
+
// pinned contracts (like e.g. next)
|
|
128
|
+
const LEGACY_ORACLE_VERSION = 12;
|
|
129
|
+
if (isProtocolContract(this.contractAddress)) {
|
|
130
|
+
if (version !== LEGACY_ORACLE_VERSION && version !== ORACLE_VERSION) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Expected legacy oracle version ${LEGACY_ORACLE_VERSION} or current oracle version ${ORACLE_VERSION} for alpha payload contract at ${this.contractAddress}, got ${version}.`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
112
138
|
if (version !== ORACLE_VERSION) {
|
|
113
139
|
throw new Error(`Incompatible oracle version. Expected version ${ORACLE_VERSION}, got ${version}.`);
|
|
114
140
|
}
|
|
115
141
|
}
|
|
116
142
|
|
|
117
|
-
public
|
|
143
|
+
public getRandomField(): Fr {
|
|
118
144
|
return Fr.random();
|
|
119
145
|
}
|
|
120
146
|
|
|
121
|
-
public
|
|
147
|
+
public getUtilityContext(): UtilityContext {
|
|
122
148
|
return new UtilityContext(this.anchorBlockHeader, this.contractAddress);
|
|
123
149
|
}
|
|
124
150
|
|
|
@@ -129,7 +155,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
129
155
|
* @throws If the keys are not registered in the key store.
|
|
130
156
|
* @throws If scopes are defined and the account is not in the scopes.
|
|
131
157
|
*/
|
|
132
|
-
public async
|
|
158
|
+
public async getKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
|
|
133
159
|
// If scopes are defined, check that the key belongs to an account in the scopes.
|
|
134
160
|
if (this.scopes !== 'ALL_SCOPES' && this.scopes.length > 0) {
|
|
135
161
|
let hasAccess = false;
|
|
@@ -152,7 +178,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
152
178
|
* @param noteHash - The note hash to find in the note hash tree.
|
|
153
179
|
* @returns The membership witness containing the leaf index and sibling path
|
|
154
180
|
*/
|
|
155
|
-
public
|
|
181
|
+
public getNoteHashMembershipWitness(
|
|
156
182
|
anchorBlockHash: BlockHash,
|
|
157
183
|
noteHash: Fr,
|
|
158
184
|
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
@@ -170,7 +196,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
170
196
|
* @param blockHash - The block hash to find in the archive tree.
|
|
171
197
|
* @returns The membership witness containing the leaf index and sibling path
|
|
172
198
|
*/
|
|
173
|
-
public
|
|
199
|
+
public getBlockHashMembershipWitness(
|
|
174
200
|
anchorBlockHash: BlockHash,
|
|
175
201
|
blockHash: BlockHash,
|
|
176
202
|
): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
|
|
@@ -183,7 +209,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
183
209
|
* @param nullifier - Nullifier we try to find witness for.
|
|
184
210
|
* @returns The nullifier membership witness (if found).
|
|
185
211
|
*/
|
|
186
|
-
public
|
|
212
|
+
public getNullifierMembershipWitness(
|
|
187
213
|
blockHash: BlockHash,
|
|
188
214
|
nullifier: Fr,
|
|
189
215
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
@@ -199,7 +225,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
199
225
|
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
200
226
|
* we are trying to prove non-inclusion for.
|
|
201
227
|
*/
|
|
202
|
-
public
|
|
228
|
+
public getLowNullifierMembershipWitness(
|
|
203
229
|
blockHash: BlockHash,
|
|
204
230
|
nullifier: Fr,
|
|
205
231
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
@@ -212,7 +238,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
212
238
|
* @param leafSlot - The slot of the public data tree to get the witness for.
|
|
213
239
|
* @returns - The witness
|
|
214
240
|
*/
|
|
215
|
-
public
|
|
241
|
+
public getPublicDataWitness(blockHash: BlockHash, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
|
|
216
242
|
return this.aztecNode.getPublicDataWitness(blockHash, leafSlot);
|
|
217
243
|
}
|
|
218
244
|
|
|
@@ -221,7 +247,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
221
247
|
* @param blockNumber - The number of a block of which to get the block header.
|
|
222
248
|
* @returns Block extracted from a block with block number `blockNumber`.
|
|
223
249
|
*/
|
|
224
|
-
public async
|
|
250
|
+
public async getBlockHeader(blockNumber: BlockNumber): Promise<BlockHeader | undefined> {
|
|
225
251
|
const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
|
|
226
252
|
if (blockNumber > anchorBlockNumber) {
|
|
227
253
|
throw new Error(`Block number ${blockNumber} is higher than current block ${anchorBlockNumber}`);
|
|
@@ -232,12 +258,18 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
232
258
|
}
|
|
233
259
|
|
|
234
260
|
/**
|
|
235
|
-
* Retrieve the
|
|
261
|
+
* Retrieve the public keys and partial address associated to a given address.
|
|
236
262
|
* @param account - The account address.
|
|
237
|
-
* @returns
|
|
263
|
+
* @returns The public keys and partial address, or `undefined` if the account is not registered.
|
|
238
264
|
*/
|
|
239
|
-
public
|
|
240
|
-
|
|
265
|
+
public async tryGetPublicKeysAndPartialAddress(
|
|
266
|
+
account: AztecAddress,
|
|
267
|
+
): Promise<{ publicKeys: PublicKeys; partialAddress: PartialAddress } | undefined> {
|
|
268
|
+
const completeAddress = await this.addressStore.getCompleteAddress(account);
|
|
269
|
+
if (!completeAddress) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
return { publicKeys: completeAddress.publicKeys, partialAddress: completeAddress.partialAddress };
|
|
241
273
|
}
|
|
242
274
|
|
|
243
275
|
protected async getCompleteAddressOrFail(account: AztecAddress): Promise<CompleteAddress> {
|
|
@@ -256,11 +288,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
256
288
|
* @param address - Address.
|
|
257
289
|
* @returns A contract instance.
|
|
258
290
|
*/
|
|
259
|
-
public
|
|
260
|
-
return this.getContractInstance(address);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
protected async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
|
|
291
|
+
public async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
|
|
264
292
|
const instance = await this.contractStore.getContractInstance(address);
|
|
265
293
|
if (!instance) {
|
|
266
294
|
throw new Error(`No contract instance found for address ${address.toString()}`);
|
|
@@ -274,7 +302,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
274
302
|
* @param messageHash - Hash of the message to authenticate.
|
|
275
303
|
* @returns Authentication witness for the requested message hash.
|
|
276
304
|
*/
|
|
277
|
-
public
|
|
305
|
+
public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
|
|
278
306
|
return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
|
|
279
307
|
}
|
|
280
308
|
|
|
@@ -300,7 +328,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
300
328
|
* @param status - The status of notes to fetch.
|
|
301
329
|
* @returns Array of note data.
|
|
302
330
|
*/
|
|
303
|
-
public async
|
|
331
|
+
public async getNotes(
|
|
304
332
|
owner: AztecAddress | undefined,
|
|
305
333
|
storageSlot: Fr,
|
|
306
334
|
numSelects: number,
|
|
@@ -340,7 +368,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
340
368
|
* @param innerNullifier - The inner nullifier.
|
|
341
369
|
* @returns A boolean indicating whether the nullifier exists in the tree or not.
|
|
342
370
|
*/
|
|
343
|
-
public async
|
|
371
|
+
public async checkNullifierExists(innerNullifier: Fr) {
|
|
344
372
|
const [nullifier, anchorBlockHash] = await Promise.all([
|
|
345
373
|
siloNullifier(this.contractAddress, innerNullifier!),
|
|
346
374
|
this.anchorBlockHeader.hash(),
|
|
@@ -359,7 +387,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
359
387
|
* @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
|
|
360
388
|
* @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
|
|
361
389
|
*/
|
|
362
|
-
public async
|
|
390
|
+
public async getL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
|
|
363
391
|
const [messageIndex, siblingPath] = await getNonNullifiedL1ToL2MessageWitness(
|
|
364
392
|
this.aztecNode,
|
|
365
393
|
contractAddress,
|
|
@@ -377,7 +405,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
377
405
|
* @param startStorageSlot - The starting storage slot.
|
|
378
406
|
* @param numberOfElements - Number of elements to read from the starting storage slot.
|
|
379
407
|
*/
|
|
380
|
-
public async
|
|
408
|
+
public async storageRead(
|
|
381
409
|
blockHash: BlockHash,
|
|
382
410
|
contractAddress: AztecAddress,
|
|
383
411
|
startStorageSlot: Fr,
|
|
@@ -391,7 +419,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
391
419
|
slots.map(storageSlot => this.aztecNode.getPublicStorageAt(blockHash, contractAddress, storageSlot)),
|
|
392
420
|
);
|
|
393
421
|
|
|
394
|
-
this.
|
|
422
|
+
this.logger.debug(
|
|
395
423
|
`Oracle storage read: slots=[${slots.map(slot => slot.toString()).join(', ')}] address=${contractAddress.toString()} values=[${values.join(', ')}]`,
|
|
396
424
|
);
|
|
397
425
|
|
|
@@ -414,7 +442,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
414
442
|
return this.contractLogger;
|
|
415
443
|
}
|
|
416
444
|
|
|
417
|
-
public async
|
|
445
|
+
public async log(level: number, message: string, fields: Fr[]): Promise<void> {
|
|
418
446
|
if (!LogLevels[level]) {
|
|
419
447
|
throw new Error(`Invalid log level: ${level}`);
|
|
420
448
|
}
|
|
@@ -422,7 +450,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
422
450
|
logContractMessage(logger, LogLevels[level], message, fields);
|
|
423
451
|
}
|
|
424
452
|
|
|
425
|
-
public async
|
|
453
|
+
public async fetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
|
|
426
454
|
const logService = new LogService(
|
|
427
455
|
this.aztecNode,
|
|
428
456
|
this.anchorBlockHeader,
|
|
@@ -432,7 +460,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
432
460
|
this.senderAddressBookStore,
|
|
433
461
|
this.addressStore,
|
|
434
462
|
this.jobId,
|
|
435
|
-
this.
|
|
463
|
+
this.logger.getBindings(),
|
|
436
464
|
);
|
|
437
465
|
|
|
438
466
|
await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
|
|
@@ -448,10 +476,12 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
448
476
|
* @param noteValidationRequestsArrayBaseSlot - The base slot of capsule array containing note validation requests.
|
|
449
477
|
* @param eventValidationRequestsArrayBaseSlot - The base slot of capsule array containing event validation requests.
|
|
450
478
|
*/
|
|
451
|
-
public async
|
|
479
|
+
public async validateAndStoreEnqueuedNotesAndEvents(
|
|
452
480
|
contractAddress: AztecAddress,
|
|
453
481
|
noteValidationRequestsArrayBaseSlot: Fr,
|
|
454
482
|
eventValidationRequestsArrayBaseSlot: Fr,
|
|
483
|
+
maxNotePackedLen: number,
|
|
484
|
+
maxEventSerializedLen: number,
|
|
455
485
|
) {
|
|
456
486
|
// TODO(#10727): allow other contracts to store notes
|
|
457
487
|
if (!this.contractAddress.equals(contractAddress)) {
|
|
@@ -462,11 +492,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
462
492
|
// faster as we don't need to wait for the network round-trip.
|
|
463
493
|
const noteValidationRequests = (
|
|
464
494
|
await this.capsuleStore.readCapsuleArray(contractAddress, noteValidationRequestsArrayBaseSlot, this.jobId)
|
|
465
|
-
).map(NoteValidationRequest.fromFields);
|
|
495
|
+
).map(fields => NoteValidationRequest.fromFields(fields, maxNotePackedLen));
|
|
466
496
|
|
|
467
497
|
const eventValidationRequests = (
|
|
468
498
|
await this.capsuleStore.readCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, this.jobId)
|
|
469
|
-
).map(EventValidationRequest.fromFields);
|
|
499
|
+
).map(fields => EventValidationRequest.fromFields(fields, maxEventSerializedLen));
|
|
470
500
|
|
|
471
501
|
const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
|
|
472
502
|
const noteStorePromises = noteValidationRequests.map(request =>
|
|
@@ -504,7 +534,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
504
534
|
await this.capsuleStore.setCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, [], this.jobId);
|
|
505
535
|
}
|
|
506
536
|
|
|
507
|
-
public async
|
|
537
|
+
public async bulkRetrieveLogs(
|
|
508
538
|
contractAddress: AztecAddress,
|
|
509
539
|
logRetrievalRequestsArrayBaseSlot: Fr,
|
|
510
540
|
logRetrievalResponsesArrayBaseSlot: Fr,
|
|
@@ -529,7 +559,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
529
559
|
this.senderAddressBookStore,
|
|
530
560
|
this.addressStore,
|
|
531
561
|
this.jobId,
|
|
532
|
-
this.
|
|
562
|
+
this.logger.getBindings(),
|
|
533
563
|
);
|
|
534
564
|
|
|
535
565
|
const maybeLogRetrievalResponses = await logService.bulkRetrieveLogs(logRetrievalRequests);
|
|
@@ -546,7 +576,48 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
546
576
|
);
|
|
547
577
|
}
|
|
548
578
|
|
|
549
|
-
public
|
|
579
|
+
public async utilityResolveMessageContexts(
|
|
580
|
+
contractAddress: AztecAddress,
|
|
581
|
+
messageContextRequestsArrayBaseSlot: Fr,
|
|
582
|
+
messageContextResponsesArrayBaseSlot: Fr,
|
|
583
|
+
) {
|
|
584
|
+
try {
|
|
585
|
+
if (!this.contractAddress.equals(contractAddress)) {
|
|
586
|
+
throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`);
|
|
587
|
+
}
|
|
588
|
+
const requestCapsules = await this.capsuleStore.readCapsuleArray(
|
|
589
|
+
contractAddress,
|
|
590
|
+
messageContextRequestsArrayBaseSlot,
|
|
591
|
+
this.jobId,
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
const txHashes = requestCapsules.map((fields, i) => {
|
|
595
|
+
if (fields.length !== 1) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Malformed message context request at index ${i}: expected 1 field (tx hash), got ${fields.length}`,
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
return fields[0];
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
const maybeMessageContexts = await this.messageContextService.resolveMessageContexts(
|
|
604
|
+
txHashes,
|
|
605
|
+
this.anchorBlockHeader.getBlockNumber(),
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
// Leave response in response capsule array.
|
|
609
|
+
await this.capsuleStore.setCapsuleArray(
|
|
610
|
+
contractAddress,
|
|
611
|
+
messageContextResponsesArrayBaseSlot,
|
|
612
|
+
maybeMessageContexts.map(MessageTxContext.toSerializedOption),
|
|
613
|
+
this.jobId,
|
|
614
|
+
);
|
|
615
|
+
} finally {
|
|
616
|
+
await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
public storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise<void> {
|
|
550
621
|
if (!contractAddress.equals(this.contractAddress)) {
|
|
551
622
|
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
552
623
|
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
@@ -555,7 +626,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
555
626
|
return Promise.resolve();
|
|
556
627
|
}
|
|
557
628
|
|
|
558
|
-
public async
|
|
629
|
+
public async loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
|
|
559
630
|
if (!contractAddress.equals(this.contractAddress)) {
|
|
560
631
|
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
561
632
|
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
@@ -567,7 +638,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
567
638
|
);
|
|
568
639
|
}
|
|
569
640
|
|
|
570
|
-
public
|
|
641
|
+
public deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise<void> {
|
|
571
642
|
if (!contractAddress.equals(this.contractAddress)) {
|
|
572
643
|
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
573
644
|
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
@@ -576,12 +647,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
576
647
|
return Promise.resolve();
|
|
577
648
|
}
|
|
578
649
|
|
|
579
|
-
public
|
|
580
|
-
contractAddress: AztecAddress,
|
|
581
|
-
srcSlot: Fr,
|
|
582
|
-
dstSlot: Fr,
|
|
583
|
-
numEntries: number,
|
|
584
|
-
): Promise<void> {
|
|
650
|
+
public copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
|
|
585
651
|
if (!contractAddress.equals(this.contractAddress)) {
|
|
586
652
|
// TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
|
|
587
653
|
throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
|
|
@@ -589,8 +655,19 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
589
655
|
return this.capsuleStore.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries, this.jobId);
|
|
590
656
|
}
|
|
591
657
|
|
|
658
|
+
/**
|
|
659
|
+
* Clears cached sync state for a contract for a set of scopes, forcing re-sync on the next query so that newly
|
|
660
|
+
* stored notes or events are discovered.
|
|
661
|
+
*/
|
|
662
|
+
public invalidateContractSyncCache(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
|
|
663
|
+
if (!contractAddress.equals(this.contractAddress)) {
|
|
664
|
+
throw new Error(`Contract ${this.contractAddress} cannot invalidate sync cache of ${contractAddress}`);
|
|
665
|
+
}
|
|
666
|
+
this.contractSyncService.invalidateContractForScopes(contractAddress, scopes);
|
|
667
|
+
}
|
|
668
|
+
|
|
592
669
|
// TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
|
|
593
|
-
public
|
|
670
|
+
public aes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
|
|
594
671
|
const aes128 = new Aes128();
|
|
595
672
|
return aes128.decryptBufferCBC(ciphertext, iv, symKey);
|
|
596
673
|
}
|
|
@@ -601,11 +678,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
601
678
|
* @param ephPk - The ephemeral public key to get the secret for.
|
|
602
679
|
* @returns The secret for the given address.
|
|
603
680
|
*/
|
|
604
|
-
public
|
|
605
|
-
return this.getSharedSecret(address, ephPk);
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
protected async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
|
|
681
|
+
public async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
|
|
609
682
|
// TODO(#12656): return an app-siloed secret
|
|
610
683
|
const recipientCompleteAddress = await this.getCompleteAddressOrFail(address);
|
|
611
684
|
const ivskM = await this.keyStore.getMasterSecretKey(
|
|
@@ -614,4 +687,14 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
614
687
|
const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
|
|
615
688
|
return deriveEcdhSharedSecret(addressSecret, ephPk);
|
|
616
689
|
}
|
|
690
|
+
|
|
691
|
+
public emitOffchainEffect(data: Fr[]): Promise<void> {
|
|
692
|
+
this.offchainEffects.push({ data, contractAddress: this.contractAddress });
|
|
693
|
+
return Promise.resolve();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/** Returns offchain effects collected during execution. */
|
|
697
|
+
public getOffchainEffects(): OffchainEffect[] {
|
|
698
|
+
return this.offchainEffects;
|
|
699
|
+
}
|
|
617
700
|
}
|
|
@@ -20,12 +20,12 @@ export class ContractSyncService implements StagedStore {
|
|
|
20
20
|
readonly storeName = 'contract_sync';
|
|
21
21
|
|
|
22
22
|
// Tracks contracts synced since last wipe. The cache is keyed per individual scope address
|
|
23
|
-
// (`contractAddress:scopeAddress`), or `contractAddress:*` for
|
|
23
|
+
// (`contractAddress:scopeAddress`), or `contractAddress:*` for all scopes (all accounts).
|
|
24
24
|
// The value is a promise that resolves when the contract is synced.
|
|
25
25
|
private syncedContracts: Map<string, Promise<void>> = new Map();
|
|
26
26
|
|
|
27
|
-
// Per-job
|
|
28
|
-
private
|
|
27
|
+
// Per-job excluded contract addresses - these contracts should not be synced.
|
|
28
|
+
private excludedFromSync: Map<string, Set<string>> = new Map();
|
|
29
29
|
|
|
30
30
|
constructor(
|
|
31
31
|
private aztecNode: AztecNode,
|
|
@@ -35,8 +35,8 @@ export class ContractSyncService implements StagedStore {
|
|
|
35
35
|
) {}
|
|
36
36
|
|
|
37
37
|
/** Sets contracts that should be skipped during sync for a specific job. */
|
|
38
|
-
|
|
39
|
-
this.
|
|
38
|
+
setExcludedFromSync(jobId: string, addresses: Set<string>): void {
|
|
39
|
+
this.excludedFromSync.set(jobId, addresses);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -56,47 +56,34 @@ export class ContractSyncService implements StagedStore {
|
|
|
56
56
|
jobId: string,
|
|
57
57
|
scopes: AccessScopes,
|
|
58
58
|
): Promise<void> {
|
|
59
|
-
|
|
60
|
-
const overrides = this.overriddenContracts.get(jobId);
|
|
61
|
-
if (overrides?.has(contractAddress.toString())) {
|
|
59
|
+
if (this.#shouldSkipSync(jobId, contractAddress)) {
|
|
62
60
|
return;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const allScopesExisting = this.syncedContracts.get(allScopesKey);
|
|
68
|
-
if (allScopesExisting || (scopes !== 'ALL_SCOPES' && scopes.length == 0)) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const unsyncedScopes =
|
|
73
|
-
scopes === 'ALL_SCOPES'
|
|
74
|
-
? scopes
|
|
75
|
-
: scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
|
|
76
|
-
const unsyncedScopesKeys = toKeys(contractAddress, unsyncedScopes);
|
|
77
|
-
|
|
78
|
-
if (unsyncedScopesKeys.length > 0) {
|
|
79
|
-
// Start sync and store the promise for all unsynced scopes
|
|
80
|
-
const promise = this.#doSync(
|
|
63
|
+
this.#startSyncIfNeeded(contractAddress, scopes, scopesToSync =>
|
|
64
|
+
this.#syncContract(
|
|
81
65
|
contractAddress,
|
|
82
66
|
functionToInvokeAfterSync,
|
|
83
67
|
utilityExecutor,
|
|
84
68
|
anchorBlockHeader,
|
|
85
69
|
jobId,
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
unsyncedScopesKeys.forEach(key => this.syncedContracts.delete(key));
|
|
90
|
-
throw err;
|
|
91
|
-
});
|
|
92
|
-
unsyncedScopesKeys.forEach(key => this.syncedContracts.set(key, promise));
|
|
93
|
-
}
|
|
70
|
+
scopesToSync,
|
|
71
|
+
),
|
|
72
|
+
);
|
|
94
73
|
|
|
95
|
-
|
|
96
|
-
await Promise.all(promises);
|
|
74
|
+
await this.#awaitSync(contractAddress, scopes);
|
|
97
75
|
}
|
|
98
76
|
|
|
99
|
-
|
|
77
|
+
/** Clears sync cache entries for the given scopes of a contract. Also clears the ALL_SCOPES entry. */
|
|
78
|
+
invalidateContractForScopes(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
|
|
79
|
+
if (scopes.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
|
|
83
|
+
this.syncedContracts.delete(toKey(contractAddress, 'ALL_SCOPES'));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async #syncContract(
|
|
100
87
|
contractAddress: AztecAddress,
|
|
101
88
|
functionToInvokeAfterSync: FunctionSelector | null,
|
|
102
89
|
utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
|
|
@@ -129,8 +116,8 @@ export class ContractSyncService implements StagedStore {
|
|
|
129
116
|
}
|
|
130
117
|
|
|
131
118
|
commit(jobId: string): Promise<void> {
|
|
132
|
-
// Clear
|
|
133
|
-
this.
|
|
119
|
+
// Clear excluded contracts for this job
|
|
120
|
+
this.excludedFromSync.delete(jobId);
|
|
134
121
|
return Promise.resolve();
|
|
135
122
|
}
|
|
136
123
|
|
|
@@ -138,9 +125,51 @@ export class ContractSyncService implements StagedStore {
|
|
|
138
125
|
// We clear the synced contracts cache here because, when the job is discarded, any associated database writes from
|
|
139
126
|
// the sync are also undone.
|
|
140
127
|
this.syncedContracts.clear();
|
|
141
|
-
this.
|
|
128
|
+
this.excludedFromSync.delete(jobId);
|
|
142
129
|
return Promise.resolve();
|
|
143
130
|
}
|
|
131
|
+
/** Returns true if sync should be skipped for this contract */
|
|
132
|
+
#shouldSkipSync(jobId: string, contractAddress: AztecAddress): boolean {
|
|
133
|
+
return !!this.excludedFromSync.get(jobId)?.has(contractAddress.toString());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** If there are unsynced scopes, starts sync and stores the promise in cache with error cleanup. */
|
|
137
|
+
#startSyncIfNeeded(
|
|
138
|
+
contractAddress: AztecAddress,
|
|
139
|
+
scopes: AccessScopes,
|
|
140
|
+
syncFn: (scopesToSync: AccessScopes) => Promise<void>,
|
|
141
|
+
): void {
|
|
142
|
+
const scopesToSync = this.#getScopesToSync(contractAddress, scopes);
|
|
143
|
+
const keys = toKeys(contractAddress, scopesToSync);
|
|
144
|
+
if (keys.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const promise = syncFn(scopesToSync).catch(err => {
|
|
148
|
+
keys.forEach(key => this.syncedContracts.delete(key));
|
|
149
|
+
throw err;
|
|
150
|
+
});
|
|
151
|
+
keys.forEach(key => this.syncedContracts.set(key, promise));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Filters out scopes that are already cached, returning only those that still need syncing. */
|
|
155
|
+
#getScopesToSync(contractAddress: AztecAddress, scopes: AccessScopes): AccessScopes {
|
|
156
|
+
if (this.syncedContracts.has(toKey(contractAddress, 'ALL_SCOPES'))) {
|
|
157
|
+
// If we are already syncing all scopes, then return an empty list
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
if (scopes === 'ALL_SCOPES') {
|
|
161
|
+
return 'ALL_SCOPES';
|
|
162
|
+
}
|
|
163
|
+
return scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
|
|
167
|
+
async #awaitSync(contractAddress: AztecAddress, scopes: AccessScopes): Promise<void> {
|
|
168
|
+
const promises = toKeys(contractAddress, scopes)
|
|
169
|
+
.map(key => this.syncedContracts.get(key))
|
|
170
|
+
.filter(p => p !== undefined);
|
|
171
|
+
await Promise.all(promises);
|
|
172
|
+
}
|
|
144
173
|
}
|
|
145
174
|
|
|
146
175
|
function toKeys(contract: AztecAddress, scopes: AccessScopes) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
3
|
+
import { TxHash } from '@aztec/stdlib/tx';
|
|
4
|
+
|
|
5
|
+
import { MessageTxContext } from '../contract_function_simulator/noir-structs/message_tx_context.js';
|
|
6
|
+
|
|
7
|
+
/** Resolves transaction hashes into the context needed to process messages. */
|
|
8
|
+
export class MessageContextService {
|
|
9
|
+
constructor(private readonly aztecNode: AztecNode) {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a list of tx hashes into their message contexts.
|
|
13
|
+
*
|
|
14
|
+
* For each tx hash, looks up the corresponding tx effect and extracts the note hashes and first nullifier needed to
|
|
15
|
+
* process messages that originated from that transaction. Returns `null` for tx hashes that are zero, not yet
|
|
16
|
+
* available, or in blocks beyond the anchor block.
|
|
17
|
+
*/
|
|
18
|
+
resolveMessageContexts(txHashes: Fr[], anchorBlockNumber: number): Promise<(MessageTxContext | null)[]> {
|
|
19
|
+
// TODO: optimize, we might be hitting the node to get the same txHash repeatedly
|
|
20
|
+
return Promise.all(
|
|
21
|
+
txHashes.map(async txHashField => {
|
|
22
|
+
// A zero tx hash indicates a tx-less offchain message (e.g. one not tied to any onchain transaction).
|
|
23
|
+
// These messages don't have a transaction context to resolve, so we return null.
|
|
24
|
+
if (txHashField.isZero()) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const txHash = TxHash.fromField(txHashField);
|
|
29
|
+
const txEffect = await this.aztecNode.getTxEffect(txHash);
|
|
30
|
+
if (!txEffect || txEffect.l2BlockNumber > anchorBlockNumber) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Every tx has at least one nullifier (the first nullifier derived from the tx hash). Hitting this condition
|
|
35
|
+
// would mean a buggy node, but since we need to access data.nullifiers[0], the defensive check does no harm.
|
|
36
|
+
const data = txEffect.data;
|
|
37
|
+
if (data.nullifiers.length === 0) {
|
|
38
|
+
throw new Error(`Tx effect for ${txHash} has no nullifiers`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]);
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|