@aztec/pxe 0.0.1-commit.2b2662070 → 0.0.1-commit.2c0ee1788
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_stream_source.d.ts +10 -0
- package/dest/block_synchronizer/block_stream_source.d.ts.map +1 -0
- package/dest/block_synchronizer/block_stream_source.js +37 -0
- 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 +24 -10
- 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 -4
- package/dest/contract_function_simulator/oracle/interfaces.d.ts +3 -1
- package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.d.ts +2 -1
- package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle.js +7 -0
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +10 -11
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +18 -15
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +9 -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 +52 -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/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/oracle_version.d.ts +2 -2
- package/dest/oracle_version.js +2 -2
- 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 +15 -4
- package/dest/pxe.d.ts.map +1 -1
- package/dest/pxe.js +41 -18
- 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/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_stream_source.ts +52 -0
- package/src/block_synchronizer/block_synchronizer.ts +27 -11
- package/src/contract_function_simulator/contract_function_simulator.ts +2 -3
- package/src/contract_function_simulator/oracle/interfaces.ts +10 -0
- package/src/contract_function_simulator/oracle/oracle.ts +14 -0
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +32 -19
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +81 -4
- package/src/contract_function_simulator/pick_notes.ts +13 -1
- package/src/events/event_service.ts +13 -1
- package/src/events/private_event_filter_validator.ts +21 -1
- package/src/oracle_version.ts +2 -2
- 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 +76 -24
- 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/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/pxe",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.2c0ee1788",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"typedocOptions": {
|
|
6
6
|
"entryPoints": [
|
|
@@ -70,19 +70,19 @@
|
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@aztec/bb-prover": "0.0.1-commit.
|
|
74
|
-
"@aztec/bb.js": "0.0.1-commit.
|
|
75
|
-
"@aztec/builder": "0.0.1-commit.
|
|
76
|
-
"@aztec/constants": "0.0.1-commit.
|
|
77
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
78
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
79
|
-
"@aztec/key-store": "0.0.1-commit.
|
|
80
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
81
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
82
|
-
"@aztec/noir-types": "0.0.1-commit.
|
|
83
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
84
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
85
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
73
|
+
"@aztec/bb-prover": "0.0.1-commit.2c0ee1788",
|
|
74
|
+
"@aztec/bb.js": "0.0.1-commit.2c0ee1788",
|
|
75
|
+
"@aztec/builder": "0.0.1-commit.2c0ee1788",
|
|
76
|
+
"@aztec/constants": "0.0.1-commit.2c0ee1788",
|
|
77
|
+
"@aztec/ethereum": "0.0.1-commit.2c0ee1788",
|
|
78
|
+
"@aztec/foundation": "0.0.1-commit.2c0ee1788",
|
|
79
|
+
"@aztec/key-store": "0.0.1-commit.2c0ee1788",
|
|
80
|
+
"@aztec/kv-store": "0.0.1-commit.2c0ee1788",
|
|
81
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.2c0ee1788",
|
|
82
|
+
"@aztec/noir-types": "0.0.1-commit.2c0ee1788",
|
|
83
|
+
"@aztec/protocol-contracts": "0.0.1-commit.2c0ee1788",
|
|
84
|
+
"@aztec/simulator": "0.0.1-commit.2c0ee1788",
|
|
85
|
+
"@aztec/stdlib": "0.0.1-commit.2c0ee1788",
|
|
86
86
|
"koa": "^2.16.1",
|
|
87
87
|
"koa-router": "^13.1.1",
|
|
88
88
|
"lodash.omit": "^4.5.0",
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
|
-
"@aztec/noir-test-contracts.js": "0.0.1-commit.
|
|
95
|
-
"@aztec/world-state": "0.0.1-commit.
|
|
94
|
+
"@aztec/noir-test-contracts.js": "0.0.1-commit.2c0ee1788",
|
|
95
|
+
"@aztec/world-state": "0.0.1-commit.2c0ee1788",
|
|
96
96
|
"@jest/globals": "^30.0.0",
|
|
97
97
|
"@types/jest": "^30.0.0",
|
|
98
98
|
"@types/lodash.omit": "^4.5.7",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { L2Block, type L2BlockSource } from '@aztec/stdlib/block';
|
|
4
|
+
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
5
|
+
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
|
|
6
|
+
|
|
7
|
+
// TODO(spl/new-rpc-api): delete once `L2BlockStream` is refactored to consume the new
|
|
8
|
+
// `BlockResponse` / `CheckpointResponse` shapes. For now the stream requires concrete `L2Block`
|
|
9
|
+
// and `PublishedCheckpoint` instances, so we rehydrate them from RPC responses.
|
|
10
|
+
/**
|
|
11
|
+
* Lifts an {@link AztecNode} RPC client into the shape {@link L2BlockStream} expects. `getBlocks`
|
|
12
|
+
* requests transaction bodies so that real `L2Block` instances can be constructed;
|
|
13
|
+
* `getCheckpoints` requests blocks + L1 info + attestations so that `PublishedCheckpoint`
|
|
14
|
+
* instances are fully populated.
|
|
15
|
+
*/
|
|
16
|
+
export function blockStreamSourceFromAztecNode(
|
|
17
|
+
node: AztecNode,
|
|
18
|
+
): Pick<L2BlockSource, 'getBlocks' | 'getBlockHeader' | 'getL2Tips' | 'getCheckpoints' | 'getCheckpointedBlocks'> {
|
|
19
|
+
return {
|
|
20
|
+
getL2Tips: () => node.getL2Tips(),
|
|
21
|
+
getBlockHeader: number => node.getBlockHeader(number),
|
|
22
|
+
getCheckpointedBlocks: (from: BlockNumber, limit: number) => node.getCheckpointedBlocks(from, limit),
|
|
23
|
+
|
|
24
|
+
async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
|
|
25
|
+
const responses = await node.getBlocks(from, limit, { includeTransactions: true });
|
|
26
|
+
return responses.map(r => new L2Block(r.archive, r.header, r.body!, r.checkpointNumber, r.indexWithinCheckpoint));
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
|
|
30
|
+
const responses = await node.getCheckpoints(from, limit, {
|
|
31
|
+
includeBlocks: true,
|
|
32
|
+
includeTransactions: true,
|
|
33
|
+
includeL1PublishInfo: true,
|
|
34
|
+
includeAttestations: true,
|
|
35
|
+
});
|
|
36
|
+
return responses.map(r => {
|
|
37
|
+
const checkpoint = new Checkpoint(
|
|
38
|
+
r.archive,
|
|
39
|
+
r.header,
|
|
40
|
+
r.blocks!.map(b => new L2Block(b.archive, b.header, b.body!, b.checkpointNumber, b.indexWithinCheckpoint)),
|
|
41
|
+
r.number,
|
|
42
|
+
r.feeAssetPriceModifier,
|
|
43
|
+
);
|
|
44
|
+
const l1 =
|
|
45
|
+
r.l1?.published === true
|
|
46
|
+
? new L1PublishedData(r.l1.blockNumber, r.l1.timestamp, r.l1.blockHash)
|
|
47
|
+
: new L1PublishedData(0n, 0n, Fr.ZERO.toString());
|
|
48
|
+
return new PublishedCheckpoint(checkpoint, l1, r.attestations ?? []);
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -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';
|
|
@@ -11,6 +12,7 @@ import type { ContractSyncService } from '../contract_sync/contract_sync_service
|
|
|
11
12
|
import type { AnchorBlockStore } from '../storage/anchor_block_store/anchor_block_store.js';
|
|
12
13
|
import type { NoteStore } from '../storage/note_store/note_store.js';
|
|
13
14
|
import type { PrivateEventStore } from '../storage/private_event_store/private_event_store.js';
|
|
15
|
+
import { blockStreamSourceFromAztecNode } from './block_stream_source.js';
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* The BlockSynchronizer class orchestrates synchronization between PXE and Aztec node, maintaining an up-to-date
|
|
@@ -20,6 +22,7 @@ import type { PrivateEventStore } from '../storage/private_event_store/private_e
|
|
|
20
22
|
export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
21
23
|
private log: Logger;
|
|
22
24
|
private isSyncing: Promise<void> | undefined;
|
|
25
|
+
private readonly eventQueue = new SerialQueue();
|
|
23
26
|
protected readonly blockStream: L2BlockStream;
|
|
24
27
|
|
|
25
28
|
constructor(
|
|
@@ -35,11 +38,12 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
35
38
|
) {
|
|
36
39
|
this.log = createLogger('pxe:block_synchronizer', bindings);
|
|
37
40
|
this.blockStream = this.createBlockStream(config);
|
|
41
|
+
this.eventQueue.start();
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
protected createBlockStream(config: Partial<BlockSynchronizerConfig>): L2BlockStream {
|
|
41
45
|
return new L2BlockStream(
|
|
42
|
-
this.node,
|
|
46
|
+
blockStreamSourceFromAztecNode(this.node),
|
|
43
47
|
this.l2TipsStore,
|
|
44
48
|
this,
|
|
45
49
|
createLogger('pxe:block_stream', this.log.getBindings()),
|
|
@@ -52,8 +56,12 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
52
56
|
);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
/** Handle events emitted by the block stream. */
|
|
56
|
-
public
|
|
59
|
+
/** Handle events emitted by the block stream. Serialized to prevent concurrent mutations to anchor state. */
|
|
60
|
+
public handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
61
|
+
return this.eventQueue.put(() => this.doHandleBlockStreamEvent(event));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async doHandleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
57
65
|
await this.l2TipsStore.handleBlockStreamEvent(event);
|
|
58
66
|
|
|
59
67
|
switch (event.type) {
|
|
@@ -74,9 +82,9 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
74
82
|
}
|
|
75
83
|
case 'chain-proven': {
|
|
76
84
|
if (this.config.syncChainTip === 'proven') {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
await this.updateAnchorBlockHeader(
|
|
85
|
+
const block = await this.node.getBlock(BlockNumber(event.block.number));
|
|
86
|
+
if (block) {
|
|
87
|
+
await this.updateAnchorBlockHeader(block.header);
|
|
80
88
|
} else {
|
|
81
89
|
this.log.warn(`Block header not found for proven block ${event.block.number}, skipping anchor update`);
|
|
82
90
|
}
|
|
@@ -85,9 +93,9 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
85
93
|
}
|
|
86
94
|
case 'chain-finalized': {
|
|
87
95
|
if (this.config.syncChainTip === 'finalized') {
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
await this.updateAnchorBlockHeader(
|
|
96
|
+
const block = await this.node.getBlock(BlockNumber(event.block.number));
|
|
97
|
+
if (block) {
|
|
98
|
+
await this.updateAnchorBlockHeader(block.header);
|
|
91
99
|
} else {
|
|
92
100
|
this.log.warn(`Block header not found for finalized block ${event.block.number}, skipping anchor update`);
|
|
93
101
|
}
|
|
@@ -110,7 +118,8 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
110
118
|
// Note that the following is not necessarily the anchor block that will be used in the transaction - if
|
|
111
119
|
// the chain has already moved past the reorg, we'll also see blocks-added events that will push the anchor
|
|
112
120
|
// forward.
|
|
113
|
-
const
|
|
121
|
+
const newAnchorBlock = await this.node.getBlock(BlockHash.fromString(event.block.hash));
|
|
122
|
+
const newAnchorBlockHeader = newAnchorBlock?.header;
|
|
114
123
|
|
|
115
124
|
if (!newAnchorBlockHeader) {
|
|
116
125
|
throw new Error(
|
|
@@ -167,6 +176,13 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
|
|
179
|
+
/** Stops the block synchronizer, waiting for any in-progress sync and queued events to complete. */
|
|
180
|
+
public async stop() {
|
|
181
|
+
await this.isSyncing;
|
|
182
|
+
await this.blockStream.stop();
|
|
183
|
+
await this.eventQueue.end();
|
|
184
|
+
}
|
|
185
|
+
|
|
170
186
|
private async doSync() {
|
|
171
187
|
let currentHeader;
|
|
172
188
|
|
|
@@ -177,7 +193,7 @@ export class BlockSynchronizer implements L2BlockStreamEventHandler {
|
|
|
177
193
|
}
|
|
178
194
|
if (!currentHeader) {
|
|
179
195
|
// REFACTOR: We should know the header of the genesis block without having to request it from the node.
|
|
180
|
-
await this.anchorBlockStore.setHeader((await this.node.
|
|
196
|
+
await this.anchorBlockStore.setHeader((await this.node.getBlock(BlockNumber.ZERO))!.header);
|
|
181
197
|
}
|
|
182
198
|
await this.blockStream.sync();
|
|
183
199
|
}
|
|
@@ -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.
|
|
@@ -351,6 +350,7 @@ export class ContractFunctionSimulator {
|
|
|
351
350
|
l2TipsStore: this.l2TipsStore,
|
|
352
351
|
jobId,
|
|
353
352
|
scopes,
|
|
353
|
+
simulator: this.simulator,
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
try {
|
|
@@ -384,7 +384,6 @@ export class ContractFunctionSimulator {
|
|
|
384
384
|
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
|
-
// docs:end:execute_utility_function
|
|
388
387
|
|
|
389
388
|
/**
|
|
390
389
|
* Returns the execution statistics collected during the simulator run.
|
|
@@ -164,6 +164,11 @@ export interface IUtilityExecutionOracle {
|
|
|
164
164
|
getSharedSecret(address: AztecAddress, ephPk: Point, contractAddress: AztecAddress): Promise<Fr>;
|
|
165
165
|
setContractSyncCacheInvalid(contractAddress: AztecAddress, scopes: AztecAddress[]): void;
|
|
166
166
|
emitOffchainEffect(data: Fr[]): Promise<void>;
|
|
167
|
+
callUtilityFunction(
|
|
168
|
+
targetContractAddress: AztecAddress,
|
|
169
|
+
functionSelector: FunctionSelector,
|
|
170
|
+
args: Fr[],
|
|
171
|
+
): Promise<Fr[]>;
|
|
167
172
|
|
|
168
173
|
// Ephemeral array methods
|
|
169
174
|
pushEphemeral(slot: Fr, elements: Fr[]): number;
|
|
@@ -204,6 +209,11 @@ export interface IPrivateExecutionOracle {
|
|
|
204
209
|
sideEffectCounter: number,
|
|
205
210
|
isStaticCall: boolean,
|
|
206
211
|
): Promise<{ endSideEffectCounter: Fr; returnsHash: Fr }>;
|
|
212
|
+
callUtilityFunction(
|
|
213
|
+
targetContractAddress: AztecAddress,
|
|
214
|
+
functionSelector: FunctionSelector,
|
|
215
|
+
args: Fr[],
|
|
216
|
+
): Promise<Fr[]>;
|
|
207
217
|
assertValidPublicCalldata(calldataHash: Fr): Promise<void>;
|
|
208
218
|
notifyRevertiblePhaseStart(minRevertibleSideEffectCounter: number): Promise<void>;
|
|
209
219
|
isExecutionInRevertiblePhase(sideEffectCounter: number): Promise<boolean>;
|
|
@@ -490,6 +490,20 @@ export class Oracle {
|
|
|
490
490
|
return [values.map(toACVMField)];
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
// eslint-disable-next-line camelcase
|
|
494
|
+
async aztec_utl_callUtilityFunction(
|
|
495
|
+
[contractAddress]: ACVMField[],
|
|
496
|
+
[functionSelector]: ACVMField[],
|
|
497
|
+
args: ACVMField[],
|
|
498
|
+
): Promise<ACVMField[][]> {
|
|
499
|
+
const result = await this.handlerAsUtility().callUtilityFunction(
|
|
500
|
+
AztecAddress.fromField(Fr.fromString(contractAddress)),
|
|
501
|
+
FunctionSelector.fromField(Fr.fromString(functionSelector)),
|
|
502
|
+
args.map(Fr.fromString),
|
|
503
|
+
);
|
|
504
|
+
return [result.map(toACVMField)];
|
|
505
|
+
}
|
|
506
|
+
|
|
493
507
|
// eslint-disable-next-line camelcase
|
|
494
508
|
aztec_prv_notifyCreatedContractClassLog(
|
|
495
509
|
[contractAddress]: ACVMField[],
|
|
@@ -2,7 +2,7 @@ import { MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS, PRIVATE_CONTEXT_INPUTS_LENGTH }
|
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { Timer } from '@aztec/foundation/timer';
|
|
5
|
-
import {
|
|
5
|
+
import { toACVMWitness } from '@aztec/simulator/client';
|
|
6
6
|
import {
|
|
7
7
|
type FunctionAbi,
|
|
8
8
|
type FunctionArtifact,
|
|
@@ -50,7 +50,6 @@ export type PrivateExecutionOracleArgs = Omit<UtilityExecutionOracleArgs, 'contr
|
|
|
50
50
|
totalPublicCalldataCount?: number;
|
|
51
51
|
sideEffectCounter?: number;
|
|
52
52
|
senderForTags?: AztecAddress;
|
|
53
|
-
simulator?: CircuitSimulator;
|
|
54
53
|
};
|
|
55
54
|
|
|
56
55
|
/**
|
|
@@ -81,9 +80,11 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
81
80
|
private readonly taggingIndexCache: ExecutionTaggingIndexCache;
|
|
82
81
|
private readonly senderTaggingStore: SenderTaggingStore;
|
|
83
82
|
private totalPublicCalldataCount: number;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
private readonly
|
|
83
|
+
private readonly initialSideEffectCounter: number;
|
|
84
|
+
/** Sender for tags passed in at oracle construction time. Returned by `getSenderForTags` unless overridden. */
|
|
85
|
+
private readonly defaultSenderForTags: AztecAddress | undefined;
|
|
86
|
+
/** Per-call sender-for-tags override, set by `setSenderForTags`. Takes precedence over `defaultSenderForTags`. */
|
|
87
|
+
private currentSenderForTags: AztecAddress | undefined;
|
|
87
88
|
|
|
88
89
|
constructor(args: PrivateExecutionOracleArgs) {
|
|
89
90
|
super({
|
|
@@ -100,13 +101,17 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
100
101
|
this.taggingIndexCache = args.taggingIndexCache;
|
|
101
102
|
this.senderTaggingStore = args.senderTaggingStore;
|
|
102
103
|
this.totalPublicCalldataCount = args.totalPublicCalldataCount ?? 0;
|
|
103
|
-
this.
|
|
104
|
-
this.
|
|
105
|
-
this.simulator = args.simulator;
|
|
104
|
+
this.initialSideEffectCounter = args.sideEffectCounter ?? 0;
|
|
105
|
+
this.defaultSenderForTags = args.senderForTags;
|
|
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
|
|
@@ -173,11 +178,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
173
178
|
* for a tag in order to emit a log. Constrained tagging should not use this as there is no
|
|
174
179
|
* guarantee that the recipient knows about the sender, and hence about the shared secret.
|
|
175
180
|
*
|
|
176
|
-
*
|
|
177
|
-
* 'senderForTags' value (unless it is replaced).
|
|
181
|
+
* Returns `currentSenderForTags` if set (via `setSenderForTags`), otherwise `defaultSenderForTags`.
|
|
178
182
|
*/
|
|
179
183
|
public getSenderForTags(): Promise<AztecAddress | undefined> {
|
|
180
|
-
return Promise.resolve(this.
|
|
184
|
+
return Promise.resolve(this.currentSenderForTags ?? this.defaultSenderForTags);
|
|
181
185
|
}
|
|
182
186
|
|
|
183
187
|
/**
|
|
@@ -187,12 +191,14 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
187
191
|
* for a tag in order to emit a log. Constrained tagging should not use this as there is no
|
|
188
192
|
* guarantee that the recipient knows about the sender, and hence about the shared secret.
|
|
189
193
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* value (unless it is replaced by another call to this setter).
|
|
194
|
+
* Overrides `defaultSenderForTags` for the remainder of this call. Each oracle instance is
|
|
195
|
+
* independent, so this has no effect on any other call in the execution.
|
|
193
196
|
*/
|
|
194
197
|
public setSenderForTags(senderForTags: AztecAddress): Promise<void> {
|
|
195
|
-
this.
|
|
198
|
+
this.logger.debug(
|
|
199
|
+
`Sender for tags switched to ${senderForTags} by contract ${this.contractAddress} (default was ${this.defaultSenderForTags})`,
|
|
200
|
+
);
|
|
201
|
+
this.currentSenderForTags = senderForTags;
|
|
196
202
|
return Promise.resolve();
|
|
197
203
|
}
|
|
198
204
|
|
|
@@ -573,21 +579,24 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
573
579
|
sideEffectCounter,
|
|
574
580
|
log: this.logger,
|
|
575
581
|
scopes: this.scopes,
|
|
576
|
-
senderForTags: this.
|
|
577
|
-
simulator: this.simulator
|
|
582
|
+
senderForTags: this.defaultSenderForTags,
|
|
583
|
+
simulator: this.simulator,
|
|
578
584
|
l2TipsStore: this.l2TipsStore,
|
|
579
585
|
});
|
|
580
586
|
|
|
581
587
|
const setupTime = simulatorSetupTimer.ms();
|
|
582
588
|
|
|
583
589
|
const childExecutionResult = await executePrivateFunction(
|
|
584
|
-
this.simulator
|
|
590
|
+
this.simulator,
|
|
585
591
|
privateExecutionOracle,
|
|
586
592
|
targetArtifact,
|
|
587
593
|
targetContractAddress,
|
|
588
594
|
functionSelector,
|
|
589
595
|
);
|
|
590
596
|
|
|
597
|
+
// Propagate the nested call's calldata count so the parent sees its increments on subsequent enqueues.
|
|
598
|
+
this.totalPublicCalldataCount = privateExecutionOracle.getTotalPublicCalldataCount();
|
|
599
|
+
|
|
591
600
|
if (isStaticCall) {
|
|
592
601
|
this.#checkValidStaticCall(childExecutionResult);
|
|
593
602
|
}
|
|
@@ -621,6 +630,10 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
621
630
|
return Promise.resolve();
|
|
622
631
|
}
|
|
623
632
|
|
|
633
|
+
public getTotalPublicCalldataCount(): number {
|
|
634
|
+
return this.totalPublicCalldataCount;
|
|
635
|
+
}
|
|
636
|
+
|
|
624
637
|
public notifyRevertiblePhaseStart(minRevertibleSideEffectCounter: number): Promise<void> {
|
|
625
638
|
return this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
626
639
|
}
|
|
@@ -7,6 +7,15 @@ 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
9
|
import { isProtocolContract } from '@aztec/protocol-contracts';
|
|
10
|
+
import {
|
|
11
|
+
type CircuitSimulator,
|
|
12
|
+
ExecutionError,
|
|
13
|
+
extractCallStack,
|
|
14
|
+
resolveAssertionMessageFromError,
|
|
15
|
+
toACVMWitness,
|
|
16
|
+
witnessMapToFields,
|
|
17
|
+
} from '@aztec/simulator/client';
|
|
18
|
+
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
10
19
|
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
|
|
11
20
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
12
21
|
import { BlockHash, type L2TipsProvider } from '@aztec/stdlib/block';
|
|
@@ -44,6 +53,7 @@ import { UtilityContext } from '../noir-structs/utility_context.js';
|
|
|
44
53
|
import { pickNotes } from '../pick_notes.js';
|
|
45
54
|
import type { IMiscOracle, IUtilityExecutionOracle, NoteData } from './interfaces.js';
|
|
46
55
|
import { MessageLoadOracleInputs } from './message_load_oracle_inputs.js';
|
|
56
|
+
import { Oracle } from './oracle.js';
|
|
47
57
|
|
|
48
58
|
/** Args for UtilityExecutionOracle constructor. */
|
|
49
59
|
export type UtilityExecutionOracleArgs = {
|
|
@@ -67,6 +77,7 @@ export type UtilityExecutionOracleArgs = {
|
|
|
67
77
|
jobId: string;
|
|
68
78
|
log?: ReturnType<typeof createLogger>;
|
|
69
79
|
scopes: AztecAddress[];
|
|
80
|
+
simulator: CircuitSimulator;
|
|
70
81
|
};
|
|
71
82
|
|
|
72
83
|
/**
|
|
@@ -103,6 +114,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
103
114
|
protected readonly jobId: string;
|
|
104
115
|
protected logger: ReturnType<typeof createLogger>;
|
|
105
116
|
protected readonly scopes: AztecAddress[];
|
|
117
|
+
protected readonly simulator: CircuitSimulator;
|
|
106
118
|
|
|
107
119
|
constructor(args: UtilityExecutionOracleArgs) {
|
|
108
120
|
this.contractAddress = args.contractAddress;
|
|
@@ -124,6 +136,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
124
136
|
this.jobId = args.jobId;
|
|
125
137
|
this.logger = args.log ?? createLogger('simulator:client_view_context');
|
|
126
138
|
this.scopes = args.scopes;
|
|
139
|
+
this.simulator = args.simulator;
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
public assertCompatibleOracleVersion(major: number, minor: number): void {
|
|
@@ -333,10 +346,9 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
333
346
|
}
|
|
334
347
|
|
|
335
348
|
/**
|
|
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.
|
|
349
|
+
* Returns an auth witness for the given message hash from the list of transient witnesses for this transaction.
|
|
338
350
|
* @param messageHash - Hash of the message to authenticate.
|
|
339
|
-
* @returns Authentication witness for the requested message hash.
|
|
351
|
+
* @returns Authentication witness for the requested message hash, or undefined if not found.
|
|
340
352
|
*/
|
|
341
353
|
public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
|
|
342
354
|
return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
|
|
@@ -895,6 +907,70 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
895
907
|
return Promise.resolve();
|
|
896
908
|
}
|
|
897
909
|
|
|
910
|
+
/** Executes another utility function from within this one and returns its serialized return values. */
|
|
911
|
+
public async callUtilityFunction(
|
|
912
|
+
targetContractAddress: AztecAddress,
|
|
913
|
+
functionSelector: FunctionSelector,
|
|
914
|
+
args: Fr[],
|
|
915
|
+
): Promise<Fr[]> {
|
|
916
|
+
// TODO(F-29): We want to support cross-contract utility calls, but doing so safely requires wallets to have
|
|
917
|
+
// a way to authorize which contracts can be called transitively, since those calls may expose private state.
|
|
918
|
+
// Until that is in place, restrict nested utility calls to the same contract only.
|
|
919
|
+
if (!targetContractAddress.equals(this.contractAddress)) {
|
|
920
|
+
throw new Error(
|
|
921
|
+
`Cross-contract utility calls are not yet supported: cannot call ${targetContractAddress} from utility function on ${this.contractAddress}.`,
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
this.logger.debug(
|
|
926
|
+
`Calling nested utility function ${targetContractAddress}:${functionSelector} from ${this.contractAddress}`,
|
|
927
|
+
);
|
|
928
|
+
|
|
929
|
+
const targetArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(
|
|
930
|
+
targetContractAddress,
|
|
931
|
+
functionSelector,
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
const nestedOracle = new UtilityExecutionOracle({
|
|
935
|
+
contractAddress: targetContractAddress,
|
|
936
|
+
authWitnesses: this.authWitnesses,
|
|
937
|
+
capsules: this.capsules,
|
|
938
|
+
anchorBlockHeader: this.anchorBlockHeader,
|
|
939
|
+
contractStore: this.contractStore,
|
|
940
|
+
noteStore: this.noteStore,
|
|
941
|
+
keyStore: this.keyStore,
|
|
942
|
+
addressStore: this.addressStore,
|
|
943
|
+
aztecNode: this.aztecNode,
|
|
944
|
+
recipientTaggingStore: this.recipientTaggingStore,
|
|
945
|
+
senderAddressBookStore: this.senderAddressBookStore,
|
|
946
|
+
capsuleService: this.capsuleService,
|
|
947
|
+
privateEventStore: this.privateEventStore,
|
|
948
|
+
messageContextService: this.messageContextService,
|
|
949
|
+
contractSyncService: this.contractSyncService,
|
|
950
|
+
l2TipsStore: this.l2TipsStore,
|
|
951
|
+
jobId: this.jobId,
|
|
952
|
+
scopes: this.scopes,
|
|
953
|
+
simulator: this.simulator,
|
|
954
|
+
log: this.logger,
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
const initialWitness = toACVMWitness(0, args);
|
|
958
|
+
const acvmCallback = new Oracle(nestedOracle);
|
|
959
|
+
const acirExecutionResult = await this.simulator
|
|
960
|
+
.executeUserCircuit(initialWitness, targetArtifact, acvmCallback.toACIRCallback())
|
|
961
|
+
.catch((err: Error) => {
|
|
962
|
+
err.message = resolveAssertionMessageFromError(err, targetArtifact);
|
|
963
|
+
throw new ExecutionError(
|
|
964
|
+
err.message,
|
|
965
|
+
{ contractAddress: targetContractAddress, functionSelector },
|
|
966
|
+
extractCallStack(err, targetArtifact.debug),
|
|
967
|
+
{ cause: err },
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
return witnessMapToFields(acirExecutionResult.returnWitness);
|
|
972
|
+
}
|
|
973
|
+
|
|
898
974
|
/** Returns offchain effects collected during execution. */
|
|
899
975
|
public getOffchainEffects(): OffchainEffect[] {
|
|
900
976
|
return this.offchainEffects;
|
|
@@ -905,7 +981,8 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
905
981
|
const [response] = await Promise.all([
|
|
906
982
|
query(),
|
|
907
983
|
(async () => {
|
|
908
|
-
const
|
|
984
|
+
const block = await this.aztecNode.getBlock(blockHash);
|
|
985
|
+
const header = block?.header;
|
|
909
986
|
if (!header) {
|
|
910
987
|
throw new Error(`Could not find block header for block hash ${blockHash}`);
|
|
911
988
|
}
|
|
@@ -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
|
|
|
@@ -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,
|