@aztec/pxe 0.0.1-commit.5914bae → 0.0.1-commit.59a0419c6
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 +2 -2
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +8 -15
- package/dest/config/package_info.js +1 -1
- 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 +3 -5
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +10 -8
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +20 -15
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +3 -4
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +2 -3
- 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 +6 -0
- 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/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 +17 -5
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +49 -23
- 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 +1 -1
- 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 +1 -1
- package/package.json +16 -16
- package/src/block_synchronizer/block_synchronizer.ts +16 -2
- package/src/config/index.ts +3 -9
- package/src/config/package_info.ts +1 -1
- package/src/contract_function_simulator/contract_function_simulator.ts +3 -5
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +31 -15
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +2 -3
- package/src/contract_function_simulator/pick_notes.ts +8 -0
- package/src/events/event_service.ts +13 -1
- package/src/private_kernel/private_kernel_oracle.ts +14 -14
- package/src/pxe.ts +82 -27
- 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 +3 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { SerialQueue } from '@aztec/foundation/queue';
|
|
3
4
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
4
5
|
import type { L2TipsKVStore } from '@aztec/kv-store/stores';
|
|
5
6
|
import { BlockHash, L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler } from '@aztec/stdlib/block';
|
|
@@ -20,6 +21,7 @@ import type { PrivateEventStore } from '../storage/private_event_store/private_e
|
|
|
20
21
|
export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
21
22
|
private log: Logger;
|
|
22
23
|
private isSyncing: Promise<void> | undefined;
|
|
24
|
+
private readonly eventQueue = new SerialQueue();
|
|
23
25
|
protected readonly blockStream: L2BlockStream;
|
|
24
26
|
|
|
25
27
|
constructor(
|
|
@@ -35,6 +37,7 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
35
37
|
) {
|
|
36
38
|
this.log = createLogger('pxe:block_synchronizer', bindings);
|
|
37
39
|
this.blockStream = this.createBlockStream(config);
|
|
40
|
+
this.eventQueue.start();
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
protected createBlockStream(config: Partial<BlockSynchronizerConfig>): L2BlockStream {
|
|
@@ -52,8 +55,12 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
52
55
|
);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
/** Handle events emitted by the block stream. */
|
|
56
|
-
public
|
|
58
|
+
/** Handle events emitted by the block stream. Serialized to prevent concurrent mutations to anchor state. */
|
|
59
|
+
public handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
60
|
+
return this.eventQueue.put(() => this.doHandleBlockStreamEvent(event));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async doHandleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
57
64
|
await this.l2TipsStore.handleBlockStreamEvent(event);
|
|
58
65
|
|
|
59
66
|
switch (event.type) {
|
|
@@ -167,6 +174,13 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
176
|
|
|
177
|
+
/** Stops the block synchronizer, waiting for any in-progress sync and queued events to complete. */
|
|
178
|
+
public async stop() {
|
|
179
|
+
await this.isSyncing;
|
|
180
|
+
await this.blockStream.stop();
|
|
181
|
+
await this.eventQueue.end();
|
|
182
|
+
}
|
|
183
|
+
|
|
170
184
|
private async doSync() {
|
|
171
185
|
let currentHeader;
|
|
172
186
|
|
package/src/config/index.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ConfigMappingsType,
|
|
3
3
|
booleanConfigHelper,
|
|
4
|
+
enumConfigHelper,
|
|
4
5
|
getConfigFromMappings,
|
|
5
6
|
numberConfigHelper,
|
|
6
7
|
parseBooleanEnv,
|
|
7
8
|
} from '@aztec/foundation/config';
|
|
8
|
-
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
9
9
|
import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config';
|
|
10
|
+
import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store';
|
|
10
11
|
|
|
11
12
|
export { getPackageInfo } from './package_info.js';
|
|
12
13
|
|
|
@@ -58,14 +59,7 @@ export const pxeConfigMappings: ConfigMappingsType<PXEConfig> = {
|
|
|
58
59
|
syncChainTip: {
|
|
59
60
|
env: 'PXE_SYNC_CHAIN_TIP',
|
|
60
61
|
description: 'Which chain tip to sync to (proposed, checkpointed, proven, finalized)',
|
|
61
|
-
|
|
62
|
-
parseEnv: (val: string) => {
|
|
63
|
-
const allowedValues = ['proposed', 'checkpointed', 'proven', 'finalized'];
|
|
64
|
-
if (allowedValues.includes(val)) {
|
|
65
|
-
return val;
|
|
66
|
-
}
|
|
67
|
-
throw new Error(`Invalid value for PXE_SYNC_CHAIN_TIP: ${val}. Allowed values are: ${allowedValues.join(', ')}`);
|
|
68
|
-
},
|
|
62
|
+
...enumConfigHelper(['proposed', 'checkpointed', 'proven', 'finalized'], 'proposed'),
|
|
69
63
|
},
|
|
70
64
|
};
|
|
71
65
|
|
|
@@ -208,7 +208,7 @@ export class ContractFunctionSimulator {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
if (request.origin !== contractAddress) {
|
|
211
|
-
|
|
211
|
+
throw new Error(
|
|
212
212
|
`Request origin does not match contract address in simulation. Request origin: ${request.origin}, contract address: ${contractAddress}`,
|
|
213
213
|
);
|
|
214
214
|
}
|
|
@@ -309,7 +309,6 @@ export class ContractFunctionSimulator {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
// docs:start:execute_utility_function
|
|
313
312
|
/**
|
|
314
313
|
* Runs a utility function.
|
|
315
314
|
* @param call - The function call to execute.
|
|
@@ -384,7 +383,6 @@ export class ContractFunctionSimulator {
|
|
|
384
383
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
385
384
|
}
|
|
386
385
|
}
|
|
387
|
-
// docs:end:execute_utility_function
|
|
388
386
|
|
|
389
387
|
/**
|
|
390
388
|
* Returns the execution statistics collected during the simulator run.
|
|
@@ -846,9 +844,9 @@ function meterGasUsed(data: PrivateToRollupAccumulatedData | PrivateToPublicAccu
|
|
|
846
844
|
meteredL2Gas += numPrivatelogs * L2_GAS_PER_PRIVATE_LOG;
|
|
847
845
|
|
|
848
846
|
const numContractClassLogs = arrayNonEmptyLength(data.contractClassLogsHashes, log => log.isEmpty());
|
|
849
|
-
// Every contract class log emits its
|
|
847
|
+
// Every contract class log emits its contract address as an additional field
|
|
850
848
|
meteredDAFields += data.contractClassLogsHashes.reduce(
|
|
851
|
-
(acc, log) => (!log.isEmpty() ? acc + log.logHash.length +
|
|
849
|
+
(acc, log) => (!log.isEmpty() ? acc + log.logHash.length + 1 : acc),
|
|
852
850
|
0,
|
|
853
851
|
);
|
|
854
852
|
meteredL2Gas += numContractClassLogs * L2_GAS_PER_CONTRACT_CLASS_LOG;
|
|
@@ -81,8 +81,11 @@ 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
|
-
|
|
85
|
-
|
|
84
|
+
private readonly initialSideEffectCounter: number;
|
|
85
|
+
/** Sender for tags passed in at oracle construction time. Returned by `getSenderForTags` unless overridden. */
|
|
86
|
+
private readonly defaultSenderForTags: AztecAddress | undefined;
|
|
87
|
+
/** Per-call sender-for-tags override, set by `setSenderForTags`. Takes precedence over `defaultSenderForTags`. */
|
|
88
|
+
private currentSenderForTags: AztecAddress | undefined;
|
|
86
89
|
private readonly simulator?: CircuitSimulator;
|
|
87
90
|
|
|
88
91
|
constructor(args: PrivateExecutionOracleArgs) {
|
|
@@ -100,13 +103,18 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
100
103
|
this.taggingIndexCache = args.taggingIndexCache;
|
|
101
104
|
this.senderTaggingStore = args.senderTaggingStore;
|
|
102
105
|
this.totalPublicCalldataCount = args.totalPublicCalldataCount ?? 0;
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
106
|
+
this.initialSideEffectCounter = args.sideEffectCounter ?? 0;
|
|
107
|
+
this.defaultSenderForTags = args.senderForTags;
|
|
105
108
|
this.simulator = args.simulator;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
public getPrivateContextInputs(): PrivateContextInputs {
|
|
109
|
-
return new PrivateContextInputs(
|
|
112
|
+
return new PrivateContextInputs(
|
|
113
|
+
this.callContext,
|
|
114
|
+
this.anchorBlockHeader,
|
|
115
|
+
this.txContext,
|
|
116
|
+
this.initialSideEffectCounter,
|
|
117
|
+
);
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
// We still need this function until we can get user-defined ordering of structs for fn arguments
|
|
@@ -173,11 +181,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
173
181
|
* for a tag in order to emit a log. Constrained tagging should not use this as there is no
|
|
174
182
|
* guarantee that the recipient knows about the sender, and hence about the shared secret.
|
|
175
183
|
*
|
|
176
|
-
*
|
|
177
|
-
* 'senderForTags' value (unless it is replaced).
|
|
184
|
+
* Returns `currentSenderForTags` if set (via `setSenderForTags`), otherwise `defaultSenderForTags`.
|
|
178
185
|
*/
|
|
179
186
|
public getSenderForTags(): Promise<AztecAddress | undefined> {
|
|
180
|
-
return Promise.resolve(this.
|
|
187
|
+
return Promise.resolve(this.currentSenderForTags ?? this.defaultSenderForTags);
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
/**
|
|
@@ -187,12 +194,14 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
187
194
|
* for a tag in order to emit a log. Constrained tagging should not use this as there is no
|
|
188
195
|
* guarantee that the recipient knows about the sender, and hence about the shared secret.
|
|
189
196
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* value (unless it is replaced by another call to this setter).
|
|
197
|
+
* Overrides `defaultSenderForTags` for the remainder of this call. Each oracle instance is
|
|
198
|
+
* independent, so this has no effect on any other call in the execution.
|
|
193
199
|
*/
|
|
194
200
|
public setSenderForTags(senderForTags: AztecAddress): Promise<void> {
|
|
195
|
-
this.
|
|
201
|
+
this.logger.debug(
|
|
202
|
+
`Sender for tags switched to ${senderForTags} by contract ${this.contractAddress} (default was ${this.defaultSenderForTags})`,
|
|
203
|
+
);
|
|
204
|
+
this.currentSenderForTags = senderForTags;
|
|
196
205
|
return Promise.resolve();
|
|
197
206
|
}
|
|
198
207
|
|
|
@@ -216,7 +225,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
216
225
|
this.logger.warn(`Computing a tag for invalid recipient ${recipient} - returning a random tag instead`, {
|
|
217
226
|
contractAddress: this.contractAddress,
|
|
218
227
|
});
|
|
219
|
-
return
|
|
228
|
+
return Tag.random();
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
const index = await this.#getIndexToUseForSecret(extendedSecret);
|
|
@@ -571,9 +580,9 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
571
580
|
jobId: this.jobId,
|
|
572
581
|
totalPublicCalldataCount: this.totalPublicCalldataCount,
|
|
573
582
|
sideEffectCounter,
|
|
574
|
-
scopes: this.scopes,
|
|
575
583
|
log: this.logger,
|
|
576
|
-
|
|
584
|
+
scopes: this.scopes,
|
|
585
|
+
senderForTags: this.defaultSenderForTags,
|
|
577
586
|
simulator: this.simulator!,
|
|
578
587
|
l2TipsStore: this.l2TipsStore,
|
|
579
588
|
});
|
|
@@ -588,6 +597,9 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
588
597
|
functionSelector,
|
|
589
598
|
);
|
|
590
599
|
|
|
600
|
+
// Propagate the nested call's calldata count so the parent sees its increments on subsequent enqueues.
|
|
601
|
+
this.totalPublicCalldataCount = privateExecutionOracle.getTotalPublicCalldataCount();
|
|
602
|
+
|
|
591
603
|
if (isStaticCall) {
|
|
592
604
|
this.#checkValidStaticCall(childExecutionResult);
|
|
593
605
|
}
|
|
@@ -621,6 +633,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
621
633
|
return Promise.resolve();
|
|
622
634
|
}
|
|
623
635
|
|
|
636
|
+
public getTotalPublicCalldataCount(): number {
|
|
637
|
+
return this.totalPublicCalldataCount;
|
|
638
|
+
}
|
|
639
|
+
|
|
624
640
|
public notifyRevertiblePhaseStart(minRevertibleSideEffectCounter: number): Promise<void> {
|
|
625
641
|
return this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
626
642
|
}
|
|
@@ -333,10 +333,9 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
/**
|
|
336
|
-
* Returns an auth witness for the given message hash
|
|
337
|
-
* 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.
|
|
338
337
|
* @param messageHash - Hash of the message to authenticate.
|
|
339
|
-
* @returns Authentication witness for the requested message hash.
|
|
338
|
+
* @returns Authentication witness for the requested message hash, or undefined if not found.
|
|
340
339
|
*/
|
|
341
340
|
public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
|
|
342
341
|
return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
|
|
@@ -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.
|
|
@@ -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
|
|
@@ -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,
|
package/src/pxe.ts
CHANGED
|
@@ -89,6 +89,14 @@ export type PackedPrivateEvent = InTx & {
|
|
|
89
89
|
eventSelector: EventSelector;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
/** Options for PXE.proveTx. */
|
|
93
|
+
export type ProveTxOpts = {
|
|
94
|
+
/** Addresses whose private state and keys are accessible during private execution. */
|
|
95
|
+
scopes: AztecAddress[];
|
|
96
|
+
/** Sender address used to derive discovery tags for private messages (notes, events, logs) this tx emits. */
|
|
97
|
+
senderForTags?: AztecAddress;
|
|
98
|
+
};
|
|
99
|
+
|
|
92
100
|
/** Options for PXE.profileTx. */
|
|
93
101
|
export type ProfileTxOpts = {
|
|
94
102
|
/** The profiling mode to use. */
|
|
@@ -97,6 +105,8 @@ export type ProfileTxOpts = {
|
|
|
97
105
|
skipProofGeneration?: boolean;
|
|
98
106
|
/** Addresses whose private state and keys are accessible during private execution. */
|
|
99
107
|
scopes: AztecAddress[];
|
|
108
|
+
/** Sender address used to derive discovery tags for private messages (notes, events, logs) this tx emits. */
|
|
109
|
+
senderForTags?: AztecAddress;
|
|
100
110
|
};
|
|
101
111
|
|
|
102
112
|
/** Options for PXE.simulateTx. */
|
|
@@ -113,6 +123,8 @@ export type SimulateTxOpts = {
|
|
|
113
123
|
overrides?: SimulationOverrides;
|
|
114
124
|
/** Addresses whose private state and keys are accessible during private execution */
|
|
115
125
|
scopes: AztecAddress[];
|
|
126
|
+
/** Sender address used to derive discovery tags for private messages (notes, events, logs) this tx emits. */
|
|
127
|
+
senderForTags?: AztecAddress;
|
|
116
128
|
};
|
|
117
129
|
|
|
118
130
|
/** Options for PXE.executeUtility. */
|
|
@@ -148,6 +160,7 @@ export type PXECreateArgs = {
|
|
|
148
160
|
export class PXE {
|
|
149
161
|
private constructor(
|
|
150
162
|
private node: AztecNode,
|
|
163
|
+
private db: AztecAsyncKVStore,
|
|
151
164
|
private blockStateSynchronizer: BlockSynchronizer,
|
|
152
165
|
private keyStore: KeyStore,
|
|
153
166
|
private contractStore: ContractStore,
|
|
@@ -247,6 +260,7 @@ export class PXE {
|
|
|
247
260
|
|
|
248
261
|
const pxe = new PXE(
|
|
249
262
|
node,
|
|
263
|
+
store,
|
|
250
264
|
synchronizer,
|
|
251
265
|
keyStore,
|
|
252
266
|
contractStore,
|
|
@@ -366,17 +380,24 @@ export class PXE {
|
|
|
366
380
|
|
|
367
381
|
// Executes the entrypoint private function, as well as all nested private
|
|
368
382
|
// functions that might arise.
|
|
369
|
-
async #executePrivate(
|
|
370
|
-
contractFunctionSimulator
|
|
371
|
-
txRequest
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
383
|
+
async #executePrivate({
|
|
384
|
+
contractFunctionSimulator,
|
|
385
|
+
txRequest,
|
|
386
|
+
anchorBlockHeader,
|
|
387
|
+
scopes,
|
|
388
|
+
jobId,
|
|
389
|
+
senderForTags,
|
|
390
|
+
}: {
|
|
391
|
+
contractFunctionSimulator: ContractFunctionSimulator;
|
|
392
|
+
txRequest: TxExecutionRequest;
|
|
393
|
+
anchorBlockHeader: BlockHeader;
|
|
394
|
+
scopes: AztecAddress[];
|
|
395
|
+
jobId: string;
|
|
396
|
+
senderForTags?: AztecAddress;
|
|
397
|
+
}): Promise<PrivateExecutionResult> {
|
|
375
398
|
const { origin: contractAddress, functionSelector } = txRequest;
|
|
376
399
|
|
|
377
400
|
try {
|
|
378
|
-
const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader();
|
|
379
|
-
|
|
380
401
|
await this.contractSyncService.ensureContractSynced(
|
|
381
402
|
contractAddress,
|
|
382
403
|
functionSelector,
|
|
@@ -393,6 +414,7 @@ export class PXE {
|
|
|
393
414
|
anchorBlockHeader,
|
|
394
415
|
scopes,
|
|
395
416
|
jobId,
|
|
417
|
+
senderForTags,
|
|
396
418
|
});
|
|
397
419
|
this.log.debug(`Private simulation completed for ${contractAddress.toString()}:${functionSelector}`);
|
|
398
420
|
return result;
|
|
@@ -481,11 +503,10 @@ export class PXE {
|
|
|
481
503
|
txExecutionRequest: TxExecutionRequest,
|
|
482
504
|
proofCreator: PrivateKernelProver,
|
|
483
505
|
privateExecutionResult: PrivateExecutionResult,
|
|
506
|
+
anchorBlockHeader: BlockHeader,
|
|
484
507
|
config: PrivateKernelExecutionProverConfig,
|
|
485
508
|
): Promise<PrivateKernelExecutionProofOutput<PrivateKernelTailCircuitPublicInputs>> {
|
|
486
|
-
const
|
|
487
|
-
const anchorBlockHash = await anchorBlockHeader.hash();
|
|
488
|
-
const kernelOracle = new PrivateKernelOracle(this.contractStore, this.keyStore, this.node, anchorBlockHash);
|
|
509
|
+
const kernelOracle = new PrivateKernelOracle(this.contractStore, this.keyStore, this.node, anchorBlockHeader);
|
|
489
510
|
const kernelTraceProver = new PrivateKernelExecutionProver(
|
|
490
511
|
kernelOracle,
|
|
491
512
|
proofCreator,
|
|
@@ -579,8 +600,8 @@ export class PXE {
|
|
|
579
600
|
if (wasAdded) {
|
|
580
601
|
this.log.info(`Added sender:\n ${sender.toString()}`);
|
|
581
602
|
// Wipe the entire sync cache: the new sender's tagged logs could contain notes/events for any contract, so
|
|
582
|
-
// all contracts must re-sync to discover them.
|
|
583
|
-
this.contractSyncService.wipe();
|
|
603
|
+
// all contracts must re-sync to discover them. Queued to avoid wiping while a job is in flight.
|
|
604
|
+
await this.#putInJobQueue(() => Promise.resolve(this.contractSyncService.wipe()));
|
|
584
605
|
} else {
|
|
585
606
|
this.log.info(`Sender:\n "${sender.toString()}"\n already registered.`);
|
|
586
607
|
}
|
|
@@ -740,7 +761,7 @@ export class PXE {
|
|
|
740
761
|
* @throws If contract code not found, or public simulation reverts.
|
|
741
762
|
* Also throws if simulatePublic is true and public simulation reverts.
|
|
742
763
|
*/
|
|
743
|
-
public proveTx(txRequest: TxExecutionRequest, scopes:
|
|
764
|
+
public proveTx(txRequest: TxExecutionRequest, { scopes, senderForTags }: ProveTxOpts): Promise<TxProvingResult> {
|
|
744
765
|
let privateExecutionResult: PrivateExecutionResult;
|
|
745
766
|
// We disable proving concurrently mostly out of caution, since it accesses some of our stores. Proving is so
|
|
746
767
|
// computationally demanding that it'd be rare for someone to try to do it concurrently regardless.
|
|
@@ -749,16 +770,24 @@ export class PXE {
|
|
|
749
770
|
try {
|
|
750
771
|
const syncTimer = new Timer();
|
|
751
772
|
await this.blockStateSynchronizer.sync();
|
|
773
|
+
const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader();
|
|
752
774
|
const syncTime = syncTimer.ms();
|
|
753
775
|
const contractFunctionSimulator = this.#getSimulatorForTx();
|
|
754
|
-
privateExecutionResult = await this.#executePrivate(
|
|
776
|
+
privateExecutionResult = await this.#executePrivate({
|
|
777
|
+
contractFunctionSimulator,
|
|
778
|
+
txRequest,
|
|
779
|
+
anchorBlockHeader,
|
|
780
|
+
scopes,
|
|
781
|
+
jobId,
|
|
782
|
+
senderForTags,
|
|
783
|
+
});
|
|
755
784
|
|
|
756
785
|
const {
|
|
757
786
|
publicInputs,
|
|
758
787
|
chonkProof,
|
|
759
788
|
executionSteps,
|
|
760
789
|
timings: { proving } = {},
|
|
761
|
-
} = await this.#prove(txRequest, this.proofCreator, privateExecutionResult, {
|
|
790
|
+
} = await this.#prove(txRequest, this.proofCreator, privateExecutionResult, anchorBlockHeader, {
|
|
762
791
|
simulate: false,
|
|
763
792
|
skipFeeEnforcement: false,
|
|
764
793
|
profileMode: 'none',
|
|
@@ -821,7 +850,7 @@ export class PXE {
|
|
|
821
850
|
*/
|
|
822
851
|
public profileTx(
|
|
823
852
|
txRequest: TxExecutionRequest,
|
|
824
|
-
{ profileMode, skipProofGeneration = true, scopes }: ProfileTxOpts,
|
|
853
|
+
{ profileMode, skipProofGeneration = true, scopes, senderForTags }: ProfileTxOpts,
|
|
825
854
|
): Promise<TxProfileResult> {
|
|
826
855
|
// We disable concurrent profiles for consistency with simulateTx.
|
|
827
856
|
return this.#putInJobQueue(async jobId => {
|
|
@@ -841,15 +870,24 @@ export class PXE {
|
|
|
841
870
|
);
|
|
842
871
|
const syncTimer = new Timer();
|
|
843
872
|
await this.blockStateSynchronizer.sync();
|
|
873
|
+
const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader();
|
|
844
874
|
const syncTime = syncTimer.ms();
|
|
845
875
|
|
|
846
876
|
const contractFunctionSimulator = this.#getSimulatorForTx();
|
|
847
|
-
const privateExecutionResult = await this.#executePrivate(
|
|
877
|
+
const privateExecutionResult = await this.#executePrivate({
|
|
878
|
+
contractFunctionSimulator,
|
|
879
|
+
txRequest,
|
|
880
|
+
anchorBlockHeader,
|
|
881
|
+
scopes,
|
|
882
|
+
jobId,
|
|
883
|
+
senderForTags,
|
|
884
|
+
});
|
|
848
885
|
|
|
849
886
|
const { executionSteps, timings: { proving } = {} } = await this.#prove(
|
|
850
887
|
txRequest,
|
|
851
888
|
this.proofCreator,
|
|
852
889
|
privateExecutionResult,
|
|
890
|
+
anchorBlockHeader,
|
|
853
891
|
{
|
|
854
892
|
simulate: skipProofGeneration,
|
|
855
893
|
skipFeeEnforcement: false,
|
|
@@ -917,6 +955,7 @@ export class PXE {
|
|
|
917
955
|
skipKernels = true,
|
|
918
956
|
overrides,
|
|
919
957
|
scopes,
|
|
958
|
+
senderForTags,
|
|
920
959
|
}: SimulateTxOpts,
|
|
921
960
|
): Promise<TxSimulationResult> {
|
|
922
961
|
// We disable concurrent simulations since those might execute oracles which read and write to the PXE stores (e.g.
|
|
@@ -939,6 +978,7 @@ export class PXE {
|
|
|
939
978
|
);
|
|
940
979
|
const syncTimer = new Timer();
|
|
941
980
|
await this.blockStateSynchronizer.sync();
|
|
981
|
+
const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader();
|
|
942
982
|
const syncTime = syncTimer.ms();
|
|
943
983
|
|
|
944
984
|
const overriddenContracts = overrides?.contracts ? new Set(Object.keys(overrides.contracts)) : undefined;
|
|
@@ -958,7 +998,14 @@ export class PXE {
|
|
|
958
998
|
}
|
|
959
999
|
|
|
960
1000
|
// Execution of private functions only; no proving, and no kernel logic.
|
|
961
|
-
const privateExecutionResult = await this.#executePrivate(
|
|
1001
|
+
const privateExecutionResult = await this.#executePrivate({
|
|
1002
|
+
contractFunctionSimulator,
|
|
1003
|
+
txRequest,
|
|
1004
|
+
anchorBlockHeader,
|
|
1005
|
+
scopes,
|
|
1006
|
+
jobId,
|
|
1007
|
+
senderForTags,
|
|
1008
|
+
});
|
|
962
1009
|
|
|
963
1010
|
let publicInputs: PrivateKernelTailCircuitPublicInputs | undefined;
|
|
964
1011
|
let executionSteps: PrivateExecutionStep[] = [];
|
|
@@ -971,11 +1018,17 @@ export class PXE {
|
|
|
971
1018
|
));
|
|
972
1019
|
} else {
|
|
973
1020
|
// Kernel logic, plus proving of all private functions and kernels.
|
|
974
|
-
({ publicInputs, executionSteps } = await this.#prove(
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1021
|
+
({ publicInputs, executionSteps } = await this.#prove(
|
|
1022
|
+
txRequest,
|
|
1023
|
+
this.proofCreator,
|
|
1024
|
+
privateExecutionResult,
|
|
1025
|
+
anchorBlockHeader,
|
|
1026
|
+
{
|
|
1027
|
+
simulate: true,
|
|
1028
|
+
skipFeeEnforcement,
|
|
1029
|
+
profileMode: 'none',
|
|
1030
|
+
},
|
|
1031
|
+
));
|
|
979
1032
|
}
|
|
980
1033
|
|
|
981
1034
|
const privateSimulationResult = new PrivateSimulationResult(privateExecutionResult, publicInputs);
|
|
@@ -1174,9 +1227,11 @@ export class PXE {
|
|
|
1174
1227
|
}
|
|
1175
1228
|
|
|
1176
1229
|
/**
|
|
1177
|
-
* Stops the PXE's job queue.
|
|
1230
|
+
* Stops the PXE's job queue and closes the backing store.
|
|
1178
1231
|
*/
|
|
1179
|
-
public stop(): Promise<void> {
|
|
1180
|
-
|
|
1232
|
+
public async stop(): Promise<void> {
|
|
1233
|
+
await this.jobQueue.end();
|
|
1234
|
+
await this.blockStateSynchronizer.stop();
|
|
1235
|
+
await this.db.close();
|
|
1181
1236
|
}
|
|
1182
1237
|
}
|
|
@@ -234,6 +234,10 @@ export class PrivateEventStore implements StagedStore {
|
|
|
234
234
|
* IMPORTANT: This method must be called within a transaction to ensure atomicity.
|
|
235
235
|
*/
|
|
236
236
|
public async rollback(blockNumber: number, synchedBlockNumber: number): Promise<void> {
|
|
237
|
+
if (this.#eventsForJob.size > 0) {
|
|
238
|
+
throw new Error('PXE private event store rollback is not allowed while jobs are running');
|
|
239
|
+
}
|
|
240
|
+
|
|
237
241
|
// First pass: collect all event IDs for all blocks, starting reads during iteration to keep tx alive.
|
|
238
242
|
const eventsByBlock: Map<number, { eventId: string; eventReadPromise: Promise<Buffer | undefined> }[]> = new Map();
|
|
239
243
|
|
|
@@ -49,7 +49,7 @@ export class StoredPrivateEvent {
|
|
|
49
49
|
const msgContentLength = reader.readNumber();
|
|
50
50
|
const msgContent = reader.readArray(msgContentLength, Fr);
|
|
51
51
|
const l2BlockNumber = reader.readNumber();
|
|
52
|
-
const l2BlockHash =
|
|
52
|
+
const l2BlockHash = BlockHash.fromBuffer(reader);
|
|
53
53
|
const txHash = TxHash.fromBuffer(reader);
|
|
54
54
|
const txIndexInBlock = reader.readNumber();
|
|
55
55
|
const eventIndexInTx = reader.readNumber();
|
|
@@ -113,7 +113,9 @@ export async function loadPrivateLogsForSenderRecipientPair(
|
|
|
113
113
|
|
|
114
114
|
if (highestAgedIndex !== undefined && highestAgedIndex > highestFinalizedIndex) {
|
|
115
115
|
// This is just a sanity check as this should never happen.
|
|
116
|
-
throw new Error(
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Highest aged index (${highestAgedIndex}) must not exceed highest finalized index (${highestFinalizedIndex})`,
|
|
118
|
+
);
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
await taggingStore.updateHighestFinalizedIndex(secret, highestFinalizedIndex, jobId);
|