@aztec/pxe 0.35.1 → 0.37.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/contract_data_oracle/index.d.ts +0 -10
- package/dest/contract_data_oracle/index.d.ts.map +1 -1
- package/dest/contract_data_oracle/index.js +1 -14
- package/dest/database/kv_pxe_database.js +2 -2
- package/dest/kernel_oracle/index.d.ts +0 -1
- package/dest/kernel_oracle/index.d.ts.map +1 -1
- package/dest/kernel_oracle/index.js +2 -2
- package/dest/kernel_prover/kernel_prover.d.ts +0 -1
- package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
- package/dest/kernel_prover/kernel_prover.js +13 -16
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.d.ts +3 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.d.ts.map +1 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.js +6 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts +4 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts.map +1 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.js +85 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.d.ts +4 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.d.ts.map +1 -0
- package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.js +10 -0
- package/dest/kernel_prover/private_inputs_builders/index.d.ts +4 -0
- package/dest/kernel_prover/private_inputs_builders/index.d.ts.map +1 -0
- package/dest/kernel_prover/private_inputs_builders/index.js +4 -0
- package/dest/note_processor/note_processor.d.ts +2 -2
- package/dest/note_processor/note_processor.d.ts.map +1 -1
- package/dest/note_processor/note_processor.js +12 -14
- package/dest/pxe_service/create_pxe_service.js +4 -3
- package/dest/pxe_service/pxe_service.d.ts +6 -4
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +89 -31
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
- package/dest/pxe_service/test/pxe_test_suite.js +11 -14
- package/dest/simulator_oracle/index.d.ts +5 -4
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +18 -9
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +9 -9
- package/package.json +20 -12
- package/src/contract_data_oracle/index.ts +0 -14
- package/src/database/kv_pxe_database.ts +1 -1
- package/src/kernel_oracle/index.ts +1 -1
- package/src/kernel_prover/kernel_prover.ts +26 -56
- package/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts +17 -0
- package/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +194 -0
- package/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts +30 -0
- package/src/kernel_prover/private_inputs_builders/index.ts +3 -0
- package/src/note_processor/note_processor.ts +12 -12
- package/src/pxe_service/create_pxe_service.ts +2 -2
- package/src/pxe_service/pxe_service.ts +118 -31
- package/src/pxe_service/test/pxe_test_suite.ts +9 -14
- package/src/simulator_oracle/index.ts +22 -11
- package/src/synchronizer/synchronizer.ts +18 -13
- package/dest/kernel_prover/hints_builder.d.ts +0 -40
- package/dest/kernel_prover/hints_builder.d.ts.map +0 -1
- package/dest/kernel_prover/hints_builder.js +0 -103
- package/src/kernel_prover/hints_builder.ts +0 -155
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type MAX_NEW_NOTE_HASHES_PER_CALL,
|
|
3
|
+
type PrivateCircuitPublicInputs,
|
|
4
|
+
PrivateKernelInnerHints,
|
|
5
|
+
} from '@aztec/circuits.js';
|
|
6
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
7
|
+
|
|
8
|
+
export function buildPrivateKernelInnerHints(
|
|
9
|
+
publicInputs: PrivateCircuitPublicInputs,
|
|
10
|
+
noteHashNullifierCounterMap: Map<number, number>,
|
|
11
|
+
) {
|
|
12
|
+
const nullifierCounters = publicInputs.newNoteHashes.map(
|
|
13
|
+
n => noteHashNullifierCounterMap.get(n.counter) ?? 0,
|
|
14
|
+
) as Tuple<number, typeof MAX_NEW_NOTE_HASHES_PER_CALL>;
|
|
15
|
+
|
|
16
|
+
return new PrivateKernelInnerHints(nullifierCounters);
|
|
17
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Fr,
|
|
3
|
+
GrumpkinScalar,
|
|
4
|
+
type MAX_ENCRYPTED_LOGS_PER_TX,
|
|
5
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
6
|
+
MAX_NEW_NULLIFIERS_PER_TX,
|
|
7
|
+
MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
|
|
8
|
+
MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX,
|
|
9
|
+
type MAX_NULLIFIER_READ_REQUESTS_PER_TX,
|
|
10
|
+
type MAX_UNENCRYPTED_LOGS_PER_TX,
|
|
11
|
+
MembershipWitness,
|
|
12
|
+
NULLIFIER_TREE_HEIGHT,
|
|
13
|
+
type NoteHashContext,
|
|
14
|
+
type Nullifier,
|
|
15
|
+
type NullifierKeyValidationRequestContext,
|
|
16
|
+
type PrivateKernelCircuitPublicInputs,
|
|
17
|
+
PrivateKernelTailHints,
|
|
18
|
+
type ReadRequestContext,
|
|
19
|
+
type SideEffect,
|
|
20
|
+
type SideEffectType,
|
|
21
|
+
buildNullifierReadRequestHints,
|
|
22
|
+
buildTransientDataHints,
|
|
23
|
+
countAccumulatedItems,
|
|
24
|
+
sortByCounterGetSortedHints,
|
|
25
|
+
} from '@aztec/circuits.js';
|
|
26
|
+
import { makeTuple } from '@aztec/foundation/array';
|
|
27
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
28
|
+
|
|
29
|
+
import { type ProvingDataOracle } from '../proving_data_oracle.js';
|
|
30
|
+
|
|
31
|
+
/** @deprecated Use sortByCounterGetSortedHints instead */
|
|
32
|
+
function sortSideEffects<T extends SideEffectType, K extends number>(
|
|
33
|
+
sideEffects: Tuple<T, K>,
|
|
34
|
+
): [Tuple<T, K>, Tuple<number, K>] {
|
|
35
|
+
const sorted = sideEffects
|
|
36
|
+
.map((sideEffect, index) => ({ sideEffect, index }))
|
|
37
|
+
.sort((a, b) => {
|
|
38
|
+
// Empty ones go to the right
|
|
39
|
+
if (a.sideEffect.isEmpty()) {
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
return Number(a.sideEffect.counter.toBigInt() - b.sideEffect.counter.toBigInt());
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const originalToSorted = sorted.map(() => 0);
|
|
46
|
+
sorted.forEach(({ index }, i) => {
|
|
47
|
+
originalToSorted[index] = i;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return [sorted.map(({ sideEffect }) => sideEffect) as Tuple<T, K>, originalToSorted as Tuple<number, K>];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isValidNoteHashReadRequest(readRequest: SideEffect, noteHash: NoteHashContext) {
|
|
54
|
+
return (
|
|
55
|
+
noteHash.value.equals(readRequest.value) &&
|
|
56
|
+
noteHash.counter < readRequest.counter.toNumber() &&
|
|
57
|
+
(noteHash.nullifierCounter === 0 || noteHash.nullifierCounter > readRequest.counter.toNumber())
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Performs the matching between an array of read request and an array of note hashes. This produces
|
|
63
|
+
* hints for the private kernel tail circuit to efficiently match a read request with the corresponding
|
|
64
|
+
* note hash. Several read requests might be pointing to the same note hash. It is therefore valid
|
|
65
|
+
* to return more than one hint with the same index.
|
|
66
|
+
*
|
|
67
|
+
* @param noteHashReadRequests - The array of read requests.
|
|
68
|
+
* @param noteHashes - The array of note hashes.
|
|
69
|
+
* @returns An array of hints where each element is the index of the note hash in note hashes array
|
|
70
|
+
* corresponding to the read request. In other words we have readRequests[i] == noteHashes[hints[i]].
|
|
71
|
+
*/
|
|
72
|
+
function getNoteHashReadRequestHints(
|
|
73
|
+
noteHashReadRequests: Tuple<SideEffect, typeof MAX_NOTE_HASH_READ_REQUESTS_PER_TX>,
|
|
74
|
+
noteHashes: Tuple<NoteHashContext, typeof MAX_NEW_NOTE_HASHES_PER_TX>,
|
|
75
|
+
): Tuple<number, typeof MAX_NOTE_HASH_READ_REQUESTS_PER_TX> {
|
|
76
|
+
const hints = makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, () => 0);
|
|
77
|
+
const numReadRequests = countAccumulatedItems(noteHashReadRequests);
|
|
78
|
+
for (let i = 0; i < numReadRequests; i++) {
|
|
79
|
+
const readRequest = noteHashReadRequests[i];
|
|
80
|
+
const noteHashIndex = noteHashes.findIndex((n: NoteHashContext) => isValidNoteHashReadRequest(readRequest, n));
|
|
81
|
+
if (noteHashIndex === -1) {
|
|
82
|
+
throw new Error(`The read request at index ${i} ${readRequest} does not match to any note hash.`);
|
|
83
|
+
}
|
|
84
|
+
hints[i] = noteHashIndex;
|
|
85
|
+
}
|
|
86
|
+
return hints;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getNullifierReadRequestHints(
|
|
90
|
+
nullifierReadRequests: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX>,
|
|
91
|
+
nullifiers: Tuple<Nullifier, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
92
|
+
oracle: ProvingDataOracle,
|
|
93
|
+
) {
|
|
94
|
+
const getNullifierMembershipWitness = async (nullifier: Fr) => {
|
|
95
|
+
const res = await oracle.getNullifierMembershipWitness(nullifier);
|
|
96
|
+
if (!res) {
|
|
97
|
+
throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const { index, siblingPath, leafPreimage } = res;
|
|
101
|
+
return {
|
|
102
|
+
membershipWitness: new MembershipWitness(
|
|
103
|
+
NULLIFIER_TREE_HEIGHT,
|
|
104
|
+
index,
|
|
105
|
+
siblingPath.toTuple<typeof NULLIFIER_TREE_HEIGHT>(),
|
|
106
|
+
),
|
|
107
|
+
leafPreimage,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return buildNullifierReadRequestHints({ getNullifierMembershipWitness }, nullifierReadRequests, nullifiers);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function getMasterNullifierSecretKeys(
|
|
115
|
+
nullifierKeyValidationRequests: Tuple<
|
|
116
|
+
NullifierKeyValidationRequestContext,
|
|
117
|
+
typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX
|
|
118
|
+
>,
|
|
119
|
+
oracle: ProvingDataOracle,
|
|
120
|
+
) {
|
|
121
|
+
const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero);
|
|
122
|
+
for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) {
|
|
123
|
+
const request = nullifierKeyValidationRequests[i];
|
|
124
|
+
if (request.isEmpty()) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
keys[i] = await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey);
|
|
128
|
+
}
|
|
129
|
+
return keys;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function buildPrivateKernelTailHints(
|
|
133
|
+
publicInputs: PrivateKernelCircuitPublicInputs,
|
|
134
|
+
oracle: ProvingDataOracle,
|
|
135
|
+
) {
|
|
136
|
+
const noteHashReadRequestHints = getNoteHashReadRequestHints(
|
|
137
|
+
publicInputs.validationRequests.noteHashReadRequests,
|
|
138
|
+
publicInputs.end.newNoteHashes,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const nullifierReadRequestHints = await getNullifierReadRequestHints(
|
|
142
|
+
publicInputs.validationRequests.nullifierReadRequests,
|
|
143
|
+
publicInputs.end.newNullifiers,
|
|
144
|
+
oracle,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const masterNullifierSecretKeys = await getMasterNullifierSecretKeys(
|
|
148
|
+
publicInputs.validationRequests.nullifierKeyValidationRequests,
|
|
149
|
+
oracle,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints(
|
|
153
|
+
publicInputs.end.newNoteHashes,
|
|
154
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const [sortedNullifiers, sortedNullifiersIndexes] = sortByCounterGetSortedHints(
|
|
158
|
+
publicInputs.end.newNullifiers,
|
|
159
|
+
MAX_NEW_NULLIFIERS_PER_TX,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const [sortedEncryptedLogHashes, sortedEncryptedLogHashesIndexes] = sortSideEffects<
|
|
163
|
+
SideEffect,
|
|
164
|
+
typeof MAX_ENCRYPTED_LOGS_PER_TX
|
|
165
|
+
>(publicInputs.end.encryptedLogsHashes);
|
|
166
|
+
|
|
167
|
+
const [sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes] = sortSideEffects<
|
|
168
|
+
SideEffect,
|
|
169
|
+
typeof MAX_UNENCRYPTED_LOGS_PER_TX
|
|
170
|
+
>(publicInputs.end.unencryptedLogsHashes);
|
|
171
|
+
|
|
172
|
+
const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints(
|
|
173
|
+
sortedNoteHashes,
|
|
174
|
+
sortedNullifiers,
|
|
175
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
176
|
+
MAX_NEW_NULLIFIERS_PER_TX,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
return new PrivateKernelTailHints(
|
|
180
|
+
transientNullifierIndexesForNoteHashes,
|
|
181
|
+
transientNoteHashIndexesForNullifiers,
|
|
182
|
+
noteHashReadRequestHints,
|
|
183
|
+
nullifierReadRequestHints,
|
|
184
|
+
masterNullifierSecretKeys,
|
|
185
|
+
sortedNoteHashes,
|
|
186
|
+
sortedNoteHashesIndexes,
|
|
187
|
+
sortedNullifiers,
|
|
188
|
+
sortedNullifiersIndexes,
|
|
189
|
+
sortedEncryptedLogHashes,
|
|
190
|
+
sortedEncryptedLogHashesIndexes,
|
|
191
|
+
sortedUnencryptedLogHashes,
|
|
192
|
+
sortedUnencryptedLogHashesIndexes,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
3
|
+
MAX_NEW_NULLIFIERS_PER_TX,
|
|
4
|
+
NoteHashContext,
|
|
5
|
+
Nullifier,
|
|
6
|
+
PrivateKernelTailOutputs,
|
|
7
|
+
} from '@aztec/circuits.js';
|
|
8
|
+
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
9
|
+
import { type Tuple } from '@aztec/foundation/serialize';
|
|
10
|
+
|
|
11
|
+
export function buildPrivateKernelTailOutputs(
|
|
12
|
+
prevNoteHashes: Tuple<NoteHashContext, typeof MAX_NEW_NOTE_HASHES_PER_TX>,
|
|
13
|
+
prevNullifiers: Tuple<Nullifier, typeof MAX_NEW_NULLIFIERS_PER_TX>,
|
|
14
|
+
) {
|
|
15
|
+
// Propagate note hashes that are not linked to a nullifier.
|
|
16
|
+
// Note that note hashes can't link to the first nullifier (counter == 0).
|
|
17
|
+
const noteHashes = padArrayEnd(
|
|
18
|
+
prevNoteHashes.filter(n => !n.nullifierCounter),
|
|
19
|
+
NoteHashContext.empty(),
|
|
20
|
+
MAX_NEW_NOTE_HASHES_PER_TX,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const nullifiers = padArrayEnd(
|
|
24
|
+
prevNullifiers.filter(n => n.noteHash.isZero()),
|
|
25
|
+
Nullifier.empty(),
|
|
26
|
+
MAX_NEW_NULLIFIERS_PER_TX,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return new PrivateKernelTailOutputs(noteHashes, nullifiers);
|
|
30
|
+
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
} from '@aztec/circuit-types';
|
|
9
9
|
import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
|
|
10
10
|
import { INITIAL_L2_BLOCK_NUM, MAX_NEW_NOTE_HASHES_PER_TX, type PublicKey } from '@aztec/circuits.js';
|
|
11
|
-
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
|
|
12
11
|
import { type Fr } from '@aztec/foundation/fields';
|
|
13
12
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
14
13
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -49,7 +48,7 @@ export class NoteProcessor {
|
|
|
49
48
|
/**
|
|
50
49
|
* The public counterpart to the private key to be used in note decryption.
|
|
51
50
|
*/
|
|
52
|
-
public readonly
|
|
51
|
+
public readonly masterIncomingViewingPublicKey: PublicKey,
|
|
53
52
|
private keyStore: KeyStore,
|
|
54
53
|
private db: PxeDatabase,
|
|
55
54
|
private node: AztecNode,
|
|
@@ -78,7 +77,7 @@ export class NoteProcessor {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
private getSyncedToBlock(): number {
|
|
81
|
-
return this.db.getSynchedBlockNumberForPublicKey(this.
|
|
80
|
+
return this.db.getSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey) ?? this.startingBlock - 1;
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
/**
|
|
@@ -99,7 +98,6 @@ export class NoteProcessor {
|
|
|
99
98
|
return;
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
const curve = new Grumpkin();
|
|
103
101
|
const blocksAndNotes: ProcessedData[] = [];
|
|
104
102
|
// Keep track of notes that we couldn't process because the contract was not found.
|
|
105
103
|
const deferredNoteDaos: DeferredNoteDao[] = [];
|
|
@@ -116,7 +114,9 @@ export class NoteProcessor {
|
|
|
116
114
|
// We are using set for `userPertainingTxIndices` to avoid duplicates. This would happen in case there were
|
|
117
115
|
// multiple encrypted logs in a tx pertaining to a user.
|
|
118
116
|
const noteDaos: NoteDao[] = [];
|
|
119
|
-
const
|
|
117
|
+
const secretKey = await this.keyStore.getMasterIncomingViewingSecretKeyForPublicKey(
|
|
118
|
+
this.masterIncomingViewingPublicKey,
|
|
119
|
+
);
|
|
120
120
|
|
|
121
121
|
// Iterate over all the encrypted logs and try decrypting them. If successful, store the note.
|
|
122
122
|
for (let indexOfTxInABlock = 0; indexOfTxInABlock < txLogs.length; ++indexOfTxInABlock) {
|
|
@@ -130,7 +130,7 @@ export class NoteProcessor {
|
|
|
130
130
|
for (const functionLogs of txFunctionLogs) {
|
|
131
131
|
for (const log of functionLogs.logs) {
|
|
132
132
|
this.stats.seen++;
|
|
133
|
-
const taggedNote = TaggedNote.fromEncryptedBuffer(log.data,
|
|
133
|
+
const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, secretKey);
|
|
134
134
|
if (taggedNote?.notePayload) {
|
|
135
135
|
const { notePayload: payload } = taggedNote;
|
|
136
136
|
// We have successfully decrypted the data.
|
|
@@ -138,7 +138,7 @@ export class NoteProcessor {
|
|
|
138
138
|
try {
|
|
139
139
|
const noteDao = await produceNoteDao(
|
|
140
140
|
this.simulator,
|
|
141
|
-
this.
|
|
141
|
+
this.masterIncomingViewingPublicKey,
|
|
142
142
|
payload,
|
|
143
143
|
txHash,
|
|
144
144
|
newNoteHashes,
|
|
@@ -152,7 +152,7 @@ export class NoteProcessor {
|
|
|
152
152
|
this.stats.deferred++;
|
|
153
153
|
this.log.warn(e.message);
|
|
154
154
|
const deferredNoteDao = new DeferredNoteDao(
|
|
155
|
-
this.
|
|
155
|
+
this.masterIncomingViewingPublicKey,
|
|
156
156
|
payload.note,
|
|
157
157
|
payload.contractAddress,
|
|
158
158
|
payload.storageSlot,
|
|
@@ -164,7 +164,7 @@ export class NoteProcessor {
|
|
|
164
164
|
deferredNoteDaos.push(deferredNoteDao);
|
|
165
165
|
} else {
|
|
166
166
|
this.stats.failed++;
|
|
167
|
-
this.log.
|
|
167
|
+
this.log.error(`Could not process note because of "${e}". Discarding note...`);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -182,7 +182,7 @@ export class NoteProcessor {
|
|
|
182
182
|
await this.processDeferredNotes(deferredNoteDaos);
|
|
183
183
|
|
|
184
184
|
const syncedToBlock = l2Blocks[l2Blocks.length - 1].number;
|
|
185
|
-
await this.db.setSynchedBlockNumberForPublicKey(this.
|
|
185
|
+
await this.db.setSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey, syncedToBlock);
|
|
186
186
|
|
|
187
187
|
this.log.debug(`Synched block ${syncedToBlock}`);
|
|
188
188
|
}
|
|
@@ -212,7 +212,7 @@ export class NoteProcessor {
|
|
|
212
212
|
const newNullifiers: Fr[] = blocksAndNotes.flatMap(b =>
|
|
213
213
|
b.block.body.txEffects.flatMap(txEffect => txEffect.nullifiers),
|
|
214
214
|
);
|
|
215
|
-
const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.
|
|
215
|
+
const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.masterIncomingViewingPublicKey);
|
|
216
216
|
removedNotes.forEach(noteDao => {
|
|
217
217
|
this.log.verbose(
|
|
218
218
|
`Removed note for contract ${noteDao.contractAddress} at slot ${
|
|
@@ -260,7 +260,7 @@ export class NoteProcessor {
|
|
|
260
260
|
try {
|
|
261
261
|
const noteDao = await produceNoteDao(
|
|
262
262
|
this.simulator,
|
|
263
|
-
this.
|
|
263
|
+
this.masterIncomingViewingPublicKey,
|
|
264
264
|
payload,
|
|
265
265
|
txHash,
|
|
266
266
|
newNoteHashes,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type AztecNode } from '@aztec/circuit-types';
|
|
2
|
-
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
|
|
3
2
|
import { randomBytes } from '@aztec/foundation/crypto';
|
|
4
3
|
import { TestKeyStore } from '@aztec/key-store';
|
|
5
4
|
import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
|
|
@@ -7,6 +6,7 @@ import { initStoreForRollup } from '@aztec/kv-store/utils';
|
|
|
7
6
|
import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
|
|
8
7
|
import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
|
|
9
8
|
import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
|
|
9
|
+
import { getCanonicalKeyRegistry } from '@aztec/protocol-contracts/key-registry';
|
|
10
10
|
import { getCanonicalMultiCallEntrypointContract } from '@aztec/protocol-contracts/multi-call-entrypoint';
|
|
11
11
|
|
|
12
12
|
import { join } from 'path';
|
|
@@ -38,7 +38,6 @@ export async function createPXEService(
|
|
|
38
38
|
const l1Contracts = await aztecNode.getL1ContractAddresses();
|
|
39
39
|
|
|
40
40
|
const keyStore = new TestKeyStore(
|
|
41
|
-
new Grumpkin(),
|
|
42
41
|
await initStoreForRollup(AztecLmdbStore.open(keyStorePath), l1Contracts.rollupAddress),
|
|
43
42
|
);
|
|
44
43
|
const db = new KVPxeDatabase(await initStoreForRollup(AztecLmdbStore.open(pxeDbPath), l1Contracts.rollupAddress));
|
|
@@ -49,6 +48,7 @@ export async function createPXEService(
|
|
|
49
48
|
getCanonicalInstanceDeployer(),
|
|
50
49
|
getCanonicalMultiCallEntrypointContract(),
|
|
51
50
|
getCanonicalGasToken(l1Contracts.gasPortalAddress),
|
|
51
|
+
getCanonicalKeyRegistry(),
|
|
52
52
|
]) {
|
|
53
53
|
await server.registerContract(contract);
|
|
54
54
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AuthWitness,
|
|
3
3
|
type AztecNode,
|
|
4
|
+
EncryptedFunctionL2Logs,
|
|
5
|
+
type EncryptedL2Log,
|
|
4
6
|
EncryptedTxL2Logs,
|
|
5
7
|
ExtendedNote,
|
|
6
8
|
type FunctionCall,
|
|
@@ -18,6 +20,8 @@ import {
|
|
|
18
20
|
type TxExecutionRequest,
|
|
19
21
|
type TxHash,
|
|
20
22
|
type TxReceipt,
|
|
23
|
+
UnencryptedFunctionL2Logs,
|
|
24
|
+
type UnencryptedL2Log,
|
|
21
25
|
UnencryptedTxL2Logs,
|
|
22
26
|
isNoirCallStackUnresolved,
|
|
23
27
|
} from '@aztec/circuit-types';
|
|
@@ -27,18 +31,18 @@ import {
|
|
|
27
31
|
CallRequest,
|
|
28
32
|
CompleteAddress,
|
|
29
33
|
FunctionData,
|
|
30
|
-
type GrumpkinPrivateKey,
|
|
31
34
|
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
32
35
|
type PartialAddress,
|
|
33
36
|
type PrivateKernelTailCircuitPublicInputs,
|
|
34
37
|
type PublicCallRequest,
|
|
38
|
+
type SideEffect,
|
|
35
39
|
computeContractClassId,
|
|
36
40
|
getContractClassFromArtifact,
|
|
37
41
|
} from '@aztec/circuits.js';
|
|
38
42
|
import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/hash';
|
|
39
43
|
import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
|
|
40
44
|
import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
|
|
41
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
45
|
+
import { Fr, type Point } from '@aztec/foundation/fields';
|
|
42
46
|
import { SerialQueue } from '@aztec/foundation/fifo';
|
|
43
47
|
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
44
48
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -105,7 +109,8 @@ export class PXEService implements PXE {
|
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
private async restoreNoteProcessors() {
|
|
108
|
-
const
|
|
112
|
+
const accounts = await this.keyStore.getAccounts();
|
|
113
|
+
const publicKeys = accounts.map(async account => await this.keyStore.getMasterIncomingViewingPublicKey(account));
|
|
109
114
|
const publicKeysSet = new Set(publicKeys.map(k => k.toString()));
|
|
110
115
|
|
|
111
116
|
const registeredAddresses = await this.db.getCompleteAddresses();
|
|
@@ -165,27 +170,36 @@ export class PXEService implements PXE {
|
|
|
165
170
|
return artifact && getContractClassFromArtifact(artifact);
|
|
166
171
|
}
|
|
167
172
|
|
|
168
|
-
public async registerAccount(
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
public async registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
174
|
+
const accounts = await this.keyStore.getAccounts();
|
|
175
|
+
const account = await this.keyStore.addAccount(secretKey, partialAddress);
|
|
176
|
+
const completeAddress = new CompleteAddress(
|
|
177
|
+
account,
|
|
178
|
+
await this.keyStore.getMasterIncomingViewingPublicKey(account),
|
|
179
|
+
partialAddress,
|
|
180
|
+
);
|
|
181
|
+
if (accounts.includes(account)) {
|
|
182
|
+
this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
|
|
183
|
+
return completeAddress;
|
|
184
|
+
} else {
|
|
185
|
+
const masterIncomingViewingPublicKey = await this.keyStore.getMasterIncomingViewingPublicKey(account);
|
|
186
|
+
this.synchronizer.addAccount(masterIncomingViewingPublicKey, this.keyStore, this.config.l2StartingBlock);
|
|
174
187
|
this.log.info(`Registered account ${completeAddress.address.toString()}`);
|
|
175
188
|
this.log.debug(`Registered account\n ${completeAddress.toReadableString()}`);
|
|
176
|
-
} else {
|
|
177
|
-
this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
|
|
178
189
|
}
|
|
190
|
+
|
|
191
|
+
await this.db.addCompleteAddress(completeAddress);
|
|
179
192
|
return completeAddress;
|
|
180
193
|
}
|
|
181
194
|
|
|
182
195
|
public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
|
|
183
196
|
// Get complete addresses of both the recipients and the accounts
|
|
184
|
-
const
|
|
197
|
+
const completeAddresses = await this.db.getCompleteAddresses();
|
|
185
198
|
// Filter out the addresses not corresponding to accounts
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
const accounts = await this.keyStore.getAccounts();
|
|
200
|
+
return completeAddresses.filter(completeAddress =>
|
|
201
|
+
accounts.find(address => address.equals(completeAddress.address)),
|
|
202
|
+
);
|
|
189
203
|
}
|
|
190
204
|
|
|
191
205
|
public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
|
|
@@ -194,8 +208,28 @@ export class PXEService implements PXE {
|
|
|
194
208
|
return Promise.resolve(account);
|
|
195
209
|
}
|
|
196
210
|
|
|
197
|
-
public async
|
|
211
|
+
public async getRegisteredAccountPublicKeysHash(address: AztecAddress): Promise<Fr | undefined> {
|
|
212
|
+
const accounts = await this.keyStore.getAccounts();
|
|
213
|
+
if (!accounts.some(account => account.equals(address))) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
return this.keyStore.getPublicKeysHash(address);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public async registerRecipient(recipient: CompleteAddress, publicKeys: Point[] = []): Promise<void> {
|
|
198
220
|
const wasAdded = await this.db.addCompleteAddress(recipient);
|
|
221
|
+
|
|
222
|
+
// TODO #5834: This should be refactored to be okay with only adding complete address
|
|
223
|
+
if (publicKeys.length !== 0) {
|
|
224
|
+
await this.keyStore.addPublicKeysForAccount(
|
|
225
|
+
recipient.address,
|
|
226
|
+
publicKeys[0],
|
|
227
|
+
publicKeys[1],
|
|
228
|
+
publicKeys[2],
|
|
229
|
+
publicKeys[3],
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
199
233
|
if (wasAdded) {
|
|
200
234
|
this.log.info(`Added recipient:\n ${recipient.toReadableString()}`);
|
|
201
235
|
} else {
|
|
@@ -205,10 +239,12 @@ export class PXEService implements PXE {
|
|
|
205
239
|
|
|
206
240
|
public async getRecipients(): Promise<CompleteAddress[]> {
|
|
207
241
|
// Get complete addresses of both the recipients and the accounts
|
|
208
|
-
const
|
|
242
|
+
const completeAddresses = await this.db.getCompleteAddresses();
|
|
209
243
|
// Filter out the addresses corresponding to accounts
|
|
210
|
-
const
|
|
211
|
-
const recipients =
|
|
244
|
+
const accounts = await this.keyStore.getAccounts();
|
|
245
|
+
const recipients = completeAddresses.filter(
|
|
246
|
+
completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
|
|
247
|
+
);
|
|
212
248
|
return recipients;
|
|
213
249
|
}
|
|
214
250
|
|
|
@@ -396,7 +432,7 @@ export class PXEService implements PXE {
|
|
|
396
432
|
txRequest: TxExecutionRequest,
|
|
397
433
|
simulatePublic: boolean,
|
|
398
434
|
msgSender: AztecAddress | undefined = undefined,
|
|
399
|
-
) {
|
|
435
|
+
): Promise<SimulatedTx> {
|
|
400
436
|
if (!txRequest.functionData.isPrivate) {
|
|
401
437
|
throw new Error(`Public entrypoints are not allowed`);
|
|
402
438
|
}
|
|
@@ -417,8 +453,7 @@ export class PXEService implements PXE {
|
|
|
417
453
|
}
|
|
418
454
|
|
|
419
455
|
if (simulatePublic) {
|
|
420
|
-
|
|
421
|
-
simulatedTx.publicReturnValues = (await this.#simulatePublicCalls(simulatedTx.tx))[0];
|
|
456
|
+
simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
|
|
422
457
|
}
|
|
423
458
|
|
|
424
459
|
if (!msgSender) {
|
|
@@ -514,10 +549,10 @@ export class PXEService implements PXE {
|
|
|
514
549
|
|
|
515
550
|
/**
|
|
516
551
|
* Retrieves the simulation parameters required to run an ACIR simulation.
|
|
517
|
-
* This includes the contract address, function artifact,
|
|
552
|
+
* This includes the contract address, function artifact, and historical tree roots.
|
|
518
553
|
*
|
|
519
554
|
* @param execRequest - The transaction request object containing details of the contract call.
|
|
520
|
-
* @returns An object containing the contract address, function artifact,
|
|
555
|
+
* @returns An object containing the contract address, function artifact, and historical tree roots.
|
|
521
556
|
*/
|
|
522
557
|
async #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) {
|
|
523
558
|
const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin;
|
|
@@ -529,7 +564,6 @@ export class PXEService implements PXE {
|
|
|
529
564
|
contractAddress,
|
|
530
565
|
execRequest.functionData.selector,
|
|
531
566
|
);
|
|
532
|
-
const portalContract = await this.contractDataOracle.getPortalContractAddress(contractAddress);
|
|
533
567
|
|
|
534
568
|
return {
|
|
535
569
|
contractAddress,
|
|
@@ -537,18 +571,17 @@ export class PXEService implements PXE {
|
|
|
537
571
|
...functionArtifact,
|
|
538
572
|
debug,
|
|
539
573
|
},
|
|
540
|
-
portalContract,
|
|
541
574
|
};
|
|
542
575
|
}
|
|
543
576
|
|
|
544
577
|
async #simulate(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise<ExecutionResult> {
|
|
545
578
|
// TODO - Pause syncing while simulating.
|
|
546
579
|
|
|
547
|
-
const { contractAddress, functionArtifact
|
|
580
|
+
const { contractAddress, functionArtifact } = await this.#getSimulationParameters(txRequest);
|
|
548
581
|
|
|
549
582
|
this.log.debug('Executing simulator...');
|
|
550
583
|
try {
|
|
551
|
-
const result = await this.simulator.run(txRequest, functionArtifact, contractAddress,
|
|
584
|
+
const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender);
|
|
552
585
|
this.log.verbose(`Simulation completed for ${contractAddress.toString()}:${functionArtifact.name}`);
|
|
553
586
|
return result;
|
|
554
587
|
} catch (err) {
|
|
@@ -637,8 +670,7 @@ export class PXEService implements PXE {
|
|
|
637
670
|
this.log.debug(`Executing kernel prover...`);
|
|
638
671
|
const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
|
|
639
672
|
|
|
640
|
-
const encryptedLogs =
|
|
641
|
-
const unencryptedLogs = new UnencryptedTxL2Logs(collectUnencryptedLogs(executionResult));
|
|
673
|
+
const { encryptedLogs, unencryptedLogs } = this.patchLogsOrdering(executionResult);
|
|
642
674
|
const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);
|
|
643
675
|
|
|
644
676
|
// HACK(#1639): Manually patches the ordering of the public call stack
|
|
@@ -646,7 +678,7 @@ export class PXEService implements PXE {
|
|
|
646
678
|
await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);
|
|
647
679
|
|
|
648
680
|
const tx = new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions);
|
|
649
|
-
return new SimulatedTx(tx,
|
|
681
|
+
return new SimulatedTx(tx, executionResult.returnValues);
|
|
650
682
|
}
|
|
651
683
|
|
|
652
684
|
/**
|
|
@@ -756,6 +788,61 @@ export class PXEService implements PXE {
|
|
|
756
788
|
);
|
|
757
789
|
}
|
|
758
790
|
|
|
791
|
+
// As above, this is a hack for encrypted/unencrypted logs ordering, now they are sorted. Since the private kernel
|
|
792
|
+
// cannot keep track of side effects that happen after or before a nested call, we override the gathered logs.
|
|
793
|
+
// As a sanity check, we at least verify that the elements are the same, so we are only tweaking their ordering.
|
|
794
|
+
// See yarn-project/end-to-end/src/e2e_ordering.test.ts
|
|
795
|
+
// See https://github.com/AztecProtocol/aztec-packages/issues/1641
|
|
796
|
+
// Added as part of resolving #5017
|
|
797
|
+
private patchLogsOrdering(execResult: ExecutionResult) {
|
|
798
|
+
const encLogs = collectEncryptedLogs(execResult).flatMap(l => l.logs);
|
|
799
|
+
const unencLogs = collectUnencryptedLogs(execResult).flatMap(l => l.logs);
|
|
800
|
+
const getLogs = (res: ExecutionResult, enc: boolean) => {
|
|
801
|
+
const logs: SideEffect[] = enc
|
|
802
|
+
? res.callStackItem.publicInputs.encryptedLogsHashes.concat(res.nestedExecutions.flatMap(e => getLogs(e, true)))
|
|
803
|
+
: res.callStackItem.publicInputs.unencryptedLogsHashes.concat(
|
|
804
|
+
res.nestedExecutions.flatMap(e => getLogs(e, false)),
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
return logs;
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
const sortSEs = (a: SideEffect, b: SideEffect) => {
|
|
811
|
+
if (a.isEmpty()) {
|
|
812
|
+
return 1;
|
|
813
|
+
} else if (b.isEmpty()) {
|
|
814
|
+
return -1;
|
|
815
|
+
} else {
|
|
816
|
+
return Number(a.counter.toBigInt() - b.counter.toBigInt());
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const sortedEncLogs = getLogs(execResult, true).sort(sortSEs);
|
|
821
|
+
const sortedUnencLogs = getLogs(execResult, false).sort(sortSEs);
|
|
822
|
+
|
|
823
|
+
const finalEncLogs: EncryptedL2Log[] = [];
|
|
824
|
+
sortedEncLogs.forEach((sideEffect: SideEffect) => {
|
|
825
|
+
if (!sideEffect.isEmpty()) {
|
|
826
|
+
const isLog = (log: EncryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
|
|
827
|
+
const thisLogIndex = encLogs.findIndex(isLog);
|
|
828
|
+
finalEncLogs.push(encLogs[thisLogIndex]);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
const finalUnencLogs: UnencryptedL2Log[] = [];
|
|
833
|
+
sortedUnencLogs.forEach((sideEffect: SideEffect) => {
|
|
834
|
+
if (!sideEffect.isEmpty()) {
|
|
835
|
+
const isLog = (log: UnencryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
|
|
836
|
+
const thisLogIndex = unencLogs.findIndex(isLog);
|
|
837
|
+
finalUnencLogs.push(unencLogs[thisLogIndex]);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
const encryptedLogs = new EncryptedTxL2Logs([new EncryptedFunctionL2Logs(finalEncLogs)]);
|
|
842
|
+
const unencryptedLogs = new UnencryptedTxL2Logs([new UnencryptedFunctionL2Logs(finalUnencLogs)]);
|
|
843
|
+
return { encryptedLogs, unencryptedLogs };
|
|
844
|
+
}
|
|
845
|
+
|
|
759
846
|
public async isGlobalStateSynchronized() {
|
|
760
847
|
return await this.synchronizer.isGlobalStateSynchronized();
|
|
761
848
|
}
|