@aztec/pxe 0.71.0 → 0.73.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/bin/index.js +1 -3
- package/dest/contract_data_oracle/index.js +4 -4
- package/dest/contract_data_oracle/private_functions_tree.d.ts +7 -6
- package/dest/contract_data_oracle/private_functions_tree.d.ts.map +1 -1
- package/dest/contract_data_oracle/private_functions_tree.js +25 -14
- package/dest/database/kv_pxe_database.d.ts +3 -3
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +8 -9
- package/dest/database/note_dao.d.ts +4 -4
- package/dest/database/note_dao.d.ts.map +1 -1
- package/dest/database/note_dao.js +5 -5
- package/dest/database/outgoing_note_dao.d.ts +1 -1
- package/dest/database/outgoing_note_dao.d.ts.map +1 -1
- package/dest/database/outgoing_note_dao.js +3 -3
- package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.js +70 -48
- package/dest/kernel_oracle/index.d.ts.map +1 -1
- package/dest/kernel_oracle/index.js +5 -5
- package/dest/kernel_prover/kernel_prover.js +5 -5
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +2 -1
- package/dest/pxe_service/error_enriching.js +4 -4
- package/dest/pxe_service/pxe_service.d.ts +16 -11
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +62 -53
- package/dest/pxe_service/test/pxe_test_suite.js +13 -13
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +36 -21
- package/package.json +15 -15
- package/src/bin/index.ts +0 -3
- package/src/contract_data_oracle/index.ts +3 -3
- package/src/contract_data_oracle/private_functions_tree.ts +28 -20
- package/src/database/kv_pxe_database.ts +19 -14
- package/src/database/note_dao.ts +7 -7
- package/src/database/outgoing_note_dao.ts +5 -5
- package/src/database/pxe_database_test_suite.ts +71 -56
- package/src/kernel_oracle/index.ts +4 -4
- package/src/kernel_prover/kernel_prover.ts +4 -4
- package/src/pxe_http/pxe_http_server.ts +1 -0
- package/src/pxe_service/error_enriching.ts +3 -3
- package/src/pxe_service/pxe_service.ts +86 -64
- package/src/pxe_service/test/pxe_test_suite.ts +12 -12
- package/src/simulator_oracle/index.ts +52 -23
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
type EventMetadataDefinition,
|
|
6
6
|
type ExtendedNote,
|
|
7
7
|
type FunctionCall,
|
|
8
|
-
type
|
|
8
|
+
type GetContractClassLogsResponse,
|
|
9
|
+
type GetPublicLogsResponse,
|
|
9
10
|
type InBlock,
|
|
10
11
|
L1EventPayload,
|
|
11
12
|
type L2Block,
|
|
@@ -145,13 +146,33 @@ export class PXEService implements PXE {
|
|
|
145
146
|
return this.db.getContractInstance(address);
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
public async
|
|
149
|
+
public async getContractClassMetadata(
|
|
150
|
+
id: Fr,
|
|
151
|
+
includeArtifact: boolean = false,
|
|
152
|
+
): Promise<{
|
|
153
|
+
contractClass: ContractClassWithId | undefined;
|
|
154
|
+
isContractClassPubliclyRegistered: boolean;
|
|
155
|
+
artifact: ContractArtifact | undefined;
|
|
156
|
+
}> {
|
|
149
157
|
const artifact = await this.db.getContractArtifact(id);
|
|
150
|
-
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
contractClass: artifact && (await getContractClassFromArtifact(artifact)),
|
|
161
|
+
isContractClassPubliclyRegistered: await this.#isContractClassPubliclyRegistered(id),
|
|
162
|
+
artifact: includeArtifact ? artifact : undefined,
|
|
163
|
+
};
|
|
151
164
|
}
|
|
152
165
|
|
|
153
|
-
public
|
|
154
|
-
|
|
166
|
+
public async getContractMetadata(address: AztecAddress): Promise<{
|
|
167
|
+
contractInstance: ContractInstanceWithAddress | undefined;
|
|
168
|
+
isContractInitialized: boolean;
|
|
169
|
+
isContractPubliclyDeployed: boolean;
|
|
170
|
+
}> {
|
|
171
|
+
return {
|
|
172
|
+
contractInstance: await this.db.getContractInstance(address),
|
|
173
|
+
isContractInitialized: await this.#isContractInitialized(address),
|
|
174
|
+
isContractPubliclyDeployed: await this.#isContractPubliclyDeployed(address),
|
|
175
|
+
};
|
|
155
176
|
}
|
|
156
177
|
|
|
157
178
|
public async registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
@@ -216,7 +237,7 @@ export class PXEService implements PXE {
|
|
|
216
237
|
}
|
|
217
238
|
|
|
218
239
|
public async registerContractClass(artifact: ContractArtifact): Promise<void> {
|
|
219
|
-
const contractClassId = computeContractClassId(getContractClassFromArtifact(artifact));
|
|
240
|
+
const contractClassId = await computeContractClassId(await getContractClassFromArtifact(artifact));
|
|
220
241
|
await this.db.addContractArtifact(contractClassId, artifact);
|
|
221
242
|
this.log.info(`Added contract class ${artifact.name} with id ${contractClassId}`);
|
|
222
243
|
}
|
|
@@ -227,14 +248,15 @@ export class PXEService implements PXE {
|
|
|
227
248
|
|
|
228
249
|
if (artifact) {
|
|
229
250
|
// If the user provides an artifact, validate it against the expected class id and register it
|
|
230
|
-
const contractClass = getContractClassFromArtifact(artifact);
|
|
231
|
-
const contractClassId = computeContractClassId(contractClass);
|
|
251
|
+
const contractClass = await getContractClassFromArtifact(artifact);
|
|
252
|
+
const contractClassId = await computeContractClassId(contractClass);
|
|
232
253
|
if (!contractClassId.equals(instance.contractClassId)) {
|
|
233
254
|
throw new Error(
|
|
234
255
|
`Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`,
|
|
235
256
|
);
|
|
236
257
|
}
|
|
237
|
-
|
|
258
|
+
const computedAddress = await computeContractAddressFromInstance(instance);
|
|
259
|
+
if (!computedAddress.equals(instance.address)) {
|
|
238
260
|
throw new Error('Added a contract in which the address does not match the contract instance.');
|
|
239
261
|
}
|
|
240
262
|
|
|
@@ -278,13 +300,15 @@ export class PXEService implements PXE {
|
|
|
278
300
|
const extendedNotes = noteDaos.map(async dao => {
|
|
279
301
|
let owner = filter.owner;
|
|
280
302
|
if (owner === undefined) {
|
|
281
|
-
const completeAddresses =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
303
|
+
const completeAddresses = await this.db.getCompleteAddresses();
|
|
304
|
+
const completeAddressIndex = (
|
|
305
|
+
await Promise.all(completeAddresses.map(completeAddresses => completeAddresses.address.toAddressPoint()))
|
|
306
|
+
).findIndex(addressPoint => addressPoint.equals(dao.addressPoint));
|
|
307
|
+
const completeAddress = completeAddresses[completeAddressIndex];
|
|
308
|
+
if (completeAddress === undefined) {
|
|
285
309
|
throw new Error(`Cannot find complete address for addressPoint ${dao.addressPoint.toString()}`);
|
|
286
310
|
}
|
|
287
|
-
owner =
|
|
311
|
+
owner = completeAddress.address;
|
|
288
312
|
}
|
|
289
313
|
return new UniqueNote(
|
|
290
314
|
dao.note,
|
|
@@ -337,7 +361,7 @@ export class PXEService implements PXE {
|
|
|
337
361
|
throw new Error('Note does not exist.');
|
|
338
362
|
}
|
|
339
363
|
|
|
340
|
-
const siloedNullifier = siloNullifier(note.contractAddress, innerNullifier!);
|
|
364
|
+
const siloedNullifier = await siloNullifier(note.contractAddress, innerNullifier!);
|
|
341
365
|
const [nullifierIndex] = await this.node.findLeavesIndexes('latest', MerkleTreeId.NULLIFIER_TREE, [
|
|
342
366
|
siloedNullifier,
|
|
343
367
|
]);
|
|
@@ -357,7 +381,7 @@ export class PXEService implements PXE {
|
|
|
357
381
|
l2BlockNumber,
|
|
358
382
|
l2BlockHash,
|
|
359
383
|
index,
|
|
360
|
-
owner.address.toAddressPoint(),
|
|
384
|
+
await owner.address.toAddressPoint(),
|
|
361
385
|
note.noteTypeId,
|
|
362
386
|
),
|
|
363
387
|
scope,
|
|
@@ -402,7 +426,7 @@ export class PXEService implements PXE {
|
|
|
402
426
|
l2BlockNumber,
|
|
403
427
|
l2BlockHash,
|
|
404
428
|
index,
|
|
405
|
-
note.owner.toAddressPoint(),
|
|
429
|
+
await note.owner.toAddressPoint(),
|
|
406
430
|
note.noteTypeId,
|
|
407
431
|
),
|
|
408
432
|
);
|
|
@@ -430,7 +454,7 @@ export class PXEService implements PXE {
|
|
|
430
454
|
break;
|
|
431
455
|
}
|
|
432
456
|
|
|
433
|
-
const nonce = computeNoteHashNonce(firstNullifier, i);
|
|
457
|
+
const nonce = await computeNoteHashNonce(firstNullifier, i);
|
|
434
458
|
const { uniqueNoteHash } = await this.simulator.computeNoteHashAndOptionallyANullifier(
|
|
435
459
|
note.contractAddress,
|
|
436
460
|
nonce,
|
|
@@ -522,8 +546,9 @@ export class PXEService implements PXE {
|
|
|
522
546
|
}
|
|
523
547
|
}
|
|
524
548
|
|
|
525
|
-
|
|
526
|
-
|
|
549
|
+
const txHash = await simulatedTx.getTxHash();
|
|
550
|
+
this.log.info(`Simulation completed for ${txHash.toString()} in ${timer.ms()}ms`, {
|
|
551
|
+
txHash,
|
|
527
552
|
...txInfo,
|
|
528
553
|
...(profileResult ? { gateCounts: profileResult.gateCounts } : {}),
|
|
529
554
|
...(publicOutput
|
|
@@ -554,7 +579,7 @@ export class PXEService implements PXE {
|
|
|
554
579
|
}
|
|
555
580
|
|
|
556
581
|
public async sendTx(tx: Tx): Promise<TxHash> {
|
|
557
|
-
const txHash = tx.getTxHash();
|
|
582
|
+
const txHash = await tx.getTxHash();
|
|
558
583
|
if (await this.node.getTxEffect(txHash)) {
|
|
559
584
|
throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`);
|
|
560
585
|
}
|
|
@@ -608,12 +633,12 @@ export class PXEService implements PXE {
|
|
|
608
633
|
}
|
|
609
634
|
|
|
610
635
|
/**
|
|
611
|
-
* Gets
|
|
636
|
+
* Gets public logs based on the provided filter.
|
|
612
637
|
* @param filter - The filter to apply to the logs.
|
|
613
638
|
* @returns The requested logs.
|
|
614
639
|
*/
|
|
615
|
-
public
|
|
616
|
-
return this.node.
|
|
640
|
+
public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
641
|
+
return this.node.getPublicLogs(filter);
|
|
617
642
|
}
|
|
618
643
|
|
|
619
644
|
/**
|
|
@@ -621,7 +646,7 @@ export class PXEService implements PXE {
|
|
|
621
646
|
* @param filter - The filter to apply to the logs.
|
|
622
647
|
* @returns The requested logs.
|
|
623
648
|
*/
|
|
624
|
-
public getContractClassLogs(filter: LogFilter): Promise<
|
|
649
|
+
public getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
|
|
625
650
|
return this.node.getContractClassLogs(filter);
|
|
626
651
|
}
|
|
627
652
|
|
|
@@ -641,7 +666,7 @@ export class PXEService implements PXE {
|
|
|
641
666
|
return {
|
|
642
667
|
name: functionDao.name,
|
|
643
668
|
args: encodeArguments(functionDao, args),
|
|
644
|
-
selector: FunctionSelector.fromNameAndParameters(functionDao.name, functionDao.parameters),
|
|
669
|
+
selector: await FunctionSelector.fromNameAndParameters(functionDao.name, functionDao.parameters),
|
|
645
670
|
type: functionDao.functionType,
|
|
646
671
|
to,
|
|
647
672
|
isStatic: functionDao.isStatic,
|
|
@@ -687,7 +712,7 @@ export class PXEService implements PXE {
|
|
|
687
712
|
async #registerProtocolContracts() {
|
|
688
713
|
const registered: Record<string, string> = {};
|
|
689
714
|
for (const name of protocolContractNames) {
|
|
690
|
-
const { address, contractClass, instance, artifact } = getCanonicalProtocolContract(name);
|
|
715
|
+
const { address, contractClass, instance, artifact } = await getCanonicalProtocolContract(name);
|
|
691
716
|
await this.db.addContractArtifact(contractClass.id, artifact);
|
|
692
717
|
await this.db.addContractInstance(instance);
|
|
693
718
|
registered[name] = address.toString();
|
|
@@ -820,20 +845,20 @@ export class PXEService implements PXE {
|
|
|
820
845
|
});
|
|
821
846
|
}
|
|
822
847
|
|
|
823
|
-
|
|
848
|
+
async #isContractClassPubliclyRegistered(id: Fr): Promise<boolean> {
|
|
824
849
|
return !!(await this.node.getContractClass(id));
|
|
825
850
|
}
|
|
826
851
|
|
|
827
|
-
|
|
852
|
+
async #isContractPubliclyDeployed(address: AztecAddress): Promise<boolean> {
|
|
828
853
|
return !!(await this.node.getContract(address));
|
|
829
854
|
}
|
|
830
855
|
|
|
831
|
-
|
|
832
|
-
const initNullifier = siloNullifier(address, address.toField());
|
|
856
|
+
async #isContractInitialized(address: AztecAddress): Promise<boolean> {
|
|
857
|
+
const initNullifier = await siloNullifier(address, address.toField());
|
|
833
858
|
return !!(await this.node.getNullifierMembershipWitness('latest', initNullifier));
|
|
834
859
|
}
|
|
835
860
|
|
|
836
|
-
public async
|
|
861
|
+
public async getPrivateEvents<T>(
|
|
837
862
|
eventMetadataDef: EventMetadataDefinition,
|
|
838
863
|
from: number,
|
|
839
864
|
limit: number,
|
|
@@ -862,27 +887,31 @@ export class PXEService implements PXE {
|
|
|
862
887
|
throw new Error('No registered account');
|
|
863
888
|
}
|
|
864
889
|
|
|
865
|
-
const preaddress = registeredAccount.getPreaddress();
|
|
890
|
+
const preaddress = await registeredAccount.getPreaddress();
|
|
866
891
|
|
|
867
|
-
secretKey = computeAddressSecret(preaddress, secretKey);
|
|
892
|
+
secretKey = await computeAddressSecret(preaddress, secretKey);
|
|
868
893
|
}
|
|
869
894
|
|
|
870
895
|
return secretKey;
|
|
871
896
|
}),
|
|
872
897
|
);
|
|
873
898
|
|
|
874
|
-
const visibleEvents =
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
899
|
+
const visibleEvents = (
|
|
900
|
+
await Promise.all(
|
|
901
|
+
privateLogs.map(async log => {
|
|
902
|
+
for (const sk of vsks) {
|
|
903
|
+
// TODO: Verify that the first field of the log is the tag siloed with contract address.
|
|
904
|
+
// Or use tags to query logs, like we do with notes.
|
|
905
|
+
const decryptedEvent = await L1EventPayload.decryptAsIncoming(log, sk);
|
|
906
|
+
if (decryptedEvent !== undefined) {
|
|
907
|
+
return [decryptedEvent];
|
|
908
|
+
}
|
|
909
|
+
}
|
|
883
910
|
|
|
884
|
-
|
|
885
|
-
|
|
911
|
+
return [];
|
|
912
|
+
}),
|
|
913
|
+
)
|
|
914
|
+
).flat();
|
|
886
915
|
|
|
887
916
|
const decodedEvents = visibleEvents
|
|
888
917
|
.map(visibleEvent => {
|
|
@@ -892,11 +921,6 @@ export class PXEService implements PXE {
|
|
|
892
921
|
if (!visibleEvent.eventTypeId.equals(eventMetadata.eventSelector)) {
|
|
893
922
|
return undefined;
|
|
894
923
|
}
|
|
895
|
-
if (visibleEvent.event.items.length !== eventMetadata.fieldNames.length) {
|
|
896
|
-
throw new Error(
|
|
897
|
-
'Something is weird here, we have matching EventSelectors, but the actual payload has mismatched length',
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
924
|
|
|
901
925
|
return eventMetadata.decode(visibleEvent);
|
|
902
926
|
})
|
|
@@ -905,34 +929,32 @@ export class PXEService implements PXE {
|
|
|
905
929
|
return decodedEvents;
|
|
906
930
|
}
|
|
907
931
|
|
|
908
|
-
async
|
|
932
|
+
async getPublicEvents<T>(eventMetadataDef: EventMetadataDefinition, from: number, limit: number): Promise<T[]> {
|
|
909
933
|
const eventMetadata = new EventMetadata<T>(eventMetadataDef);
|
|
910
|
-
const { logs
|
|
934
|
+
const { logs } = await this.node.getPublicLogs({
|
|
911
935
|
fromBlock: from,
|
|
912
936
|
toBlock: from + limit,
|
|
913
937
|
});
|
|
914
938
|
|
|
915
|
-
const decodedEvents =
|
|
916
|
-
.map(
|
|
917
|
-
|
|
939
|
+
const decodedEvents = logs
|
|
940
|
+
.map(log => {
|
|
941
|
+
// +1 for the event selector
|
|
942
|
+
const expectedLength = eventMetadata.fieldNames.length + 1;
|
|
943
|
+
const logFields = log.log.log.slice(0, expectedLength);
|
|
918
944
|
// We are assuming here that event logs are the last 4 bytes of the event. This is not enshrined but is a function of aztec.nr raw log emission.
|
|
919
|
-
if (
|
|
920
|
-
!EventSelector.fromBuffer(unencryptedLogBuf.subarray(unencryptedLogBuf.byteLength - 4)).equals(
|
|
921
|
-
eventMetadata.eventSelector,
|
|
922
|
-
)
|
|
923
|
-
) {
|
|
945
|
+
if (!EventSelector.fromField(logFields[logFields.length - 1]).equals(eventMetadata.eventSelector)) {
|
|
924
946
|
return undefined;
|
|
925
947
|
}
|
|
926
|
-
|
|
927
|
-
if (
|
|
948
|
+
// If any of the remaining fields, are non-zero, the payload does match expected:
|
|
949
|
+
if (log.log.log.slice(expectedLength + 1).find(f => !f.isZero())) {
|
|
928
950
|
throw new Error(
|
|
929
951
|
'Something is weird here, we have matching EventSelectors, but the actual payload has mismatched length',
|
|
930
952
|
);
|
|
931
953
|
}
|
|
932
954
|
|
|
933
|
-
return eventMetadata.decode(
|
|
955
|
+
return eventMetadata.decode(log.log);
|
|
934
956
|
})
|
|
935
|
-
.filter(
|
|
957
|
+
.filter(log => log !== undefined) as T[];
|
|
936
958
|
|
|
937
959
|
return decodedEvents;
|
|
938
960
|
}
|
|
@@ -35,7 +35,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('successfully adds a contract', async () => {
|
|
38
|
-
const contracts = [randomDeployedContract(), randomDeployedContract()];
|
|
38
|
+
const contracts = await Promise.all([randomDeployedContract(), randomDeployedContract()]);
|
|
39
39
|
for (const contract of contracts) {
|
|
40
40
|
await pxe.registerContract(contract);
|
|
41
41
|
}
|
|
@@ -47,29 +47,29 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
47
47
|
|
|
48
48
|
it('registers a class and adds a contract for it', async () => {
|
|
49
49
|
const artifact = randomContractArtifact();
|
|
50
|
-
const contractClass = getContractClassFromArtifact(artifact);
|
|
50
|
+
const contractClass = await getContractClassFromArtifact(artifact);
|
|
51
51
|
const contractClassId = contractClass.id;
|
|
52
|
-
const instance = randomContractInstanceWithAddress({ contractClassId });
|
|
52
|
+
const instance = await randomContractInstanceWithAddress({ contractClassId });
|
|
53
53
|
|
|
54
54
|
await pxe.registerContractClass(artifact);
|
|
55
|
-
expect(await pxe.
|
|
55
|
+
expect((await pxe.getContractClassMetadata(contractClassId)).contractClass).toMatchObject(
|
|
56
56
|
omit(contractClass, 'privateFunctionsRoot', 'publicBytecodeCommitment'),
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
await pxe.registerContract({ instance });
|
|
60
|
-
expect(await pxe.
|
|
60
|
+
expect((await pxe.getContractMetadata(instance.address)).contractInstance).toEqual(instance);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
it('refuses to register a class with a mismatched address', async () => {
|
|
64
64
|
const artifact = randomContractArtifact();
|
|
65
|
-
const contractClass = getContractClassFromArtifact(artifact);
|
|
65
|
+
const contractClass = await getContractClassFromArtifact(artifact);
|
|
66
66
|
const contractClassId = contractClass.id;
|
|
67
|
-
const instance = randomContractInstanceWithAddress({ contractClassId });
|
|
67
|
+
const instance = await randomContractInstanceWithAddress({ contractClassId });
|
|
68
68
|
await expect(
|
|
69
69
|
pxe.registerContract({
|
|
70
70
|
instance: {
|
|
71
71
|
...instance,
|
|
72
|
-
address: AztecAddress.random(),
|
|
72
|
+
address: await AztecAddress.random(),
|
|
73
73
|
},
|
|
74
74
|
artifact,
|
|
75
75
|
}),
|
|
@@ -77,13 +77,13 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
it('refuses to register a contract with a class that has not been registered', async () => {
|
|
80
|
-
const instance = randomContractInstanceWithAddress();
|
|
80
|
+
const instance = await randomContractInstanceWithAddress();
|
|
81
81
|
await expect(pxe.registerContract({ instance })).rejects.toThrow(/Missing contract artifact/i);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
it('refuses to register a contract with an artifact with mismatching class id', async () => {
|
|
85
85
|
const artifact = randomContractArtifact();
|
|
86
|
-
const instance = randomContractInstanceWithAddress();
|
|
86
|
+
const instance = await randomContractInstanceWithAddress();
|
|
87
87
|
await expect(pxe.registerContract({ instance, artifact })).rejects.toThrow(/Artifact does not match/i);
|
|
88
88
|
});
|
|
89
89
|
|
|
@@ -91,13 +91,13 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
91
91
|
// a larger setup and it's sufficiently tested in the e2e tests.
|
|
92
92
|
|
|
93
93
|
it('throws when getting public storage for non-existent contract', async () => {
|
|
94
|
-
const contract = AztecAddress.random();
|
|
94
|
+
const contract = await AztecAddress.random();
|
|
95
95
|
await expect(async () => await pxe.getPublicStorageAt(contract, new Fr(0n))).rejects.toThrow(
|
|
96
96
|
`Contract ${contract.toString()} is not deployed`,
|
|
97
97
|
);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
// Note: Not testing `getContractData` and `
|
|
100
|
+
// Note: Not testing `getContractData` and `getPublicLogs` here as these
|
|
101
101
|
// functions only call AztecNode and these methods are frequently used by the e2e tests.
|
|
102
102
|
|
|
103
103
|
it('successfully gets a block number', async () => {
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
MAX_NOTE_HASHES_PER_TX,
|
|
28
28
|
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
29
29
|
PrivateLog,
|
|
30
|
+
PublicLog,
|
|
30
31
|
computeAddressSecret,
|
|
31
32
|
computeTaggingSecretPoint,
|
|
32
33
|
} from '@aztec/circuits.js';
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
encodeArguments,
|
|
39
40
|
getFunctionArtifact,
|
|
40
41
|
} from '@aztec/foundation/abi';
|
|
42
|
+
import { timesParallel } from '@aztec/foundation/collection';
|
|
41
43
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
42
44
|
import { createLogger } from '@aztec/foundation/log';
|
|
43
45
|
import { type KeyStore } from '@aztec/key-store';
|
|
@@ -329,7 +331,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
329
331
|
async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
|
|
330
332
|
const senderCompleteAddress = await this.getCompleteAddress(sender);
|
|
331
333
|
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
332
|
-
const secretPoint = computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
|
|
334
|
+
const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
|
|
333
335
|
// Silo the secret so it can't be used to track other app's notes
|
|
334
336
|
const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
|
|
335
337
|
return appSecret;
|
|
@@ -356,10 +358,12 @@ export class SimulatorOracle implements DBOracle {
|
|
|
356
358
|
const senders = [...(await this.db.getSenderAddresses()), ...(await this.keyStore.getAccounts())].filter(
|
|
357
359
|
(address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)),
|
|
358
360
|
);
|
|
359
|
-
const appTaggingSecrets =
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
const appTaggingSecrets = await Promise.all(
|
|
362
|
+
senders.map(async contact => {
|
|
363
|
+
const sharedSecret = await computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
|
|
364
|
+
return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
|
|
365
|
+
}),
|
|
366
|
+
);
|
|
363
367
|
const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
|
|
364
368
|
return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
|
|
365
369
|
}
|
|
@@ -390,7 +394,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
390
394
|
let [numConsecutiveEmptyLogs, currentIndex] = [0, oldIndex];
|
|
391
395
|
do {
|
|
392
396
|
// We compute the tags for the current window of indexes
|
|
393
|
-
const currentTags =
|
|
397
|
+
const currentTags = await timesParallel(WINDOW_SIZE, i => {
|
|
394
398
|
const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i);
|
|
395
399
|
return indexedAppTaggingSecret.computeSiloedTag(recipient, contractAddress);
|
|
396
400
|
});
|
|
@@ -486,8 +490,8 @@ export class SimulatorOracle implements DBOracle {
|
|
|
486
490
|
|
|
487
491
|
while (secretsAndWindows.length > 0) {
|
|
488
492
|
const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
|
|
489
|
-
const tagsForTheWholeWindow =
|
|
490
|
-
secret.computeSiloedTag(recipient, contractAddress),
|
|
493
|
+
const tagsForTheWholeWindow = await Promise.all(
|
|
494
|
+
secretsForTheWholeWindow.map(secret => secret.computeSiloedTag(recipient, contractAddress)),
|
|
491
495
|
);
|
|
492
496
|
|
|
493
497
|
// We store the new largest indexes we find in the iteration in the following map to later on construct
|
|
@@ -499,15 +503,31 @@ export class SimulatorOracle implements DBOracle {
|
|
|
499
503
|
|
|
500
504
|
logsByTags.forEach((logsByTag, logIndex) => {
|
|
501
505
|
if (logsByTag.length > 0) {
|
|
506
|
+
// Check that public logs have the correct contract address
|
|
507
|
+
const checkedLogsbyTag = logsByTag.filter(
|
|
508
|
+
l => !l.isFromPublic || PublicLog.fromBuffer(l.logData).contractAddress.equals(contractAddress),
|
|
509
|
+
);
|
|
510
|
+
if (checkedLogsbyTag.length < logsByTag.length) {
|
|
511
|
+
const discarded = logsByTag.filter(
|
|
512
|
+
log => checkedLogsbyTag.find(filteredLog => filteredLog.equals(log)) === undefined,
|
|
513
|
+
);
|
|
514
|
+
this.log.warn(
|
|
515
|
+
`Discarded ${
|
|
516
|
+
logsByTag.length - checkedLogsbyTag.length
|
|
517
|
+
} public logs with mismatched contract address ${contractAddress}:`,
|
|
518
|
+
discarded.map(l => PublicLog.fromBuffer(l.logData)),
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
502
522
|
// The logs for the given tag exist so we store them for later processing
|
|
503
|
-
logsForRecipient.push(...
|
|
523
|
+
logsForRecipient.push(...checkedLogsbyTag);
|
|
504
524
|
|
|
505
525
|
// We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
|
|
506
526
|
// a new largest index have been found.
|
|
507
527
|
const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
|
|
508
528
|
const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
|
|
509
529
|
|
|
510
|
-
this.log.debug(`Found ${
|
|
530
|
+
this.log.debug(`Found ${checkedLogsbyTag.length} logs as recipient ${recipient}`, {
|
|
511
531
|
recipient,
|
|
512
532
|
secret: secretCorrespondingToLog.appTaggingSecret,
|
|
513
533
|
contractName,
|
|
@@ -588,7 +608,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
588
608
|
const ivskM = await this.keyStore.getMasterSecretKey(
|
|
589
609
|
recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
|
|
590
610
|
);
|
|
591
|
-
const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM);
|
|
611
|
+
const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
|
|
592
612
|
|
|
593
613
|
// Since we could have notes with the same index for different txs, we need
|
|
594
614
|
// to keep track of them scoping by txHash
|
|
@@ -597,8 +617,8 @@ export class SimulatorOracle implements DBOracle {
|
|
|
597
617
|
|
|
598
618
|
for (const scopedLog of scopedLogs) {
|
|
599
619
|
const payload = scopedLog.isFromPublic
|
|
600
|
-
? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
|
|
601
|
-
: L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
|
|
620
|
+
? await L1NotePayload.decryptAsIncomingFromPublic(PublicLog.fromBuffer(scopedLog.logData), addressSecret)
|
|
621
|
+
: await L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
|
|
602
622
|
|
|
603
623
|
if (!payload) {
|
|
604
624
|
this.log.verbose('Unable to decrypt log');
|
|
@@ -703,7 +723,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
703
723
|
})
|
|
704
724
|
.filter(nullifier => nullifier !== undefined) as InBlock<Fr>[];
|
|
705
725
|
|
|
706
|
-
const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint());
|
|
726
|
+
const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, await recipient.toAddressPoint());
|
|
707
727
|
nullifiedNotes.forEach(noteDao => {
|
|
708
728
|
this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
|
|
709
729
|
contract: noteDao.contractAddress,
|
|
@@ -724,21 +744,30 @@ export class SimulatorOracle implements DBOracle {
|
|
|
724
744
|
txHash: Fr,
|
|
725
745
|
recipient: AztecAddress,
|
|
726
746
|
): Promise<NoteDao> {
|
|
747
|
+
// We need to validate that the note does indeed exist in the world state to avoid adding notes that are then
|
|
748
|
+
// impossible to prove.
|
|
749
|
+
|
|
727
750
|
const receipt = await this.aztecNode.getTxReceipt(new TxHash(txHash));
|
|
728
751
|
if (receipt === undefined) {
|
|
729
752
|
throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`);
|
|
730
753
|
}
|
|
731
|
-
const { blockNumber, blockHash } = receipt;
|
|
732
754
|
|
|
733
|
-
|
|
734
|
-
|
|
755
|
+
// Siloed and unique hashes are computed by us instead of relying on values sent by the contract to make sure
|
|
756
|
+
// we're not e.g. storing notes that belong to some other contract, which would constitute a security breach.
|
|
757
|
+
const uniqueNoteHash = await computeUniqueNoteHash(nonce, await siloNoteHash(contractAddress, noteHash));
|
|
758
|
+
const siloedNullifier = await siloNullifier(contractAddress, nullifier);
|
|
735
759
|
|
|
760
|
+
// We store notes by their index in the global note hash tree, which has the convenient side effect of validating
|
|
761
|
+
// note existence in said tree. Note that while this is technically a historical query, we perform it at the latest
|
|
762
|
+
// locally synced block number which *should* be recent enough to be available. We avoid querying at 'latest' since
|
|
763
|
+
// we want to avoid accidentally processing notes that only exist ahead in time of the locally synced state.
|
|
764
|
+
const syncedBlockNumber = await this.db.getBlockNumber();
|
|
736
765
|
const uniqueNoteHashTreeIndex = (
|
|
737
|
-
await this.aztecNode.findLeavesIndexes(
|
|
766
|
+
await this.aztecNode.findLeavesIndexes(syncedBlockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
|
|
738
767
|
)[0];
|
|
739
768
|
if (uniqueNoteHashTreeIndex === undefined) {
|
|
740
769
|
throw new Error(
|
|
741
|
-
`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${
|
|
770
|
+
`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`,
|
|
742
771
|
);
|
|
743
772
|
}
|
|
744
773
|
|
|
@@ -750,10 +779,10 @@ export class SimulatorOracle implements DBOracle {
|
|
|
750
779
|
noteHash,
|
|
751
780
|
siloedNullifier,
|
|
752
781
|
new TxHash(txHash),
|
|
753
|
-
blockNumber!,
|
|
754
|
-
blockHash!.toString(),
|
|
782
|
+
receipt.blockNumber!,
|
|
783
|
+
receipt.blockHash!.toString(),
|
|
755
784
|
uniqueNoteHashTreeIndex,
|
|
756
|
-
recipient.toAddressPoint(),
|
|
785
|
+
await recipient.toAddressPoint(),
|
|
757
786
|
NoteSelector.empty(), // todo: remove
|
|
758
787
|
);
|
|
759
788
|
}
|
|
@@ -780,7 +809,7 @@ export class SimulatorOracle implements DBOracle {
|
|
|
780
809
|
const execRequest: FunctionCall = {
|
|
781
810
|
name: artifact.name,
|
|
782
811
|
to: contractAddress,
|
|
783
|
-
selector: FunctionSelector.fromNameAndParameters(artifact),
|
|
812
|
+
selector: await FunctionSelector.fromNameAndParameters(artifact),
|
|
784
813
|
type: FunctionType.UNCONSTRAINED,
|
|
785
814
|
isStatic: artifact.isStatic,
|
|
786
815
|
args: encodeArguments(artifact, [
|