@aztec/pxe 0.63.0 → 0.64.0
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/database/contracts/contract_artifact_db.d.ts +1 -0
- package/dest/database/contracts/contract_artifact_db.d.ts.map +1 -1
- package/dest/database/incoming_note_dao.d.ts +10 -1
- package/dest/database/incoming_note_dao.d.ts.map +1 -1
- package/dest/database/incoming_note_dao.js +18 -5
- package/dest/database/kv_pxe_database.d.ts +6 -3
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +123 -22
- package/dest/database/outgoing_note_dao.d.ts +10 -1
- package/dest/database/outgoing_note_dao.d.ts.map +1 -1
- package/dest/database/outgoing_note_dao.js +18 -5
- package/dest/database/pxe_database.d.ts +22 -5
- package/dest/database/pxe_database.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.js +65 -16
- package/dest/kernel_oracle/index.js +2 -2
- package/dest/note_decryption_utils/produce_note_daos.d.ts +1 -1
- package/dest/note_decryption_utils/produce_note_daos.d.ts.map +1 -1
- package/dest/note_decryption_utils/produce_note_daos.js +5 -5
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +1 -1
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +1 -1
- package/dest/note_decryption_utils/produce_note_daos_for_key.js +3 -3
- package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/create_pxe_service.js +6 -3
- package/dest/pxe_service/pxe_service.d.ts +5 -4
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +51 -25
- package/dest/simulator_oracle/index.d.ts +8 -0
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +67 -10
- package/dest/synchronizer/synchronizer.d.ts +13 -28
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +42 -64
- package/package.json +14 -14
- package/src/database/contracts/contract_artifact_db.ts +1 -0
- package/src/database/incoming_note_dao.ts +46 -1
- package/src/database/kv_pxe_database.ts +148 -23
- package/src/database/outgoing_note_dao.ts +43 -1
- package/src/database/pxe_database.ts +25 -5
- package/src/database/pxe_database_test_suite.ts +79 -17
- package/src/kernel_oracle/index.ts +1 -1
- package/src/note_decryption_utils/produce_note_daos.ts +8 -0
- package/src/note_decryption_utils/produce_note_daos_for_key.ts +5 -1
- package/src/pxe_service/create_pxe_service.ts +7 -4
- package/src/pxe_service/pxe_service.ts +109 -72
- package/src/simulator_oracle/index.ts +95 -17
- package/src/synchronizer/synchronizer.ts +60 -71
|
@@ -13,6 +13,8 @@ export async function produceNoteDaosForKey<T>(
|
|
|
13
13
|
pkM: PublicKey,
|
|
14
14
|
payload: L1NotePayload,
|
|
15
15
|
txHash: TxHash,
|
|
16
|
+
l2BlockNumber: number,
|
|
17
|
+
l2BlockHash: string,
|
|
16
18
|
noteHashes: Fr[],
|
|
17
19
|
dataStartIndexForTx: number,
|
|
18
20
|
excludedIndices: Set<number>,
|
|
@@ -21,6 +23,8 @@ export async function produceNoteDaosForKey<T>(
|
|
|
21
23
|
note: Note,
|
|
22
24
|
payload: L1NotePayload,
|
|
23
25
|
noteInfo: NoteInfo,
|
|
26
|
+
l2BlockNumber: number,
|
|
27
|
+
l2BlockHash: string,
|
|
24
28
|
dataStartIndexForTx: number,
|
|
25
29
|
pkM: PublicKey,
|
|
26
30
|
) => T,
|
|
@@ -44,7 +48,7 @@ export async function produceNoteDaosForKey<T>(
|
|
|
44
48
|
);
|
|
45
49
|
excludedIndices?.add(noteInfo.noteHashIndex);
|
|
46
50
|
|
|
47
|
-
noteDao = daoConstructor(note, payload, noteInfo, dataStartIndexForTx, pkM);
|
|
51
|
+
noteDao = daoConstructor(note, payload, noteInfo, l2BlockNumber, l2BlockHash, dataStartIndexForTx, pkM);
|
|
48
52
|
} catch (e) {
|
|
49
53
|
logger.error(`Could not process note because of "${e}". Discarding note...`);
|
|
50
54
|
}
|
|
@@ -3,6 +3,7 @@ import { type AztecNode, type PrivateKernelProver } from '@aztec/circuit-types';
|
|
|
3
3
|
import { randomBytes } from '@aztec/foundation/crypto';
|
|
4
4
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { KeyStore } from '@aztec/key-store';
|
|
6
|
+
import { L2TipsStore } from '@aztec/kv-store/stores';
|
|
6
7
|
import { createStore } from '@aztec/kv-store/utils';
|
|
7
8
|
|
|
8
9
|
import { type PXEServiceConfig } from '../config/index.js';
|
|
@@ -39,12 +40,14 @@ export async function createPXEService(
|
|
|
39
40
|
const keyStore = new KeyStore(
|
|
40
41
|
await createStore('pxe_key_store', configWithContracts, createDebugLogger('aztec:pxe:keystore:lmdb')),
|
|
41
42
|
);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
|
|
44
|
+
const store = await createStore('pxe_data', configWithContracts, createDebugLogger('aztec:pxe:data:lmdb'));
|
|
45
|
+
|
|
46
|
+
const db = new KVPxeDatabase(store);
|
|
47
|
+
const tips = new L2TipsStore(store, 'pxe');
|
|
45
48
|
|
|
46
49
|
const prover = proofCreator ?? (await createProver(config, logSuffix));
|
|
47
|
-
const server = new PXEService(keyStore, aztecNode, db, prover, config, logSuffix);
|
|
50
|
+
const server = new PXEService(keyStore, aztecNode, db, tips, prover, config, logSuffix);
|
|
48
51
|
await server.start();
|
|
49
52
|
return server;
|
|
50
53
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ExtendedNote,
|
|
7
7
|
type FunctionCall,
|
|
8
8
|
type GetUnencryptedLogsResponse,
|
|
9
|
+
type InBlock,
|
|
9
10
|
type IncomingNotesFilter,
|
|
10
11
|
L1EventPayload,
|
|
11
12
|
type L2Block,
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
type SiblingPath,
|
|
23
24
|
SimulationError,
|
|
24
25
|
type Tx,
|
|
25
|
-
type TxEffect,
|
|
26
26
|
type TxExecutionRequest,
|
|
27
27
|
type TxHash,
|
|
28
28
|
TxProvingResult,
|
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
computeAddressSecret,
|
|
44
44
|
computeContractAddressFromInstance,
|
|
45
45
|
computeContractClassId,
|
|
46
|
-
computePoint,
|
|
47
46
|
getContractClassFromArtifact,
|
|
48
47
|
} from '@aztec/circuits.js';
|
|
49
48
|
import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
|
|
@@ -58,6 +57,7 @@ import { Fr, type Point } from '@aztec/foundation/fields';
|
|
|
58
57
|
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
59
58
|
import { SerialQueue } from '@aztec/foundation/queue';
|
|
60
59
|
import { type KeyStore } from '@aztec/key-store';
|
|
60
|
+
import { type L2TipsStore } from '@aztec/kv-store/stores';
|
|
61
61
|
import {
|
|
62
62
|
ProtocolContractAddress,
|
|
63
63
|
getCanonicalProtocolContract,
|
|
@@ -93,12 +93,13 @@ export class PXEService implements PXE {
|
|
|
93
93
|
private keyStore: KeyStore,
|
|
94
94
|
private node: AztecNode,
|
|
95
95
|
private db: PxeDatabase,
|
|
96
|
+
tipsStore: L2TipsStore,
|
|
96
97
|
private proofCreator: PrivateKernelProver,
|
|
97
|
-
|
|
98
|
+
config: PXEServiceConfig,
|
|
98
99
|
logSuffix?: string,
|
|
99
100
|
) {
|
|
100
101
|
this.log = createDebugLogger(logSuffix ? `aztec:pxe_service_${logSuffix}` : `aztec:pxe_service`);
|
|
101
|
-
this.synchronizer = new Synchronizer(node, db,
|
|
102
|
+
this.synchronizer = new Synchronizer(node, db, tipsStore, config, logSuffix);
|
|
102
103
|
this.contractDataOracle = new ContractDataOracle(db);
|
|
103
104
|
this.simulator = getAcirSimulator(db, node, keyStore, this.contractDataOracle);
|
|
104
105
|
this.packageVersion = getPackageInfo().version;
|
|
@@ -112,8 +113,7 @@ export class PXEService implements PXE {
|
|
|
112
113
|
* @returns A promise that resolves when the server has started successfully.
|
|
113
114
|
*/
|
|
114
115
|
public async start() {
|
|
115
|
-
|
|
116
|
-
await this.synchronizer.start(1, l2BlockPollingIntervalMS);
|
|
116
|
+
await this.synchronizer.start();
|
|
117
117
|
await this.#registerProtocolContracts();
|
|
118
118
|
const info = await this.getNodeInfo();
|
|
119
119
|
this.log.info(`Started PXE connected to chain ${info.l1ChainId} version ${info.protocolVersion}`);
|
|
@@ -242,21 +242,24 @@ export class PXEService implements PXE {
|
|
|
242
242
|
|
|
243
243
|
if (artifact) {
|
|
244
244
|
// If the user provides an artifact, validate it against the expected class id and register it
|
|
245
|
-
const
|
|
245
|
+
const contractClass = getContractClassFromArtifact(artifact);
|
|
246
|
+
const contractClassId = computeContractClassId(contractClass);
|
|
246
247
|
if (!contractClassId.equals(instance.contractClassId)) {
|
|
247
248
|
throw new Error(
|
|
248
249
|
`Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`,
|
|
249
250
|
);
|
|
250
251
|
}
|
|
251
|
-
if (
|
|
252
|
-
// Computed address from the instance does not match address inside instance
|
|
253
|
-
!computeContractAddressFromInstance(instance).equals(instance.address)
|
|
254
|
-
) {
|
|
252
|
+
if (!computeContractAddressFromInstance(instance).equals(instance.address)) {
|
|
255
253
|
throw new Error('Added a contract in which the address does not match the contract instance.');
|
|
256
254
|
}
|
|
257
255
|
|
|
258
256
|
await this.db.addContractArtifact(contractClassId, artifact);
|
|
257
|
+
|
|
258
|
+
// TODO: PXE may not want to broadcast the artifact to the network
|
|
259
259
|
await this.node.addContractArtifact(instance.address, artifact);
|
|
260
|
+
|
|
261
|
+
// TODO(#10007): Node should get public contract class from the registration event, not from PXE registration
|
|
262
|
+
await this.node.addContractClass({ ...contractClass, privateFunctions: [], unconstrainedFunctions: [] });
|
|
260
263
|
} else {
|
|
261
264
|
// Otherwise, make sure there is an artifact already registered for that class id
|
|
262
265
|
artifact = await this.db.getContractArtifact(instance.contractClassId);
|
|
@@ -289,7 +292,7 @@ export class PXEService implements PXE {
|
|
|
289
292
|
let owner = filter.owner;
|
|
290
293
|
if (owner === undefined) {
|
|
291
294
|
const completeAddresses = (await this.db.getCompleteAddresses()).find(completeAddress =>
|
|
292
|
-
|
|
295
|
+
completeAddress.address.toAddressPoint().equals(dao.addressPoint),
|
|
293
296
|
);
|
|
294
297
|
if (completeAddresses === undefined) {
|
|
295
298
|
throw new Error(`Cannot find complete address for addressPoint ${dao.addressPoint.toString()}`);
|
|
@@ -350,7 +353,7 @@ export class PXEService implements PXE {
|
|
|
350
353
|
throw new Error(`Unknown account: ${note.owner.toString()}`);
|
|
351
354
|
}
|
|
352
355
|
|
|
353
|
-
const nonces = await this.#getNoteNonces(note);
|
|
356
|
+
const { data: nonces, l2BlockNumber, l2BlockHash } = await this.#getNoteNonces(note);
|
|
354
357
|
if (nonces.length === 0) {
|
|
355
358
|
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
|
|
356
359
|
}
|
|
@@ -385,11 +388,13 @@ export class PXEService implements PXE {
|
|
|
385
388
|
note.storageSlot,
|
|
386
389
|
note.noteTypeId,
|
|
387
390
|
note.txHash,
|
|
391
|
+
l2BlockNumber,
|
|
392
|
+
l2BlockHash,
|
|
388
393
|
nonce,
|
|
389
394
|
noteHash,
|
|
390
395
|
siloedNullifier,
|
|
391
396
|
index,
|
|
392
|
-
|
|
397
|
+
owner.address.toAddressPoint(),
|
|
393
398
|
),
|
|
394
399
|
scope,
|
|
395
400
|
);
|
|
@@ -397,7 +402,7 @@ export class PXEService implements PXE {
|
|
|
397
402
|
}
|
|
398
403
|
|
|
399
404
|
public async addNullifiedNote(note: ExtendedNote) {
|
|
400
|
-
const nonces = await this.#getNoteNonces(note);
|
|
405
|
+
const { data: nonces, l2BlockHash, l2BlockNumber } = await this.#getNoteNonces(note);
|
|
401
406
|
if (nonces.length === 0) {
|
|
402
407
|
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
|
|
403
408
|
}
|
|
@@ -428,11 +433,13 @@ export class PXEService implements PXE {
|
|
|
428
433
|
note.storageSlot,
|
|
429
434
|
note.noteTypeId,
|
|
430
435
|
note.txHash,
|
|
436
|
+
l2BlockNumber,
|
|
437
|
+
l2BlockHash,
|
|
431
438
|
nonce,
|
|
432
439
|
noteHash,
|
|
433
440
|
Fr.ZERO, // We are not able to derive
|
|
434
441
|
index,
|
|
435
|
-
|
|
442
|
+
note.owner.toAddressPoint(),
|
|
436
443
|
),
|
|
437
444
|
);
|
|
438
445
|
}
|
|
@@ -444,15 +451,15 @@ export class PXEService implements PXE {
|
|
|
444
451
|
* @returns The nonces of the note.
|
|
445
452
|
* @remarks More than a single nonce may be returned since there might be more than one nonce for a given note.
|
|
446
453
|
*/
|
|
447
|
-
async #getNoteNonces(note: ExtendedNote): Promise<Fr[]
|
|
454
|
+
async #getNoteNonces(note: ExtendedNote): Promise<InBlock<Fr[]>> {
|
|
448
455
|
const tx = await this.node.getTxEffect(note.txHash);
|
|
449
456
|
if (!tx) {
|
|
450
457
|
throw new Error(`Unknown tx: ${note.txHash}`);
|
|
451
458
|
}
|
|
452
459
|
|
|
453
460
|
const nonces: Fr[] = [];
|
|
454
|
-
const firstNullifier = tx.nullifiers[0];
|
|
455
|
-
const hashes = tx.noteHashes;
|
|
461
|
+
const firstNullifier = tx.data.nullifiers[0];
|
|
462
|
+
const hashes = tx.data.noteHashes;
|
|
456
463
|
for (let i = 0; i < hashes.length; ++i) {
|
|
457
464
|
const hash = hashes[i];
|
|
458
465
|
if (hash.equals(Fr.ZERO)) {
|
|
@@ -473,7 +480,7 @@ export class PXEService implements PXE {
|
|
|
473
480
|
}
|
|
474
481
|
}
|
|
475
482
|
|
|
476
|
-
return nonces;
|
|
483
|
+
return { l2BlockHash: tx.l2BlockHash, l2BlockNumber: tx.l2BlockNumber, data: nonces };
|
|
477
484
|
}
|
|
478
485
|
|
|
479
486
|
public async getBlock(blockNumber: number): Promise<L2Block | undefined> {
|
|
@@ -496,10 +503,19 @@ export class PXEService implements PXE {
|
|
|
496
503
|
txRequest: TxExecutionRequest,
|
|
497
504
|
privateExecutionResult: PrivateExecutionResult,
|
|
498
505
|
): Promise<TxProvingResult> {
|
|
499
|
-
return this.jobQueue
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
506
|
+
return this.jobQueue
|
|
507
|
+
.put(async () => {
|
|
508
|
+
const { publicInputs, clientIvcProof } = await this.#prove(
|
|
509
|
+
txRequest,
|
|
510
|
+
this.proofCreator,
|
|
511
|
+
privateExecutionResult,
|
|
512
|
+
);
|
|
513
|
+
return new TxProvingResult(privateExecutionResult, publicInputs, clientIvcProof!);
|
|
514
|
+
})
|
|
515
|
+
.catch(err => {
|
|
516
|
+
this.log.error(err);
|
|
517
|
+
throw err;
|
|
518
|
+
});
|
|
503
519
|
}
|
|
504
520
|
|
|
505
521
|
// TODO(#7456) Prevent msgSender being defined here for the first call
|
|
@@ -511,47 +527,52 @@ export class PXEService implements PXE {
|
|
|
511
527
|
profile: boolean = false,
|
|
512
528
|
scopes?: AztecAddress[],
|
|
513
529
|
): Promise<TxSimulationResult> {
|
|
514
|
-
return await this.jobQueue
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
({
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
530
|
+
return await this.jobQueue
|
|
531
|
+
.put(async () => {
|
|
532
|
+
const privateExecutionResult = await this.#executePrivate(txRequest, msgSender, scopes);
|
|
533
|
+
|
|
534
|
+
let publicInputs: PrivateKernelTailCircuitPublicInputs;
|
|
535
|
+
let profileResult;
|
|
536
|
+
if (profile) {
|
|
537
|
+
({ publicInputs, profileResult } = await this.#profileKernelProver(
|
|
538
|
+
txRequest,
|
|
539
|
+
this.proofCreator,
|
|
540
|
+
privateExecutionResult,
|
|
541
|
+
));
|
|
542
|
+
} else {
|
|
543
|
+
publicInputs = await this.#simulateKernels(txRequest, privateExecutionResult);
|
|
544
|
+
}
|
|
528
545
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
546
|
+
const privateSimulationResult = new PrivateSimulationResult(privateExecutionResult, publicInputs);
|
|
547
|
+
const simulatedTx = privateSimulationResult.toSimulatedTx();
|
|
548
|
+
let publicOutput: PublicSimulationOutput | undefined;
|
|
549
|
+
if (simulatePublic) {
|
|
550
|
+
publicOutput = await this.#simulatePublicCalls(simulatedTx);
|
|
551
|
+
}
|
|
535
552
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
553
|
+
if (!skipTxValidation) {
|
|
554
|
+
if (!(await this.node.isValidTx(simulatedTx, true))) {
|
|
555
|
+
throw new Error('The simulated transaction is unable to be added to state and is invalid.');
|
|
556
|
+
}
|
|
539
557
|
}
|
|
540
|
-
}
|
|
541
558
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
559
|
+
// We log only if the msgSender is undefined, as simulating with a different msgSender
|
|
560
|
+
// is unlikely to be a real transaction, and likely to be only used to read data.
|
|
561
|
+
// Meaning that it will not necessarily have produced a nullifier (and thus have no TxHash)
|
|
562
|
+
// If we log, the `getTxHash` function will throw.
|
|
563
|
+
if (!msgSender) {
|
|
564
|
+
this.log.info(`Executed local simulation for ${simulatedTx.getTxHash()}`);
|
|
565
|
+
}
|
|
566
|
+
return TxSimulationResult.fromPrivateSimulationResultAndPublicOutput(
|
|
567
|
+
privateSimulationResult,
|
|
568
|
+
publicOutput,
|
|
569
|
+
profileResult,
|
|
570
|
+
);
|
|
571
|
+
})
|
|
572
|
+
.catch(err => {
|
|
573
|
+
this.log.error(err);
|
|
574
|
+
throw err;
|
|
575
|
+
});
|
|
555
576
|
}
|
|
556
577
|
|
|
557
578
|
public async sendTx(tx: Tx): Promise<TxHash> {
|
|
@@ -560,7 +581,10 @@ export class PXEService implements PXE {
|
|
|
560
581
|
throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`);
|
|
561
582
|
}
|
|
562
583
|
this.log.info(`Sending transaction ${txHash}`);
|
|
563
|
-
await this.node.sendTx(tx)
|
|
584
|
+
await this.node.sendTx(tx).catch(err => {
|
|
585
|
+
this.log.error(err);
|
|
586
|
+
throw err;
|
|
587
|
+
});
|
|
564
588
|
this.log.info(`Sent transaction ${txHash}`);
|
|
565
589
|
return txHash;
|
|
566
590
|
}
|
|
@@ -573,21 +597,26 @@ export class PXEService implements PXE {
|
|
|
573
597
|
scopes?: AztecAddress[],
|
|
574
598
|
): Promise<AbiDecoded> {
|
|
575
599
|
// all simulations must be serialized w.r.t. the synchronizer
|
|
576
|
-
return await this.jobQueue
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
600
|
+
return await this.jobQueue
|
|
601
|
+
.put(async () => {
|
|
602
|
+
// TODO - Should check if `from` has the permission to call the view function.
|
|
603
|
+
const functionCall = await this.#getFunctionCall(functionName, args, to);
|
|
604
|
+
const executionResult = await this.#simulateUnconstrained(functionCall, scopes);
|
|
605
|
+
|
|
606
|
+
// TODO - Return typed result based on the function artifact.
|
|
607
|
+
return executionResult;
|
|
608
|
+
})
|
|
609
|
+
.catch(err => {
|
|
610
|
+
this.log.error(err);
|
|
611
|
+
throw err;
|
|
612
|
+
});
|
|
584
613
|
}
|
|
585
614
|
|
|
586
615
|
public getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
|
|
587
616
|
return this.node.getTxReceipt(txHash);
|
|
588
617
|
}
|
|
589
618
|
|
|
590
|
-
public getTxEffect(txHash: TxHash)
|
|
619
|
+
public getTxEffect(txHash: TxHash) {
|
|
591
620
|
return this.node.getTxEffect(txHash);
|
|
592
621
|
}
|
|
593
622
|
|
|
@@ -773,7 +802,11 @@ export class PXEService implements PXE {
|
|
|
773
802
|
return result;
|
|
774
803
|
} catch (err) {
|
|
775
804
|
if (err instanceof SimulationError) {
|
|
776
|
-
|
|
805
|
+
try {
|
|
806
|
+
await enrichPublicSimulationError(err, this.contractDataOracle, this.db, this.log);
|
|
807
|
+
} catch (enrichErr) {
|
|
808
|
+
this.log.error(`Failed to enrich public simulation error: ${enrichErr}`);
|
|
809
|
+
}
|
|
777
810
|
}
|
|
778
811
|
throw err;
|
|
779
812
|
}
|
|
@@ -944,4 +977,8 @@ export class PXEService implements PXE {
|
|
|
944
977
|
|
|
945
978
|
return decodedEvents;
|
|
946
979
|
}
|
|
980
|
+
|
|
981
|
+
async resetNoteSyncData() {
|
|
982
|
+
return await this.db.resetNoteSyncData();
|
|
983
|
+
}
|
|
947
984
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AztecNode,
|
|
3
|
+
type InBlock,
|
|
3
4
|
L1NotePayload,
|
|
4
5
|
type L2Block,
|
|
5
6
|
type L2BlockNumber,
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
type KeyValidationRequest,
|
|
23
24
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
24
25
|
computeAddressSecret,
|
|
25
|
-
computePoint,
|
|
26
26
|
computeTaggingSecret,
|
|
27
27
|
} from '@aztec/circuits.js';
|
|
28
28
|
import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
|
|
@@ -268,8 +268,11 @@ export class SimulatorOracle implements DBOracle {
|
|
|
268
268
|
sender: AztecAddress,
|
|
269
269
|
recipient: AztecAddress,
|
|
270
270
|
): Promise<IndexedTaggingSecret> {
|
|
271
|
+
await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
|
|
272
|
+
|
|
271
273
|
const secret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
|
|
272
274
|
const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
|
|
275
|
+
|
|
273
276
|
return new IndexedTaggingSecret(secret, index);
|
|
274
277
|
}
|
|
275
278
|
|
|
@@ -289,7 +292,9 @@ export class SimulatorOracle implements DBOracle {
|
|
|
289
292
|
this.log.verbose(
|
|
290
293
|
`Incrementing secret ${secret} as sender ${sender} for recipient: ${recipient} at contract: ${contractName}(${contractAddress})`,
|
|
291
294
|
);
|
|
292
|
-
|
|
295
|
+
|
|
296
|
+
const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
|
|
297
|
+
await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]);
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
async #calculateTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
|
|
@@ -329,6 +334,70 @@ export class SimulatorOracle implements DBOracle {
|
|
|
329
334
|
return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
|
|
330
335
|
}
|
|
331
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Updates the local index of the shared tagging secret of a sender / recipient pair
|
|
339
|
+
* if a log with a larger index is found from the node.
|
|
340
|
+
* @param contractAddress - The address of the contract that the logs are tagged for
|
|
341
|
+
* @param sender - The address of the sender, we must know the sender's ivsk_m.
|
|
342
|
+
* @param recipient - The address of the recipient.
|
|
343
|
+
*/
|
|
344
|
+
public async syncTaggedLogsAsSender(
|
|
345
|
+
contractAddress: AztecAddress,
|
|
346
|
+
sender: AztecAddress,
|
|
347
|
+
recipient: AztecAddress,
|
|
348
|
+
): Promise<void> {
|
|
349
|
+
const appTaggingSecret = await this.#calculateTaggingSecret(contractAddress, sender, recipient);
|
|
350
|
+
let [currentIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
|
|
351
|
+
|
|
352
|
+
const INDEX_OFFSET = 10;
|
|
353
|
+
|
|
354
|
+
let previousEmptyBack = 0;
|
|
355
|
+
let currentEmptyBack = 0;
|
|
356
|
+
let currentEmptyFront: number;
|
|
357
|
+
|
|
358
|
+
// The below code is trying to find the index of the start of the first window in which for all elements of window, we do not see logs.
|
|
359
|
+
// We take our window size, and fetch the node for these logs. We store both the amount of empty consecutive slots from the front and the back.
|
|
360
|
+
// We use our current empty consecutive slots from the front, as well as the previous consecutive empty slots from the back to see if we ever hit a time where there
|
|
361
|
+
// is a window in which we see the combination of them to be greater than the window's size. If true, we rewind current index to the start of said window and use it.
|
|
362
|
+
// Assuming two windows of 5:
|
|
363
|
+
// [0, 1, 0, 1, 0], [0, 0, 0, 0, 0]
|
|
364
|
+
// We can see that when processing the second window, the previous amount of empty slots from the back of the window (1), added with the empty elements from the front of the window (5)
|
|
365
|
+
// is greater than 5 (6) and therefore we have found a window to use.
|
|
366
|
+
// We simply need to take the number of elements (10) - the size of the window (5) - the number of consecutive empty elements from the back of the last window (1) = 4;
|
|
367
|
+
// This is the first index of our desired window.
|
|
368
|
+
// Note that if we ever see a situation like so:
|
|
369
|
+
// [0, 1, 0, 1, 0], [0, 0, 0, 0, 1]
|
|
370
|
+
// This also returns the correct index (4), but this is indicative of a problem / desync. i.e. we should never have a window that has a log that exists after the window.
|
|
371
|
+
|
|
372
|
+
do {
|
|
373
|
+
const currentTags = [...new Array(INDEX_OFFSET)].map((_, i) => {
|
|
374
|
+
const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i);
|
|
375
|
+
return indexedAppTaggingSecret.computeTag(recipient);
|
|
376
|
+
});
|
|
377
|
+
previousEmptyBack = currentEmptyBack;
|
|
378
|
+
|
|
379
|
+
const possibleLogs = await this.aztecNode.getLogsByTags(currentTags);
|
|
380
|
+
|
|
381
|
+
const indexOfFirstLog = possibleLogs.findIndex(possibleLog => possibleLog.length !== 0);
|
|
382
|
+
currentEmptyFront = indexOfFirstLog === -1 ? INDEX_OFFSET : indexOfFirstLog;
|
|
383
|
+
|
|
384
|
+
const indexOfLastLog = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0);
|
|
385
|
+
currentEmptyBack = indexOfLastLog === -1 ? INDEX_OFFSET : INDEX_OFFSET - 1 - indexOfLastLog;
|
|
386
|
+
|
|
387
|
+
currentIndex += INDEX_OFFSET;
|
|
388
|
+
} while (currentEmptyFront + previousEmptyBack < INDEX_OFFSET);
|
|
389
|
+
|
|
390
|
+
// We unwind the entire current window and the amount of consecutive empty slots from the previous window
|
|
391
|
+
const newIndex = currentIndex - (INDEX_OFFSET + previousEmptyBack);
|
|
392
|
+
|
|
393
|
+
await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(appTaggingSecret, newIndex)]);
|
|
394
|
+
|
|
395
|
+
const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
|
|
396
|
+
this.log.debug(
|
|
397
|
+
`Syncing logs for sender ${sender}, secret ${appTaggingSecret}:${currentIndex} at contract: ${contractName}(${contractAddress})`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
332
401
|
/**
|
|
333
402
|
* Synchronizes the logs tagged with scoped addresses and all the senders in the addressbook.
|
|
334
403
|
* Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync.
|
|
@@ -441,7 +510,12 @@ export class SimulatorOracle implements DBOracle {
|
|
|
441
510
|
|
|
442
511
|
result.set(
|
|
443
512
|
recipient.toString(),
|
|
444
|
-
logs
|
|
513
|
+
// Remove logs with a block number higher than the max block number
|
|
514
|
+
// Duplicates are likely to happen due to the sliding window, so we also filter them out
|
|
515
|
+
logs.filter(
|
|
516
|
+
(log, index, self) =>
|
|
517
|
+
log.blockNumber <= maxBlockNumber && index === self.findIndex(otherLog => otherLog.equals(log)),
|
|
518
|
+
),
|
|
445
519
|
);
|
|
446
520
|
}
|
|
447
521
|
return result;
|
|
@@ -469,7 +543,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
469
543
|
const incomingNotes: IncomingNoteDao[] = [];
|
|
470
544
|
const outgoingNotes: OutgoingNoteDao[] = [];
|
|
471
545
|
|
|
472
|
-
const txEffectsCache = new Map<string, TxEffect | undefined>();
|
|
546
|
+
const txEffectsCache = new Map<string, InBlock<TxEffect> | undefined>();
|
|
473
547
|
|
|
474
548
|
for (const scopedLog of scopedLogs) {
|
|
475
549
|
const incomingNotePayload = L1NotePayload.decryptAsIncoming(
|
|
@@ -510,11 +584,13 @@ export class SimulatorOracle implements DBOracle {
|
|
|
510
584
|
// mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
|
|
511
585
|
simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle),
|
|
512
586
|
this.db,
|
|
513
|
-
incomingNotePayload ?
|
|
587
|
+
incomingNotePayload ? recipient.toAddressPoint() : undefined,
|
|
514
588
|
outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined,
|
|
515
589
|
payload!,
|
|
516
|
-
txEffect.txHash,
|
|
517
|
-
txEffect.
|
|
590
|
+
txEffect.data.txHash,
|
|
591
|
+
txEffect.l2BlockNumber,
|
|
592
|
+
txEffect.l2BlockHash,
|
|
593
|
+
txEffect.data.noteHashes,
|
|
518
594
|
scopedLog.dataStartIndexForTx,
|
|
519
595
|
excludedIndices.get(scopedLog.txHash.toString())!,
|
|
520
596
|
this.log,
|
|
@@ -557,17 +633,19 @@ export class SimulatorOracle implements DBOracle {
|
|
|
557
633
|
}
|
|
558
634
|
const nullifiedNotes: IncomingNoteDao[] = [];
|
|
559
635
|
const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient });
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
636
|
+
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
|
|
637
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
638
|
+
const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck);
|
|
639
|
+
|
|
640
|
+
const foundNullifiers = nullifiersToCheck
|
|
641
|
+
.map((nullifier, i) => {
|
|
642
|
+
if (nullifierIndexes[i] !== undefined) {
|
|
643
|
+
return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock<Fr>;
|
|
644
|
+
}
|
|
645
|
+
})
|
|
646
|
+
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
|
|
569
647
|
|
|
570
|
-
await this.db.removeNullifiedNotes(foundNullifiers,
|
|
648
|
+
await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
|
|
571
649
|
nullifiedNotes.forEach(noteDao => {
|
|
572
650
|
this.log.verbose(
|
|
573
651
|
`Removed note for contract ${noteDao.contractAddress} at slot ${
|