@aztec/pxe 0.70.0 → 0.71.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/config/index.d.ts.map +1 -1
- package/dest/config/index.js +3 -3
- package/dest/database/kv_pxe_database.d.ts +4 -2
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +32 -10
- package/dest/database/note_dao.d.ts +1 -3
- package/dest/database/note_dao.d.ts.map +1 -1
- package/dest/database/note_dao.js +1 -5
- package/dest/database/outgoing_note_dao.d.ts +1 -3
- package/dest/database/outgoing_note_dao.d.ts.map +1 -1
- package/dest/database/outgoing_note_dao.js +1 -5
- package/dest/database/pxe_database.d.ts +29 -12
- 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 +93 -20
- package/dest/note_decryption_utils/add_public_values_to_payload.js +2 -2
- package/dest/simulator_oracle/index.d.ts +12 -20
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +99 -59
- package/package.json +14 -14
- package/src/config/index.ts +2 -1
- package/src/database/kv_pxe_database.ts +40 -8
- package/src/database/note_dao.ts +1 -29
- package/src/database/outgoing_note_dao.ts +1 -28
- package/src/database/pxe_database.ts +31 -12
- package/src/database/pxe_database_test_suite.ts +118 -19
- package/src/note_decryption_utils/add_public_values_to_payload.ts +1 -1
- package/src/simulator_oracle/index.ts +198 -77
- package/dest/note_decryption_utils/brute_force_note_info.d.ts +0 -31
- package/dest/note_decryption_utils/brute_force_note_info.d.ts.map +0 -1
- package/dest/note_decryption_utils/brute_force_note_info.js +0 -54
- package/dest/note_decryption_utils/index.d.ts +0 -3
- package/dest/note_decryption_utils/index.d.ts.map +0 -1
- package/dest/note_decryption_utils/index.js +0 -2
- package/dest/note_decryption_utils/produce_note_daos.d.ts +0 -28
- package/dest/note_decryption_utils/produce_note_daos.d.ts.map +0 -1
- package/dest/note_decryption_utils/produce_note_daos.js +0 -33
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts +0 -8
- package/dest/note_decryption_utils/produce_note_daos_for_key.d.ts.map +0 -1
- package/dest/note_decryption_utils/produce_note_daos_for_key.js +0 -17
- package/src/note_decryption_utils/brute_force_note_info.ts +0 -90
- package/src/note_decryption_utils/index.ts +0 -2
- package/src/note_decryption_utils/produce_note_daos.ts +0 -69
- package/src/note_decryption_utils/produce_note_daos_for_key.ts +0 -59
|
@@ -406,7 +406,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
|
|
|
406
406
|
});
|
|
407
407
|
});
|
|
408
408
|
|
|
409
|
-
describe('contract
|
|
409
|
+
describe('contract non-volatile database', () => {
|
|
410
410
|
let contract: AztecAddress;
|
|
411
411
|
|
|
412
412
|
beforeEach(() => {
|
|
@@ -415,56 +415,155 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
|
|
|
415
415
|
});
|
|
416
416
|
|
|
417
417
|
it('stores and loads a single value', async () => {
|
|
418
|
-
const
|
|
418
|
+
const slot = new Fr(1);
|
|
419
419
|
const values = [new Fr(42)];
|
|
420
420
|
|
|
421
|
-
await database.
|
|
422
|
-
const result = await database.
|
|
421
|
+
await database.dbStore(contract, slot, values);
|
|
422
|
+
const result = await database.dbLoad(contract, slot);
|
|
423
423
|
expect(result).toEqual(values);
|
|
424
424
|
});
|
|
425
425
|
|
|
426
426
|
it('stores and loads multiple values', async () => {
|
|
427
|
-
const
|
|
427
|
+
const slot = new Fr(1);
|
|
428
428
|
const values = [new Fr(42), new Fr(43), new Fr(44)];
|
|
429
429
|
|
|
430
|
-
await database.
|
|
431
|
-
const result = await database.
|
|
430
|
+
await database.dbStore(contract, slot, values);
|
|
431
|
+
const result = await database.dbLoad(contract, slot);
|
|
432
432
|
expect(result).toEqual(values);
|
|
433
433
|
});
|
|
434
434
|
|
|
435
435
|
it('overwrites existing values', async () => {
|
|
436
|
-
const
|
|
436
|
+
const slot = new Fr(1);
|
|
437
437
|
const initialValues = [new Fr(42)];
|
|
438
438
|
const newValues = [new Fr(100)];
|
|
439
439
|
|
|
440
|
-
await database.
|
|
441
|
-
await database.
|
|
440
|
+
await database.dbStore(contract, slot, initialValues);
|
|
441
|
+
await database.dbStore(contract, slot, newValues);
|
|
442
442
|
|
|
443
|
-
const result = await database.
|
|
443
|
+
const result = await database.dbLoad(contract, slot);
|
|
444
444
|
expect(result).toEqual(newValues);
|
|
445
445
|
});
|
|
446
446
|
|
|
447
447
|
it('stores values for different contracts independently', async () => {
|
|
448
448
|
const anotherContract = AztecAddress.random();
|
|
449
|
-
const
|
|
449
|
+
const slot = new Fr(1);
|
|
450
450
|
const values1 = [new Fr(42)];
|
|
451
451
|
const values2 = [new Fr(100)];
|
|
452
452
|
|
|
453
|
-
await database.
|
|
454
|
-
await database.
|
|
453
|
+
await database.dbStore(contract, slot, values1);
|
|
454
|
+
await database.dbStore(anotherContract, slot, values2);
|
|
455
455
|
|
|
456
|
-
const result1 = await database.
|
|
457
|
-
const result2 = await database.
|
|
456
|
+
const result1 = await database.dbLoad(contract, slot);
|
|
457
|
+
const result2 = await database.dbLoad(anotherContract, slot);
|
|
458
458
|
|
|
459
459
|
expect(result1).toEqual(values1);
|
|
460
460
|
expect(result2).toEqual(values2);
|
|
461
461
|
});
|
|
462
462
|
|
|
463
|
-
it('returns null for non-existent
|
|
464
|
-
const
|
|
465
|
-
const result = await database.
|
|
463
|
+
it('returns null for non-existent slots', async () => {
|
|
464
|
+
const slot = Fr.random();
|
|
465
|
+
const result = await database.dbLoad(contract, slot);
|
|
466
466
|
expect(result).toBeNull();
|
|
467
467
|
});
|
|
468
|
+
|
|
469
|
+
it('deletes a slot', async () => {
|
|
470
|
+
const slot = new Fr(1);
|
|
471
|
+
const values = [new Fr(42)];
|
|
472
|
+
|
|
473
|
+
await database.dbStore(contract, slot, values);
|
|
474
|
+
await database.dbDelete(contract, slot);
|
|
475
|
+
|
|
476
|
+
expect(await database.dbLoad(contract, slot)).toBeNull();
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('deletes an empty slot', async () => {
|
|
480
|
+
const slot = new Fr(1);
|
|
481
|
+
await database.dbDelete(contract, slot);
|
|
482
|
+
|
|
483
|
+
expect(await database.dbLoad(contract, slot)).toBeNull();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('copies a single value', async () => {
|
|
487
|
+
const slot = new Fr(1);
|
|
488
|
+
const values = [new Fr(42)];
|
|
489
|
+
|
|
490
|
+
await database.dbStore(contract, slot, values);
|
|
491
|
+
|
|
492
|
+
const dstSlot = new Fr(5);
|
|
493
|
+
await database.dbCopy(contract, slot, dstSlot, 1);
|
|
494
|
+
|
|
495
|
+
expect(await database.dbLoad(contract, dstSlot)).toEqual(values);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('copies multiple non-overlapping values', async () => {
|
|
499
|
+
const src = new Fr(1);
|
|
500
|
+
const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
|
|
501
|
+
|
|
502
|
+
await database.dbStore(contract, src, valuesArray[0]);
|
|
503
|
+
await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
|
|
504
|
+
await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
|
|
505
|
+
|
|
506
|
+
const dst = new Fr(5);
|
|
507
|
+
await database.dbCopy(contract, src, dst, 3);
|
|
508
|
+
|
|
509
|
+
expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
|
|
510
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
|
|
511
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('copies overlapping values with src ahead', async () => {
|
|
515
|
+
const src = new Fr(1);
|
|
516
|
+
const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
|
|
517
|
+
|
|
518
|
+
await database.dbStore(contract, src, valuesArray[0]);
|
|
519
|
+
await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
|
|
520
|
+
await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
|
|
521
|
+
|
|
522
|
+
const dst = new Fr(2);
|
|
523
|
+
await database.dbCopy(contract, src, dst, 3);
|
|
524
|
+
|
|
525
|
+
expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
|
|
526
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
|
|
527
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
|
|
528
|
+
|
|
529
|
+
// Slots 2 and 3 (src[1] and src[2]) should have been overwritten since they are also dst[0] and dst[1]
|
|
530
|
+
expect(await database.dbLoad(contract, src)).toEqual(valuesArray[0]); // src[0] (unchanged)
|
|
531
|
+
expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[0]); // dst[0]
|
|
532
|
+
expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[1]); // dst[1]
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('copies overlapping values with dst ahead', async () => {
|
|
536
|
+
const src = new Fr(5);
|
|
537
|
+
const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
|
|
538
|
+
|
|
539
|
+
await database.dbStore(contract, src, valuesArray[0]);
|
|
540
|
+
await database.dbStore(contract, src.add(new Fr(1)), valuesArray[1]);
|
|
541
|
+
await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
|
|
542
|
+
|
|
543
|
+
const dst = new Fr(4);
|
|
544
|
+
await database.dbCopy(contract, src, dst, 3);
|
|
545
|
+
|
|
546
|
+
expect(await database.dbLoad(contract, dst)).toEqual(valuesArray[0]);
|
|
547
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(1)))).toEqual(valuesArray[1]);
|
|
548
|
+
expect(await database.dbLoad(contract, dst.add(new Fr(2)))).toEqual(valuesArray[2]);
|
|
549
|
+
|
|
550
|
+
// Slots 5 and 6 (src[0] and src[1]) should have been overwritten since they are also dst[1] and dst[2]
|
|
551
|
+
expect(await database.dbLoad(contract, src)).toEqual(valuesArray[1]); // dst[1]
|
|
552
|
+
expect(await database.dbLoad(contract, src.add(new Fr(1)))).toEqual(valuesArray[2]); // dst[2]
|
|
553
|
+
expect(await database.dbLoad(contract, src.add(new Fr(2)))).toEqual(valuesArray[2]); // src[2] (unchanged)
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('copying fails if any value is empty', async () => {
|
|
557
|
+
const src = new Fr(1);
|
|
558
|
+
const valuesArray = [[new Fr(42)], [new Fr(1337)], [new Fr(13)]];
|
|
559
|
+
|
|
560
|
+
await database.dbStore(contract, src, valuesArray[0]);
|
|
561
|
+
// We skip src[1]
|
|
562
|
+
await database.dbStore(contract, src.add(new Fr(2)), valuesArray[2]);
|
|
563
|
+
|
|
564
|
+
const dst = new Fr(5);
|
|
565
|
+
await expect(database.dbCopy(contract, src, dst, 3)).rejects.toThrow('Attempted to copy empty slot');
|
|
566
|
+
});
|
|
468
567
|
});
|
|
469
568
|
});
|
|
470
569
|
}
|
|
@@ -41,7 +41,7 @@ export async function getOrderedNoteItems(
|
|
|
41
41
|
noteFields.sort((a, b) => a.index - b.index);
|
|
42
42
|
|
|
43
43
|
// Now we insert the public fields into the note based on its indices defined in the ABI.
|
|
44
|
-
const modifiedNoteItems = privateNoteValues;
|
|
44
|
+
const modifiedNoteItems = [...privateNoteValues];
|
|
45
45
|
let indexInPublicValues = 0;
|
|
46
46
|
for (let i = 0; i < noteFields.length; i++) {
|
|
47
47
|
const noteField = noteFields[i];
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AztecNode,
|
|
3
|
+
type FunctionCall,
|
|
3
4
|
type InBlock,
|
|
4
5
|
L1NotePayload,
|
|
5
6
|
type L2Block,
|
|
6
7
|
type L2BlockNumber,
|
|
7
8
|
MerkleTreeId,
|
|
9
|
+
Note,
|
|
8
10
|
type NoteStatus,
|
|
9
11
|
type NullifierMembershipWitness,
|
|
10
12
|
type PublicDataWitness,
|
|
11
|
-
|
|
13
|
+
TxHash,
|
|
12
14
|
type TxScopedL2Log,
|
|
13
15
|
getNonNullifiedL1ToL2MessageWitness,
|
|
14
16
|
} from '@aztec/circuit-types';
|
|
@@ -18,15 +20,24 @@ import {
|
|
|
18
20
|
type CompleteAddress,
|
|
19
21
|
type ContractInstance,
|
|
20
22
|
Fr,
|
|
21
|
-
|
|
23
|
+
FunctionSelector,
|
|
22
24
|
IndexedTaggingSecret,
|
|
23
25
|
type KeyValidationRequest,
|
|
24
26
|
type L1_TO_L2_MSG_TREE_HEIGHT,
|
|
27
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
28
|
+
PRIVATE_LOG_SIZE_IN_FIELDS,
|
|
25
29
|
PrivateLog,
|
|
26
30
|
computeAddressSecret,
|
|
27
31
|
computeTaggingSecretPoint,
|
|
28
32
|
} from '@aztec/circuits.js';
|
|
29
|
-
import {
|
|
33
|
+
import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
|
|
34
|
+
import {
|
|
35
|
+
type FunctionArtifact,
|
|
36
|
+
FunctionType,
|
|
37
|
+
NoteSelector,
|
|
38
|
+
encodeArguments,
|
|
39
|
+
getFunctionArtifact,
|
|
40
|
+
} from '@aztec/foundation/abi';
|
|
30
41
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
31
42
|
import { createLogger } from '@aztec/foundation/log';
|
|
32
43
|
import { type KeyStore } from '@aztec/key-store';
|
|
@@ -37,10 +48,10 @@ import {
|
|
|
37
48
|
type SimulationProvider,
|
|
38
49
|
} from '@aztec/simulator/client';
|
|
39
50
|
|
|
40
|
-
import {
|
|
51
|
+
import { ContractDataOracle } from '../contract_data_oracle/index.js';
|
|
41
52
|
import { type PxeDatabase } from '../database/index.js';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
53
|
+
import { NoteDao } from '../database/note_dao.js';
|
|
54
|
+
import { getOrderedNoteItems } from '../note_decryption_utils/add_public_values_to_payload.js';
|
|
44
55
|
import { getAcirSimulator } from '../simulator/index.js';
|
|
45
56
|
import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
|
|
46
57
|
|
|
@@ -431,6 +442,8 @@ export class SimulatorOracle implements DBOracle {
|
|
|
431
442
|
maxBlockNumber: number,
|
|
432
443
|
scopes?: AztecAddress[],
|
|
433
444
|
): Promise<Map<string, TxScopedL2Log[]>> {
|
|
445
|
+
this.log.verbose('Searching for tagged logs', { contract: contractAddress });
|
|
446
|
+
|
|
434
447
|
// Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
|
|
435
448
|
// However it is impossible at the moment due to the language not supporting nested slices.
|
|
436
449
|
// This nesting is necessary because for a given set of tags we don't
|
|
@@ -568,10 +581,9 @@ export class SimulatorOracle implements DBOracle {
|
|
|
568
581
|
* Decrypts logs tagged for a recipient and returns them.
|
|
569
582
|
* @param scopedLogs - The logs to decrypt.
|
|
570
583
|
* @param recipient - The recipient of the logs.
|
|
571
|
-
* @param simulator - The simulator to use for decryption.
|
|
572
584
|
* @returns The decrypted notes.
|
|
573
585
|
*/
|
|
574
|
-
async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress
|
|
586
|
+
async #decryptTaggedLogs(scopedLogs: TxScopedL2Log[], recipient: AztecAddress) {
|
|
575
587
|
const recipientCompleteAddress = await this.getCompleteAddress(recipient);
|
|
576
588
|
const ivskM = await this.keyStore.getMasterSecretKey(
|
|
577
589
|
recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
|
|
@@ -581,56 +593,29 @@ export class SimulatorOracle implements DBOracle {
|
|
|
581
593
|
// Since we could have notes with the same index for different txs, we need
|
|
582
594
|
// to keep track of them scoping by txHash
|
|
583
595
|
const excludedIndices: Map<string, Set<number>> = new Map();
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
const txEffectsCache = new Map<string, InBlock<TxEffect> | undefined>();
|
|
596
|
+
const decrypted = [];
|
|
587
597
|
|
|
588
598
|
for (const scopedLog of scopedLogs) {
|
|
589
|
-
const
|
|
599
|
+
const payload = scopedLog.isFromPublic
|
|
590
600
|
? L1NotePayload.decryptAsIncomingFromPublic(scopedLog.logData, addressSecret)
|
|
591
601
|
: L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
|
|
592
602
|
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
txEffectsCache.get(scopedLog.txHash.toString()) ?? (await this.aztecNode.getTxEffect(scopedLog.txHash));
|
|
598
|
-
|
|
599
|
-
if (!txEffect) {
|
|
600
|
-
this.log.warn(`No tx effect found for ${scopedLog.txHash} while decrypting tagged logs`);
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
+
if (!payload) {
|
|
604
|
+
this.log.verbose('Unable to decrypt log');
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
603
607
|
|
|
604
|
-
|
|
608
|
+
if (!excludedIndices.has(scopedLog.txHash.toString())) {
|
|
609
|
+
excludedIndices.set(scopedLog.txHash.toString(), new Set());
|
|
610
|
+
}
|
|
605
611
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
const { note } = await produceNoteDaos(
|
|
610
|
-
// I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates
|
|
611
|
-
// a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow
|
|
612
|
-
// mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one.
|
|
613
|
-
simulator ??
|
|
614
|
-
getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle),
|
|
615
|
-
this.db,
|
|
616
|
-
notePayload ? recipient.toAddressPoint() : undefined,
|
|
617
|
-
payload!,
|
|
618
|
-
txEffect.data.txHash,
|
|
619
|
-
txEffect.data.nullifiers[0],
|
|
620
|
-
txEffect.l2BlockNumber,
|
|
621
|
-
txEffect.l2BlockHash,
|
|
622
|
-
txEffect.data.noteHashes,
|
|
623
|
-
scopedLog.dataStartIndexForTx,
|
|
624
|
-
excludedIndices.get(scopedLog.txHash.toString())!,
|
|
625
|
-
this.log,
|
|
626
|
-
);
|
|
612
|
+
const note = await getOrderedNoteItems(this.db, payload);
|
|
613
|
+
const plaintext = [payload.storageSlot, payload.noteTypeId.toField(), ...note.items];
|
|
627
614
|
|
|
628
|
-
|
|
629
|
-
notes.push(note);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
615
|
+
decrypted.push({ plaintext, txHash: scopedLog.txHash, contractAddress: payload.contractAddress });
|
|
632
616
|
}
|
|
633
|
-
|
|
617
|
+
|
|
618
|
+
return decrypted;
|
|
634
619
|
}
|
|
635
620
|
|
|
636
621
|
/**
|
|
@@ -643,20 +628,68 @@ export class SimulatorOracle implements DBOracle {
|
|
|
643
628
|
recipient: AztecAddress,
|
|
644
629
|
simulator?: AcirSimulator,
|
|
645
630
|
): Promise<void> {
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
631
|
+
const decryptedLogs = await this.#decryptTaggedLogs(logs, recipient);
|
|
632
|
+
|
|
633
|
+
// We've produced the full NoteDao, which we'd be able to simply insert into the database. However, this is
|
|
634
|
+
// only a temporary measure as we migrate from the PXE-driven discovery into the new contract-driven approach. We
|
|
635
|
+
// discard most of the work done up to this point and reconstruct the note plaintext to then hand over to the
|
|
636
|
+
// contract for further processing.
|
|
637
|
+
for (const decryptedLog of decryptedLogs) {
|
|
638
|
+
// Log processing requires the note hashes in the tx in which the note was created. We are now assuming that the
|
|
639
|
+
// note was included in the same block in which the log was delivered - note that partial notes will not work this
|
|
640
|
+
// way.
|
|
641
|
+
const txEffect = await this.aztecNode.getTxEffect(decryptedLog.txHash);
|
|
642
|
+
if (!txEffect) {
|
|
643
|
+
throw new Error(`Could not find tx effect for tx hash ${decryptedLog.txHash}`);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// This will trigger calls to the deliverNote oracle
|
|
647
|
+
await this.callProcessLog(
|
|
648
|
+
decryptedLog.contractAddress,
|
|
649
|
+
decryptedLog.plaintext,
|
|
650
|
+
decryptedLog.txHash,
|
|
651
|
+
txEffect.data.noteHashes,
|
|
652
|
+
txEffect.data.nullifiers[0],
|
|
653
|
+
recipient,
|
|
654
|
+
simulator,
|
|
655
|
+
);
|
|
656
656
|
}
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Called when notes are delivered, usually as a result to a call to the process_log contract function
|
|
661
|
+
public async deliverNote(
|
|
662
|
+
contractAddress: AztecAddress,
|
|
663
|
+
storageSlot: Fr,
|
|
664
|
+
nonce: Fr,
|
|
665
|
+
content: Fr[],
|
|
666
|
+
noteHash: Fr,
|
|
667
|
+
nullifier: Fr,
|
|
668
|
+
txHash: Fr,
|
|
669
|
+
recipient: AztecAddress,
|
|
670
|
+
): Promise<void> {
|
|
671
|
+
const noteDao = await this.produceNoteDao(
|
|
672
|
+
contractAddress,
|
|
673
|
+
storageSlot,
|
|
674
|
+
nonce,
|
|
675
|
+
content,
|
|
676
|
+
noteHash,
|
|
677
|
+
nullifier,
|
|
678
|
+
txHash,
|
|
679
|
+
recipient,
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
await this.db.addNotes([noteDao], recipient);
|
|
683
|
+
this.log.verbose('Added note', {
|
|
684
|
+
contract: noteDao.contractAddress,
|
|
685
|
+
slot: noteDao.storageSlot,
|
|
686
|
+
nullifier: noteDao.siloedNullifier.toString,
|
|
687
|
+
});
|
|
657
688
|
}
|
|
658
689
|
|
|
659
690
|
public async removeNullifiedNotes(contractAddress: AztecAddress) {
|
|
691
|
+
this.log.verbose('Removing nullified notes', { contract: contractAddress });
|
|
692
|
+
|
|
660
693
|
for (const recipient of await this.keyStore.getAccounts()) {
|
|
661
694
|
const currentNotesForRecipient = await this.db.getNotes({ contractAddress, owner: recipient });
|
|
662
695
|
const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
|
|
@@ -681,25 +714,113 @@ export class SimulatorOracle implements DBOracle {
|
|
|
681
714
|
}
|
|
682
715
|
}
|
|
683
716
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
717
|
+
async produceNoteDao(
|
|
718
|
+
contractAddress: AztecAddress,
|
|
719
|
+
storageSlot: Fr,
|
|
720
|
+
nonce: Fr,
|
|
721
|
+
content: Fr[],
|
|
722
|
+
noteHash: Fr,
|
|
723
|
+
nullifier: Fr,
|
|
724
|
+
txHash: Fr,
|
|
725
|
+
recipient: AztecAddress,
|
|
726
|
+
): Promise<NoteDao> {
|
|
727
|
+
const receipt = await this.aztecNode.getTxReceipt(new TxHash(txHash));
|
|
728
|
+
if (receipt === undefined) {
|
|
729
|
+
throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`);
|
|
730
|
+
}
|
|
731
|
+
const { blockNumber, blockHash } = receipt;
|
|
732
|
+
|
|
733
|
+
const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(contractAddress, noteHash));
|
|
734
|
+
const siloedNullifier = siloNullifier(contractAddress, nullifier);
|
|
735
|
+
|
|
736
|
+
const uniqueNoteHashTreeIndex = (
|
|
737
|
+
await this.aztecNode.findLeavesIndexes(blockNumber!, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash])
|
|
738
|
+
)[0];
|
|
739
|
+
if (uniqueNoteHashTreeIndex === undefined) {
|
|
740
|
+
throw new Error(
|
|
741
|
+
`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${blockNumber} (from tx ${txHash})`,
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return new NoteDao(
|
|
746
|
+
new Note(content),
|
|
747
|
+
contractAddress,
|
|
748
|
+
storageSlot,
|
|
749
|
+
nonce,
|
|
750
|
+
noteHash,
|
|
751
|
+
siloedNullifier,
|
|
752
|
+
new TxHash(txHash),
|
|
753
|
+
blockNumber!,
|
|
754
|
+
blockHash!.toString(),
|
|
755
|
+
uniqueNoteHashTreeIndex,
|
|
756
|
+
recipient.toAddressPoint(),
|
|
757
|
+
NoteSelector.empty(), // todo: remove
|
|
758
|
+
);
|
|
693
759
|
}
|
|
694
760
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
761
|
+
async callProcessLog(
|
|
762
|
+
contractAddress: AztecAddress,
|
|
763
|
+
logPlaintext: Fr[],
|
|
764
|
+
txHash: TxHash,
|
|
765
|
+
noteHashes: Fr[],
|
|
766
|
+
firstNullifier: Fr,
|
|
767
|
+
recipient: AztecAddress,
|
|
768
|
+
simulator?: AcirSimulator,
|
|
769
|
+
) {
|
|
770
|
+
const artifact: FunctionArtifact | undefined = await new ContractDataOracle(this.db).getFunctionArtifactByName(
|
|
771
|
+
contractAddress,
|
|
772
|
+
'process_log',
|
|
773
|
+
);
|
|
774
|
+
if (!artifact) {
|
|
775
|
+
throw new Error(
|
|
776
|
+
`Mandatory implementation of "process_log" missing in noir contract ${contractAddress.toString()}.`,
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const execRequest: FunctionCall = {
|
|
781
|
+
name: artifact.name,
|
|
782
|
+
to: contractAddress,
|
|
783
|
+
selector: FunctionSelector.fromNameAndParameters(artifact),
|
|
784
|
+
type: FunctionType.UNCONSTRAINED,
|
|
785
|
+
isStatic: artifact.isStatic,
|
|
786
|
+
args: encodeArguments(artifact, [
|
|
787
|
+
toBoundedVec(logPlaintext, PRIVATE_LOG_SIZE_IN_FIELDS),
|
|
788
|
+
txHash.toString(),
|
|
789
|
+
toBoundedVec(noteHashes, MAX_NOTE_HASHES_PER_TX),
|
|
790
|
+
firstNullifier,
|
|
791
|
+
recipient,
|
|
792
|
+
]),
|
|
793
|
+
returnTypes: artifact.returnTypes,
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
await (
|
|
797
|
+
simulator ??
|
|
798
|
+
getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle)
|
|
799
|
+
).runUnconstrained(
|
|
800
|
+
execRequest,
|
|
801
|
+
artifact,
|
|
802
|
+
contractAddress,
|
|
803
|
+
[], // empty scope as this call should not require access to private information
|
|
804
|
+
);
|
|
704
805
|
}
|
|
806
|
+
|
|
807
|
+
dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
|
|
808
|
+
return this.db.dbStore(contractAddress, slot, values);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
|
|
812
|
+
return this.db.dbLoad(contractAddress, slot);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
|
|
816
|
+
return this.db.dbDelete(contractAddress, slot);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
|
|
820
|
+
return this.db.dbCopy(contractAddress, srcSlot, dstSlot, numEntries);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function toBoundedVec(array: Fr[], maxLength: number) {
|
|
825
|
+
return { storage: array.concat(Array(maxLength - array.length).fill(new Fr(0))), len: array.length };
|
|
705
826
|
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type Note, type TxHash } from '@aztec/circuit-types';
|
|
2
|
-
import { type AztecAddress } from '@aztec/circuits.js';
|
|
3
|
-
import { type NoteSelector } from '@aztec/foundation/abi';
|
|
4
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
5
|
-
import { type AcirSimulator } from '@aztec/simulator/client';
|
|
6
|
-
export interface NoteInfo {
|
|
7
|
-
noteHashIndex: number;
|
|
8
|
-
nonce: Fr;
|
|
9
|
-
noteHash: Fr;
|
|
10
|
-
siloedNullifier: Fr;
|
|
11
|
-
txHash: TxHash;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Finds nonce, index, inner hash and siloed nullifier for a given note.
|
|
15
|
-
* @dev Finds the index in the note hash tree by computing the note hash with different nonce and see which hash for
|
|
16
|
-
* the current tx matches this value.
|
|
17
|
-
* @remarks This method assists in identifying spent notes in the note hash tree.
|
|
18
|
-
* @param uniqueNoteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
|
|
19
|
-
* @param txHash - Hash of a tx the note was emitted in.
|
|
20
|
-
* @param contractAddress - Address of the contract the note was emitted in.
|
|
21
|
-
* @param storageSlot - Storage slot of the note.
|
|
22
|
-
* @param noteTypeId - Type of the note.
|
|
23
|
-
* @param note - Note items.
|
|
24
|
-
* @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
|
|
25
|
-
* l1NotePayload. We need to find a different index for each replicate.
|
|
26
|
-
* @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
|
|
27
|
-
* @returns Nonce, index, inner hash and siloed nullifier for a given note.
|
|
28
|
-
* @throws If cannot find the nonce for the note.
|
|
29
|
-
*/
|
|
30
|
-
export declare function bruteForceNoteInfo(simulator: AcirSimulator, uniqueNoteHashes: Fr[], txHash: TxHash, firstNullifier: Fr, contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector, note: Note, excludedIndices: Set<number>, computeNullifier: boolean): Promise<NoteInfo>;
|
|
31
|
-
//# sourceMappingURL=brute_force_note_info.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"brute_force_note_info.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/brute_force_note_info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,EAAE,CAAC;IACb,eAAe,EAAE,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,aAAa,EACxB,gBAAgB,EAAE,EAAE,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,EAAE,EAClB,eAAe,EAAE,YAAY,EAC7B,WAAW,EAAE,EAAE,EACf,UAAU,EAAE,YAAY,EACxB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAAC,QAAQ,CAAC,CA8CnB"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
|
|
2
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
-
/**
|
|
4
|
-
* Finds nonce, index, inner hash and siloed nullifier for a given note.
|
|
5
|
-
* @dev Finds the index in the note hash tree by computing the note hash with different nonce and see which hash for
|
|
6
|
-
* the current tx matches this value.
|
|
7
|
-
* @remarks This method assists in identifying spent notes in the note hash tree.
|
|
8
|
-
* @param uniqueNoteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
|
|
9
|
-
* @param txHash - Hash of a tx the note was emitted in.
|
|
10
|
-
* @param contractAddress - Address of the contract the note was emitted in.
|
|
11
|
-
* @param storageSlot - Storage slot of the note.
|
|
12
|
-
* @param noteTypeId - Type of the note.
|
|
13
|
-
* @param note - Note items.
|
|
14
|
-
* @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
|
|
15
|
-
* l1NotePayload. We need to find a different index for each replicate.
|
|
16
|
-
* @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
|
|
17
|
-
* @returns Nonce, index, inner hash and siloed nullifier for a given note.
|
|
18
|
-
* @throws If cannot find the nonce for the note.
|
|
19
|
-
*/
|
|
20
|
-
export async function bruteForceNoteInfo(simulator, uniqueNoteHashes, txHash, firstNullifier, contractAddress, storageSlot, noteTypeId, note, excludedIndices, computeNullifier) {
|
|
21
|
-
let noteHashIndex = 0;
|
|
22
|
-
let nonce;
|
|
23
|
-
let noteHash;
|
|
24
|
-
let uniqueNoteHash;
|
|
25
|
-
let innerNullifier;
|
|
26
|
-
for (; noteHashIndex < uniqueNoteHashes.length; ++noteHashIndex) {
|
|
27
|
-
if (excludedIndices.has(noteHashIndex)) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const uniqueNoteHashFromTxEffect = uniqueNoteHashes[noteHashIndex];
|
|
31
|
-
if (uniqueNoteHashFromTxEffect.equals(Fr.ZERO)) {
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
const expectedNonce = computeNoteHashNonce(firstNullifier, noteHashIndex);
|
|
35
|
-
({ noteHash, uniqueNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier(contractAddress, expectedNonce, storageSlot, noteTypeId, computeNullifier, note));
|
|
36
|
-
if (uniqueNoteHashFromTxEffect.equals(uniqueNoteHash)) {
|
|
37
|
-
nonce = expectedNonce;
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (!nonce) {
|
|
42
|
-
// NB: this used to warn the user that a decrypted log didn't match any notes.
|
|
43
|
-
// This was previously fine as we didn't chop transient note logs, but now we do (#1641 complete).
|
|
44
|
-
throw new Error('Cannot find a matching note hash for the note.');
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
noteHashIndex,
|
|
48
|
-
nonce,
|
|
49
|
-
noteHash: noteHash,
|
|
50
|
-
siloedNullifier: siloNullifier(contractAddress, innerNullifier),
|
|
51
|
-
txHash,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJ1dGVfZm9yY2Vfbm90ZV9pbmZvLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25vdGVfZGVjcnlwdGlvbl91dGlscy9icnV0ZV9mb3JjZV9ub3RlX2luZm8udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLG9CQUFvQixFQUFFLGFBQWEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRTlFLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQVc5Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQ3RDLFNBQXdCLEVBQ3hCLGdCQUFzQixFQUN0QixNQUFjLEVBQ2QsY0FBa0IsRUFDbEIsZUFBNkIsRUFDN0IsV0FBZSxFQUNmLFVBQXdCLEVBQ3hCLElBQVUsRUFDVixlQUE0QixFQUM1QixnQkFBeUI7SUFFekIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0lBQ3RCLElBQUksS0FBcUIsQ0FBQztJQUMxQixJQUFJLFFBQXdCLENBQUM7SUFDN0IsSUFBSSxjQUE4QixDQUFDO0lBQ25DLElBQUksY0FBOEIsQ0FBQztJQUVuQyxPQUFPLGFBQWEsR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsRUFBRSxhQUFhLEVBQUUsQ0FBQztRQUNoRSxJQUFJLGVBQWUsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxTQUFTO1FBQ1gsQ0FBQztRQUVELE1BQU0sMEJBQTBCLEdBQUcsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDbkUsSUFBSSwwQkFBMEIsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDL0MsTUFBTTtRQUNSLENBQUM7UUFFRCxNQUFNLGFBQWEsR0FBRyxvQkFBb0IsQ0FBQyxjQUFjLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDMUUsQ0FBQyxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLEdBQUcsTUFBTSxTQUFTLENBQUMsc0NBQXNDLENBQ3BHLGVBQWUsRUFDZixhQUFhLEVBQ2IsV0FBVyxFQUNYLFVBQVUsRUFDVixnQkFBZ0IsRUFDaEIsSUFBSSxDQUNMLENBQUMsQ0FBQztRQUVILElBQUksMEJBQTBCLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7WUFDdEQsS0FBSyxHQUFHLGFBQWEsQ0FBQztZQUN0QixNQUFNO1FBQ1IsQ0FBQztJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCw4RUFBOEU7UUFDOUUsa0dBQWtHO1FBQ2xHLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQsT0FBTztRQUNMLGFBQWE7UUFDYixLQUFLO1FBQ0wsUUFBUSxFQUFFLFFBQVM7UUFDbkIsZUFBZSxFQUFFLGFBQWEsQ0FBQyxlQUFlLEVBQUUsY0FBZSxDQUFDO1FBQ2hFLE1BQU07S0FDUCxDQUFDO0FBQ0osQ0FBQyJ9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/note_decryption_utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export { produceNoteDaos } from './produce_note_daos.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbm90ZV9kZWNyeXB0aW9uX3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQyJ9
|