@aztec/pxe 0.0.1-commit.f650c0a5c → 0.0.1-commit.f7ea82942
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/block_synchronizer/block_synchronizer.d.ts +6 -2
- package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
- package/dest/block_synchronizer/block_synchronizer.js +13 -1
- package/dest/config/index.d.ts +1 -1
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +7 -14
- package/dest/contract_function_simulator/contract_function_simulator.d.ts +4 -1
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +6 -4
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +3 -2
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +11 -5
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +6 -5
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +5 -4
- package/dest/contract_function_simulator/pick_notes.d.ts +1 -1
- package/dest/contract_function_simulator/pick_notes.d.ts.map +1 -1
- package/dest/contract_function_simulator/pick_notes.js +11 -1
- package/dest/contract_function_simulator/proxied_contract_data_source.d.ts +1 -1
- package/dest/contract_function_simulator/proxied_contract_data_source.d.ts.map +1 -1
- package/dest/contract_function_simulator/proxied_contract_data_source.js +3 -0
- package/dest/contract_sync/contract_sync_service.d.ts +1 -1
- package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
- package/dest/contract_sync/contract_sync_service.js +35 -23
- package/dest/events/event_service.d.ts +1 -1
- package/dest/events/event_service.d.ts.map +1 -1
- package/dest/events/event_service.js +10 -1
- package/dest/events/private_event_filter_validator.d.ts +3 -2
- package/dest/events/private_event_filter_validator.d.ts.map +1 -1
- package/dest/events/private_event_filter_validator.js +15 -0
- package/dest/logs/log_service.d.ts +4 -2
- package/dest/logs/log_service.d.ts.map +1 -1
- package/dest/logs/log_service.js +6 -3
- package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.js +4 -7
- package/dest/private_kernel/private_kernel_oracle.d.ts +5 -5
- package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_oracle.js +12 -15
- package/dest/pxe.d.ts +2 -1
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +23 -16
- package/dest/storage/anchor_block_store/anchor_block_store.js +1 -1
- package/dest/storage/capsule_store/capsule_store.d.ts +1 -1
- package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
- package/dest/storage/capsule_store/capsule_store.js +8 -5
- package/dest/storage/contract_store/contract_store.d.ts +1 -1
- package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
- package/dest/storage/contract_store/contract_store.js +4 -2
- package/dest/storage/private_event_store/private_event_store.d.ts +1 -1
- package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
- package/dest/storage/private_event_store/private_event_store.js +3 -0
- package/dest/storage/private_event_store/stored_private_event.js +1 -1
- package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +2 -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 -16
- package/package.json +16 -16
- package/src/block_synchronizer/block_synchronizer.ts +16 -2
- package/src/config/index.ts +2 -8
- package/src/contract_function_simulator/contract_function_simulator.ts +7 -4
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +17 -4
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +7 -4
- package/src/contract_function_simulator/pick_notes.ts +13 -1
- package/src/contract_function_simulator/proxied_contract_data_source.ts +8 -1
- package/src/contract_sync/contract_sync_service.ts +57 -51
- package/src/events/event_service.ts +13 -1
- package/src/events/private_event_filter_validator.ts +21 -1
- package/src/logs/log_service.ts +6 -1
- package/src/private_kernel/private_kernel_execution_prover.ts +4 -9
- package/src/private_kernel/private_kernel_oracle.ts +14 -14
- package/src/pxe.ts +53 -16
- package/src/storage/anchor_block_store/anchor_block_store.ts +1 -1
- package/src/storage/capsule_store/capsule_store.ts +15 -5
- package/src/storage/contract_store/contract_store.ts +8 -6
- package/src/storage/private_event_store/private_event_store.ts +4 -0
- package/src/storage/private_event_store/stored_private_event.ts +1 -1
- package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +5 -15
|
@@ -42,7 +42,7 @@ import type { FunctionCall } from '@aztec/stdlib/abi';
|
|
|
42
42
|
import { FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
|
|
43
43
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
44
44
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
45
|
-
import type { BlockParameter } from '@aztec/stdlib/block';
|
|
45
|
+
import type { BlockParameter, L2TipsProvider } from '@aztec/stdlib/block';
|
|
46
46
|
import { Gas } from '@aztec/stdlib/gas';
|
|
47
47
|
import {
|
|
48
48
|
computeNoteHashNonce,
|
|
@@ -134,6 +134,7 @@ export type ContractFunctionSimulatorArgs = {
|
|
|
134
134
|
keyStore: KeyStore;
|
|
135
135
|
addressStore: AddressStore;
|
|
136
136
|
aztecNode: AztecNode;
|
|
137
|
+
l2TipsStore: L2TipsProvider;
|
|
137
138
|
senderTaggingStore: SenderTaggingStore;
|
|
138
139
|
recipientTaggingStore: RecipientTaggingStore;
|
|
139
140
|
senderAddressBookStore: SenderAddressBookStore;
|
|
@@ -154,6 +155,7 @@ export class ContractFunctionSimulator {
|
|
|
154
155
|
private readonly keyStore: KeyStore;
|
|
155
156
|
private readonly addressStore: AddressStore;
|
|
156
157
|
private readonly aztecNode: AztecNode;
|
|
158
|
+
private readonly l2TipsStore: L2TipsProvider;
|
|
157
159
|
private readonly senderTaggingStore: SenderTaggingStore;
|
|
158
160
|
private readonly recipientTaggingStore: RecipientTaggingStore;
|
|
159
161
|
private readonly senderAddressBookStore: SenderAddressBookStore;
|
|
@@ -169,6 +171,7 @@ export class ContractFunctionSimulator {
|
|
|
169
171
|
this.keyStore = args.keyStore;
|
|
170
172
|
this.addressStore = args.addressStore;
|
|
171
173
|
this.aztecNode = args.aztecNode;
|
|
174
|
+
this.l2TipsStore = args.l2TipsStore;
|
|
172
175
|
this.senderTaggingStore = args.senderTaggingStore;
|
|
173
176
|
this.recipientTaggingStore = args.recipientTaggingStore;
|
|
174
177
|
this.senderAddressBookStore = args.senderAddressBookStore;
|
|
@@ -205,7 +208,7 @@ export class ContractFunctionSimulator {
|
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
if (request.origin !== contractAddress) {
|
|
208
|
-
|
|
211
|
+
throw new Error(
|
|
209
212
|
`Request origin does not match contract address in simulation. Request origin: ${request.origin}, contract address: ${contractAddress}`,
|
|
210
213
|
);
|
|
211
214
|
}
|
|
@@ -255,6 +258,7 @@ export class ContractFunctionSimulator {
|
|
|
255
258
|
scopes,
|
|
256
259
|
senderForTags,
|
|
257
260
|
simulator: this.simulator,
|
|
261
|
+
l2TipsStore: this.l2TipsStore,
|
|
258
262
|
});
|
|
259
263
|
|
|
260
264
|
const setupTime = simulatorSetupTimer.ms();
|
|
@@ -305,7 +309,6 @@ export class ContractFunctionSimulator {
|
|
|
305
309
|
}
|
|
306
310
|
}
|
|
307
311
|
|
|
308
|
-
// docs:start:execute_utility_function
|
|
309
312
|
/**
|
|
310
313
|
* Runs a utility function.
|
|
311
314
|
* @param call - The function call to execute.
|
|
@@ -344,6 +347,7 @@ export class ContractFunctionSimulator {
|
|
|
344
347
|
privateEventStore: this.privateEventStore,
|
|
345
348
|
messageContextService: this.messageContextService,
|
|
346
349
|
contractSyncService: this.contractSyncService,
|
|
350
|
+
l2TipsStore: this.l2TipsStore,
|
|
347
351
|
jobId,
|
|
348
352
|
scopes,
|
|
349
353
|
});
|
|
@@ -379,7 +383,6 @@ export class ContractFunctionSimulator {
|
|
|
379
383
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
380
384
|
}
|
|
381
385
|
}
|
|
382
|
-
// docs:end:execute_utility_function
|
|
383
386
|
|
|
384
387
|
/**
|
|
385
388
|
* Returns the execution statistics collected during the simulator run.
|
|
@@ -81,7 +81,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
81
81
|
private readonly taggingIndexCache: ExecutionTaggingIndexCache;
|
|
82
82
|
private readonly senderTaggingStore: SenderTaggingStore;
|
|
83
83
|
private totalPublicCalldataCount: number;
|
|
84
|
-
|
|
84
|
+
private readonly initialSideEffectCounter: number;
|
|
85
85
|
private senderForTags?: AztecAddress;
|
|
86
86
|
private readonly simulator?: CircuitSimulator;
|
|
87
87
|
|
|
@@ -100,13 +100,18 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
100
100
|
this.taggingIndexCache = args.taggingIndexCache;
|
|
101
101
|
this.senderTaggingStore = args.senderTaggingStore;
|
|
102
102
|
this.totalPublicCalldataCount = args.totalPublicCalldataCount ?? 0;
|
|
103
|
-
this.
|
|
103
|
+
this.initialSideEffectCounter = args.sideEffectCounter ?? 0;
|
|
104
104
|
this.senderForTags = args.senderForTags;
|
|
105
105
|
this.simulator = args.simulator;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
public getPrivateContextInputs(): PrivateContextInputs {
|
|
109
|
-
return new PrivateContextInputs(
|
|
109
|
+
return new PrivateContextInputs(
|
|
110
|
+
this.callContext,
|
|
111
|
+
this.anchorBlockHeader,
|
|
112
|
+
this.txContext,
|
|
113
|
+
this.initialSideEffectCounter,
|
|
114
|
+
);
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
// We still need this function until we can get user-defined ordering of structs for fn arguments
|
|
@@ -216,7 +221,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
216
221
|
this.logger.warn(`Computing a tag for invalid recipient ${recipient} - returning a random tag instead`, {
|
|
217
222
|
contractAddress: this.contractAddress,
|
|
218
223
|
});
|
|
219
|
-
return
|
|
224
|
+
return Tag.random();
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
const index = await this.#getIndexToUseForSecret(extendedSecret);
|
|
@@ -575,6 +580,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
575
580
|
scopes: this.scopes,
|
|
576
581
|
senderForTags: this.senderForTags,
|
|
577
582
|
simulator: this.simulator!,
|
|
583
|
+
l2TipsStore: this.l2TipsStore,
|
|
578
584
|
});
|
|
579
585
|
|
|
580
586
|
const setupTime = simulatorSetupTimer.ms();
|
|
@@ -587,6 +593,9 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
587
593
|
functionSelector,
|
|
588
594
|
);
|
|
589
595
|
|
|
596
|
+
// Propagate the nested call's calldata count so the parent sees its increments on subsequent enqueues.
|
|
597
|
+
this.totalPublicCalldataCount = privateExecutionOracle.getTotalPublicCalldataCount();
|
|
598
|
+
|
|
590
599
|
if (isStaticCall) {
|
|
591
600
|
this.#checkValidStaticCall(childExecutionResult);
|
|
592
601
|
}
|
|
@@ -620,6 +629,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
620
629
|
return Promise.resolve();
|
|
621
630
|
}
|
|
622
631
|
|
|
632
|
+
public getTotalPublicCalldataCount(): number {
|
|
633
|
+
return this.totalPublicCalldataCount;
|
|
634
|
+
}
|
|
635
|
+
|
|
623
636
|
public notifyRevertiblePhaseStart(minRevertibleSideEffectCounter: number): Promise<void> {
|
|
624
637
|
return this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
625
638
|
}
|
|
@@ -9,7 +9,7 @@ import type { KeyStore } from '@aztec/key-store';
|
|
|
9
9
|
import { isProtocolContract } from '@aztec/protocol-contracts';
|
|
10
10
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
11
11
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
12
|
-
import { BlockHash } from '@aztec/stdlib/block';
|
|
12
|
+
import { BlockHash, type L2TipsProvider } from '@aztec/stdlib/block';
|
|
13
13
|
import type { CompleteAddress, ContractInstance, PartialAddress } from '@aztec/stdlib/contract';
|
|
14
14
|
import { siloNullifier } from '@aztec/stdlib/hash';
|
|
15
15
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
@@ -63,6 +63,7 @@ export type UtilityExecutionOracleArgs = {
|
|
|
63
63
|
privateEventStore: PrivateEventStore;
|
|
64
64
|
messageContextService: MessageContextService;
|
|
65
65
|
contractSyncService: ContractSyncService;
|
|
66
|
+
l2TipsStore: L2TipsProvider;
|
|
66
67
|
jobId: string;
|
|
67
68
|
log?: ReturnType<typeof createLogger>;
|
|
68
69
|
scopes: AztecAddress[];
|
|
@@ -98,6 +99,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
98
99
|
protected readonly privateEventStore: PrivateEventStore;
|
|
99
100
|
protected readonly messageContextService: MessageContextService;
|
|
100
101
|
protected readonly contractSyncService: ContractSyncService;
|
|
102
|
+
protected readonly l2TipsStore: L2TipsProvider;
|
|
101
103
|
protected readonly jobId: string;
|
|
102
104
|
protected logger: ReturnType<typeof createLogger>;
|
|
103
105
|
protected readonly scopes: AztecAddress[];
|
|
@@ -118,6 +120,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
118
120
|
this.privateEventStore = args.privateEventStore;
|
|
119
121
|
this.messageContextService = args.messageContextService;
|
|
120
122
|
this.contractSyncService = args.contractSyncService;
|
|
123
|
+
this.l2TipsStore = args.l2TipsStore;
|
|
121
124
|
this.jobId = args.jobId;
|
|
122
125
|
this.logger = args.log ?? createLogger('simulator:client_view_context');
|
|
123
126
|
this.scopes = args.scopes;
|
|
@@ -330,10 +333,9 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
330
333
|
}
|
|
331
334
|
|
|
332
335
|
/**
|
|
333
|
-
* Returns an auth witness for the given message hash
|
|
334
|
-
* for this transaction first, and falls back to the local database if not found.
|
|
336
|
+
* Returns an auth witness for the given message hash from the list of transient witnesses for this transaction.
|
|
335
337
|
* @param messageHash - Hash of the message to authenticate.
|
|
336
|
-
* @returns Authentication witness for the requested message hash.
|
|
338
|
+
* @returns Authentication witness for the requested message hash, or undefined if not found.
|
|
337
339
|
*/
|
|
338
340
|
public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
|
|
339
341
|
return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
|
|
@@ -531,6 +533,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
531
533
|
return new LogService(
|
|
532
534
|
this.aztecNode,
|
|
533
535
|
this.anchorBlockHeader,
|
|
536
|
+
this.l2TipsStore,
|
|
534
537
|
this.keyStore,
|
|
535
538
|
this.recipientTaggingStore,
|
|
536
539
|
this.senderAddressBookStore,
|
|
@@ -85,6 +85,14 @@ interface ContainsNote {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const selectPropertyFromPackedNoteContent = (noteData: Fr[], selector: PropertySelector): Fr => {
|
|
88
|
+
if (selector.index >= noteData.length) {
|
|
89
|
+
throw new Error(`Property selector index ${selector.index} out of bounds for note with ${noteData.length} fields`);
|
|
90
|
+
}
|
|
91
|
+
if (selector.offset + selector.length > Fr.SIZE_IN_BYTES) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Property selector range (offset=${selector.offset}, length=${selector.length}) exceeds Fr buffer size of ${Fr.SIZE_IN_BYTES} bytes`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
88
96
|
const noteValueBuffer = noteData[selector.index].toBuffer();
|
|
89
97
|
// Noir's PropertySelector counts offset from the LSB (last byte of the big-endian buffer),
|
|
90
98
|
// so offset=0,length=Fr.SIZE_IN_BYTES reads the entire field, and offset=0,length=1 reads the last byte.
|
|
@@ -110,7 +118,11 @@ const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]):
|
|
|
110
118
|
[Comparator.GTE]: () => !noteValueFr.lt(value),
|
|
111
119
|
};
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
const fn = comparatorSelector[comparator];
|
|
122
|
+
if (!fn) {
|
|
123
|
+
throw new Error(`Invalid comparator value: ${comparator}`);
|
|
124
|
+
}
|
|
125
|
+
return fn();
|
|
114
126
|
}),
|
|
115
127
|
);
|
|
116
128
|
|
|
@@ -29,6 +29,7 @@ export class ProxiedContractStoreFactory {
|
|
|
29
29
|
}
|
|
30
30
|
instance.currentContractClassId = realInstance.currentContractClassId;
|
|
31
31
|
instance.originalContractClassId = realInstance.originalContractClassId;
|
|
32
|
+
instance.initializationHash = realInstance.initializationHash;
|
|
32
33
|
return instance;
|
|
33
34
|
} else {
|
|
34
35
|
return target.getContractInstance(address);
|
|
@@ -47,6 +48,9 @@ export class ProxiedContractStoreFactory {
|
|
|
47
48
|
return fn;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Function with selector ${selector} not found in stub artifact for overridden contract at ${contractAddress}. The stub does not implement this function.`,
|
|
53
|
+
);
|
|
50
54
|
} else {
|
|
51
55
|
return target.getFunctionArtifact(contractAddress, selector);
|
|
52
56
|
}
|
|
@@ -64,6 +68,9 @@ export class ProxiedContractStoreFactory {
|
|
|
64
68
|
return fn;
|
|
65
69
|
}
|
|
66
70
|
}
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Function with selector ${selector} not found in stub artifact for overridden contract at ${contractAddress}. The stub does not implement this function.`,
|
|
73
|
+
);
|
|
67
74
|
} else {
|
|
68
75
|
return target.getFunctionArtifactWithDebugMetadata(contractAddress, selector);
|
|
69
76
|
}
|
|
@@ -78,6 +85,6 @@ export class ProxiedContractStoreFactory {
|
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
},
|
|
81
|
-
});
|
|
88
|
+
}) satisfies ContractStore;
|
|
82
89
|
}
|
|
83
90
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Logger } from '@aztec/foundation/log';
|
|
2
|
+
import { Semaphore } from '@aztec/foundation/queue';
|
|
2
3
|
import type { FunctionCall, FunctionSelector } from '@aztec/stdlib/abi';
|
|
3
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
5
|
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
@@ -9,6 +10,9 @@ import type { ContractStore } from '../storage/contract_store/contract_store.js'
|
|
|
9
10
|
import type { NoteStore } from '../storage/note_store/note_store.js';
|
|
10
11
|
import { syncState, verifyCurrentClassId } from './helpers.js';
|
|
11
12
|
|
|
13
|
+
/** Maximum number of scope syncs running concurrently across the PXE. */
|
|
14
|
+
const MAX_CONCURRENT_SCOPE_SYNCS = 5;
|
|
15
|
+
|
|
12
16
|
/**
|
|
13
17
|
* Service for syncing the private state of contracts and verifying that the PXE holds the current class artifact.
|
|
14
18
|
* It uses a cache to avoid redundant sync operations - the cache is wiped when the anchor block changes.
|
|
@@ -26,6 +30,11 @@ export class ContractSyncService implements StagedStore {
|
|
|
26
30
|
// Per-job excluded contract addresses - these contracts should not be synced.
|
|
27
31
|
private excludedFromSync: Map<string, Set<string>> = new Map();
|
|
28
32
|
|
|
33
|
+
// Bounds the number of scope syncs running concurrently. Scopes beyond this limit queue here. Sized to trade off
|
|
34
|
+
// parallelism on non-ACIR work (node RPC, note store reads) against memory pressure from concurrent circuit
|
|
35
|
+
// execution.
|
|
36
|
+
#syncSlot = new Semaphore(MAX_CONCURRENT_SCOPE_SYNCS);
|
|
37
|
+
|
|
29
38
|
constructor(
|
|
30
39
|
private aztecNode: AztecNode,
|
|
31
40
|
private contractStore: ContractStore,
|
|
@@ -59,15 +68,22 @@ export class ContractSyncService implements StagedStore {
|
|
|
59
68
|
return;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
|
-
this.#startSyncIfNeeded(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
this.#startSyncIfNeeded(
|
|
72
|
+
contractAddress,
|
|
73
|
+
scopes,
|
|
74
|
+
() => verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
|
|
75
|
+
scope =>
|
|
76
|
+
syncState(
|
|
77
|
+
contractAddress,
|
|
78
|
+
this.contractStore,
|
|
79
|
+
functionToInvokeAfterSync,
|
|
80
|
+
utilityExecutor,
|
|
81
|
+
this.noteStore,
|
|
82
|
+
this.aztecNode,
|
|
83
|
+
anchorBlockHeader,
|
|
84
|
+
jobId,
|
|
85
|
+
scope,
|
|
86
|
+
),
|
|
71
87
|
);
|
|
72
88
|
|
|
73
89
|
await this.#awaitSync(contractAddress, scopes);
|
|
@@ -81,39 +97,6 @@ export class ContractSyncService implements StagedStore {
|
|
|
81
97
|
scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
async #syncContract(
|
|
85
|
-
contractAddress: AztecAddress,
|
|
86
|
-
functionToInvokeAfterSync: FunctionSelector | null,
|
|
87
|
-
utilityExecutor: (call: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
|
|
88
|
-
anchorBlockHeader: BlockHeader,
|
|
89
|
-
jobId: string,
|
|
90
|
-
scopes: AztecAddress[],
|
|
91
|
-
): Promise<void> {
|
|
92
|
-
this.log.debug(`Syncing contract ${contractAddress}`);
|
|
93
|
-
|
|
94
|
-
await Promise.all([
|
|
95
|
-
// Call sync_state sequentially for each scope address — each invocation synchronizes one account's private
|
|
96
|
-
// state using scoped capsule arrays.
|
|
97
|
-
(async () => {
|
|
98
|
-
for (const scope of scopes) {
|
|
99
|
-
await syncState(
|
|
100
|
-
contractAddress,
|
|
101
|
-
this.contractStore,
|
|
102
|
-
functionToInvokeAfterSync,
|
|
103
|
-
utilityExecutor,
|
|
104
|
-
this.noteStore,
|
|
105
|
-
this.aztecNode,
|
|
106
|
-
anchorBlockHeader,
|
|
107
|
-
jobId,
|
|
108
|
-
scope,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
})(),
|
|
112
|
-
verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
|
|
113
|
-
]);
|
|
114
|
-
this.log.debug(`Contract ${contractAddress} synced`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
100
|
/** Clears sync cache. Called by BlockSynchronizer when anchor block changes. */
|
|
118
101
|
wipe(): void {
|
|
119
102
|
this.log.debug(`Wiping contract sync cache (${this.syncedContracts.size} entries)`);
|
|
@@ -138,22 +121,45 @@ export class ContractSyncService implements StagedStore {
|
|
|
138
121
|
return !!this.excludedFromSync.get(jobId)?.has(contractAddress.toString());
|
|
139
122
|
}
|
|
140
123
|
|
|
141
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* If there are unsynced scopes, starts one sync per scope (bounded by #syncSlot) and stores each promise in the
|
|
126
|
+
* cache with per-scope error cleanup. The verifyFn runs once for the whole fan-out and is awaited by every new
|
|
127
|
+
* scope's promise, matching the pre-parallelization invariant that a cache-miss batch re-verifies the class id.
|
|
128
|
+
*/
|
|
142
129
|
#startSyncIfNeeded(
|
|
143
130
|
contractAddress: AztecAddress,
|
|
144
131
|
scopes: AztecAddress[],
|
|
145
|
-
|
|
132
|
+
verifyFn: () => Promise<void>,
|
|
133
|
+
syncScopeFn: (scope: AztecAddress) => Promise<void>,
|
|
146
134
|
): void {
|
|
147
135
|
const scopesToSync = scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
|
|
148
|
-
|
|
149
|
-
if (keys.length === 0) {
|
|
136
|
+
if (scopesToSync.length === 0) {
|
|
150
137
|
return;
|
|
151
138
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
139
|
+
|
|
140
|
+
this.log.debug(`Syncing contract ${contractAddress} for ${scopesToSync.length} scope(s)`);
|
|
141
|
+
const verifyPromise = verifyFn();
|
|
142
|
+
|
|
143
|
+
for (const scope of scopesToSync) {
|
|
144
|
+
const key = toKey(contractAddress, scope);
|
|
145
|
+
const promise = Promise.all([verifyPromise, this.#runBounded(() => syncScopeFn(scope))])
|
|
146
|
+
.then(() => {})
|
|
147
|
+
.catch(err => {
|
|
148
|
+
this.syncedContracts.delete(key);
|
|
149
|
+
throw err;
|
|
150
|
+
});
|
|
151
|
+
this.syncedContracts.set(key, promise);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Runs fn while holding a slot in #syncSlot, bounding total concurrent scope syncs. */
|
|
156
|
+
async #runBounded<T>(fn: () => Promise<T>): Promise<T> {
|
|
157
|
+
await this.#syncSlot.acquire();
|
|
158
|
+
try {
|
|
159
|
+
return await fn();
|
|
160
|
+
} finally {
|
|
161
|
+
this.#syncSlot.release();
|
|
162
|
+
}
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
/** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
|
|
@@ -2,7 +2,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import type { EventSelector } from '@aztec/stdlib/abi';
|
|
4
4
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
-
import { siloNullifier } from '@aztec/stdlib/hash';
|
|
5
|
+
import { computePrivateEventCommitment, siloNullifier } from '@aztec/stdlib/hash';
|
|
6
6
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
7
7
|
import type { BlockHeader, TxHash } from '@aztec/stdlib/tx';
|
|
8
8
|
|
|
@@ -26,6 +26,18 @@ export class EventService {
|
|
|
26
26
|
txHash: TxHash,
|
|
27
27
|
scope: AztecAddress,
|
|
28
28
|
): Promise<void> {
|
|
29
|
+
// Defense-in-depth: the built-in private-event path derives this commitment from content before enqueueing, but
|
|
30
|
+
// unconstrained PXE-side code (e.g. a custom message handler) can reach this oracle with arbitrary
|
|
31
|
+
// (content, commitment) pairs. Without this check it could bind arbitrary content to a legitimate tx nullifier,
|
|
32
|
+
// causing PXE to surface fabricated event data.
|
|
33
|
+
const recomputedCommitment = await computePrivateEventCommitment(randomness, selector.toField(), content);
|
|
34
|
+
if (!recomputedCommitment.equals(eventCommitment)) {
|
|
35
|
+
this.log.warn(
|
|
36
|
+
`Skipping event whose content does not hash to the provided commitment. contract=${contractAddress}, selector=${selector}, eventCommitment=${eventCommitment}, txHash=${txHash}, recomputedCommitment=${recomputedCommitment}`,
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
// While using 'latest' block number would be fine for private events since they cannot be accessed from Aztec.nr
|
|
30
42
|
// (and thus we're less concerned about being ahead of the synced block), we use the synced block number to
|
|
31
43
|
// maintain consistent behavior in the PXE. Additionally, events should never be ahead of the synced block here
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { PrivateEventFilter } from '@aztec/aztec.js/wallet';
|
|
2
2
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
3
3
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
4
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
5
|
|
|
5
6
|
import type { PrivateEventStoreFilter } from '../storage/private_event_store/private_event_store.js';
|
|
6
7
|
|
|
7
8
|
export class PrivateEventFilterValidator {
|
|
8
|
-
|
|
9
|
+
private readonly log = createLogger('pxe:private_event_filter_validator');
|
|
10
|
+
|
|
11
|
+
constructor(private readonly lastBlock: BlockNumber) {}
|
|
9
12
|
|
|
10
13
|
validate(filter: PrivateEventFilter): PrivateEventStoreFilter {
|
|
11
14
|
let { fromBlock, toBlock } = filter;
|
|
@@ -35,6 +38,23 @@ export class PrivateEventFilterValidator {
|
|
|
35
38
|
throw new Error('toBlock must be strictly greater than fromBlock');
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
// Cap the requested range to the synced block range. Without this, callers that pass a large
|
|
42
|
+
// toBlock (e.g. Number.MAX_SAFE_INTEGER as a "give me everything" idiom) would silently receive
|
|
43
|
+
// only the events that happen to be synced and believe they have complete coverage.
|
|
44
|
+
// We warn + cap rather than throw so callers don't need to query the last synced block before
|
|
45
|
+
// every request (which would also be unreliable, as the block can advance between the two calls).
|
|
46
|
+
const syncedUpperBound = BlockNumber(this.lastBlock + 1);
|
|
47
|
+
if (fromBlock >= syncedUpperBound) {
|
|
48
|
+
this.log.warn(
|
|
49
|
+
`Requested fromBlock ${fromBlock} is past last synced block ${this.lastBlock}; no events will be returned until PXE syncs further.`,
|
|
50
|
+
);
|
|
51
|
+
} else if (toBlock > syncedUpperBound) {
|
|
52
|
+
this.log.warn(
|
|
53
|
+
`Requested toBlock ${toBlock} exceeds last synced block ${this.lastBlock}; capping to ${syncedUpperBound}. Retry once PXE is further synced for complete coverage.`,
|
|
54
|
+
);
|
|
55
|
+
toBlock = syncedUpperBound;
|
|
56
|
+
}
|
|
57
|
+
|
|
38
58
|
return {
|
|
39
59
|
contractAddress: filter.contractAddress,
|
|
40
60
|
scopes: filter.scopes,
|
package/src/logs/log_service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import type { KeyStore } from '@aztec/key-store';
|
|
3
3
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
|
+
import type { L2TipsProvider } from '@aztec/stdlib/block';
|
|
4
5
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
5
6
|
import { ExtendedDirectionalAppTaggingSecret, PendingTaggedLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
|
|
6
7
|
import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
@@ -22,6 +23,7 @@ export class LogService {
|
|
|
22
23
|
constructor(
|
|
23
24
|
private readonly aztecNode: AztecNode,
|
|
24
25
|
private readonly anchorBlockHeader: BlockHeader,
|
|
26
|
+
private readonly l2TipsStore: L2TipsProvider,
|
|
25
27
|
private readonly keyStore: KeyStore,
|
|
26
28
|
private readonly recipientTaggingStore: RecipientTaggingStore,
|
|
27
29
|
private readonly senderAddressBookStore: SenderAddressBookStore,
|
|
@@ -118,6 +120,8 @@ export class LogService {
|
|
|
118
120
|
const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
|
|
119
121
|
const anchorBlockHash = await this.anchorBlockHeader.hash();
|
|
120
122
|
|
|
123
|
+
const l2Tips = await this.l2TipsStore.getL2Tips();
|
|
124
|
+
const currentTimestamp = this.anchorBlockHeader.globalVariables.timestamp;
|
|
121
125
|
// Get all secrets for this recipient (one per sender)
|
|
122
126
|
const secrets = await this.#getSecretsForSenders(contractAddress, recipient);
|
|
123
127
|
|
|
@@ -130,6 +134,8 @@ export class LogService {
|
|
|
130
134
|
this.recipientTaggingStore,
|
|
131
135
|
anchorBlockNumber,
|
|
132
136
|
anchorBlockHash,
|
|
137
|
+
currentTimestamp,
|
|
138
|
+
l2Tips.finalized.block.number,
|
|
133
139
|
this.jobId,
|
|
134
140
|
),
|
|
135
141
|
),
|
|
@@ -174,7 +180,6 @@ export class LogService {
|
|
|
174
180
|
|
|
175
181
|
if (!secret) {
|
|
176
182
|
// Note that all senders originate from either the SenderAddressBookStore or the KeyStore.
|
|
177
|
-
// TODO(F-512): make sure we actually prevent registering invalid senders.
|
|
178
183
|
throw new Error(
|
|
179
184
|
`Failed to compute a tagging secret for sender ${sender} - this implies this is an invalid address, which should not happen as they have been previously registered in PXE.`,
|
|
180
185
|
);
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from '@aztec/stdlib/tx';
|
|
34
34
|
import { VerificationKeyAsFields, VerificationKeyData, VkData } from '@aztec/stdlib/vks';
|
|
35
35
|
|
|
36
|
+
import { computeTxExpirationTimestamp } from './hints/compute_tx_expiration_timestamp.js';
|
|
36
37
|
import { PrivateKernelResetPrivateInputsBuilder } from './hints/private_kernel_reset_private_inputs_builder.js';
|
|
37
38
|
import type { PrivateKernelOracle } from './private_kernel_oracle.js';
|
|
38
39
|
|
|
@@ -267,15 +268,9 @@ export class PrivateKernelExecutionProver {
|
|
|
267
268
|
// TODO: Enable padding once we better understand the final amounts to pad to.
|
|
268
269
|
const paddedSideEffectAmounts = PaddedSideEffectAmounts.empty();
|
|
269
270
|
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
const expirationTimestampUpperBound = previousKernelData.publicInputs
|
|
273
|
-
const anchorBlockTimestamp = previousKernelData.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp;
|
|
274
|
-
if (expirationTimestampUpperBound <= anchorBlockTimestamp) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
`Include-by timestamp must be greater than the anchor block timestamp. Anchor block timestamp: ${anchorBlockTimestamp}. Include-by timestamp: ${expirationTimestampUpperBound}.`,
|
|
277
|
-
);
|
|
278
|
-
}
|
|
271
|
+
// Round the aggregated expirationTimestamp down to reduce precision and avoid leaking which private
|
|
272
|
+
// functions were called via their exact expiration offsets.
|
|
273
|
+
const expirationTimestampUpperBound = computeTxExpirationTimestamp(previousKernelData.publicInputs);
|
|
279
274
|
|
|
280
275
|
const privateInputs = new PrivateKernelTailCircuitPrivateInputs(
|
|
281
276
|
previousKernelData,
|
|
@@ -7,13 +7,13 @@ import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-type
|
|
|
7
7
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
8
8
|
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
9
9
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
|
-
import { BlockHash } from '@aztec/stdlib/block';
|
|
11
10
|
import { type ContractInstanceWithAddress, computeSaltedInitializationHash } from '@aztec/stdlib/contract';
|
|
12
11
|
import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from '@aztec/stdlib/delayed-public-mutable';
|
|
13
12
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
14
13
|
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
15
14
|
import { UpdatedClassIdHints } from '@aztec/stdlib/kernel';
|
|
16
15
|
import type { NullifierMembershipWitness } from '@aztec/stdlib/trees';
|
|
16
|
+
import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
17
17
|
import type { VerificationKeyAsFields } from '@aztec/stdlib/vks';
|
|
18
18
|
|
|
19
19
|
import type { ContractStore } from '../storage/contract_store/contract_store.js';
|
|
@@ -26,7 +26,7 @@ export class PrivateKernelOracle {
|
|
|
26
26
|
private contractStore: ContractStore,
|
|
27
27
|
private keyStore: KeyStore,
|
|
28
28
|
private node: AztecNode,
|
|
29
|
-
private
|
|
29
|
+
private blockHeader: BlockHeader,
|
|
30
30
|
) {}
|
|
31
31
|
|
|
32
32
|
/** Retrieves the preimage of a contract address from the registered contract instances db. */
|
|
@@ -80,22 +80,20 @@ export class PrivateKernelOracle {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/** Returns a membership witness with the sibling path and leaf index in our note hash tree. */
|
|
83
|
-
getNoteHashMembershipWitness(
|
|
84
|
-
|
|
83
|
+
async getNoteHashMembershipWitness(
|
|
84
|
+
noteHash: Fr,
|
|
85
|
+
): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
|
|
86
|
+
return this.node.getNoteHashMembershipWitness(await this.blockHeader.hash(), noteHash);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
/** Returns a membership witness with the sibling path and leaf index in our nullifier indexed merkle tree. */
|
|
88
|
-
getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
|
|
89
|
-
return this.node.getNullifierMembershipWitness(this.
|
|
90
|
+
async getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
|
|
91
|
+
return this.node.getNullifierMembershipWitness(await this.blockHeader.hash(), nullifier);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/** Returns the root of our note hash merkle tree. */
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (!header) {
|
|
96
|
-
throw new Error(`No block header found for block hash ${this.blockHash}`);
|
|
97
|
-
}
|
|
98
|
-
return header.state.partial.noteHashTree.root;
|
|
95
|
+
getNoteHashTreeRoot(): Fr {
|
|
96
|
+
return this.blockHeader.state.partial.noteHashTree.root;
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
/**
|
|
@@ -126,14 +124,16 @@ export class PrivateKernelOracle {
|
|
|
126
124
|
ProtocolContractAddress.ContractInstanceRegistry,
|
|
127
125
|
delayedPublicMutableHashSlot,
|
|
128
126
|
);
|
|
129
|
-
const
|
|
127
|
+
const blockHash = await this.blockHeader.hash();
|
|
128
|
+
|
|
129
|
+
const updatedClassIdWitness = await this.node.getPublicDataWitness(blockHash, hashLeafSlot);
|
|
130
130
|
|
|
131
131
|
if (!updatedClassIdWitness) {
|
|
132
132
|
throw new Error(`No public data tree witness found for ${hashLeafSlot}`);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const readStorage = (storageSlot: Fr) =>
|
|
136
|
-
this.node.getPublicStorageAt(
|
|
136
|
+
this.node.getPublicStorageAt(blockHash, ProtocolContractAddress.ContractInstanceRegistry, storageSlot);
|
|
137
137
|
const delayedPublicMutableValues = await DelayedPublicMutableValues.readFromTree(
|
|
138
138
|
delayedPublicMutableSlot,
|
|
139
139
|
readStorage,
|