@aztec/pxe 0.32.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/database/kv_pxe_database.d.ts +1 -1
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/pxe_database.d.ts +1 -1
- package/dest/database/pxe_database.d.ts.map +1 -1
- package/dest/kernel_prover/kernel_prover.d.ts +3 -42
- package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
- package/dest/kernel_prover/kernel_prover.js +9 -37
- package/dest/kernel_prover/proof_creator.d.ts +2 -2
- package/dest/kernel_prover/proof_creator.d.ts.map +1 -1
- package/dest/kernel_prover/proof_creator.js +4 -3
- package/dest/note_processor/note_processor.d.ts +7 -9
- package/dest/note_processor/note_processor.d.ts.map +1 -1
- package/dest/note_processor/note_processor.js +18 -20
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +3 -3
- package/dest/pxe_service/pxe_service.d.ts +3 -2
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +52 -34
- package/dest/pxe_service/test/pxe_test_suite.js +3 -3
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +9 -13
- package/package.json +22 -16
- package/src/database/kv_pxe_database.ts +1 -1
- package/src/database/pxe_database.ts +1 -1
- package/src/kernel_prover/kernel_prover.ts +22 -88
- package/src/kernel_prover/proof_creator.ts +7 -4
- package/src/note_processor/note_processor.ts +21 -26
- package/src/pxe_http/pxe_http_server.ts +2 -1
- package/src/pxe_service/pxe_service.ts +63 -40
- package/src/pxe_service/test/pxe_test_suite.ts +2 -2
- package/src/synchronizer/synchronizer.ts +9 -15
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type AztecAddress,
|
|
3
2
|
CallRequest,
|
|
4
3
|
Fr,
|
|
5
4
|
type MAX_NEW_NOTE_HASHES_PER_TX,
|
|
@@ -9,10 +8,10 @@ import {
|
|
|
9
8
|
MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
|
|
10
9
|
NoteHashReadRequestMembershipWitness,
|
|
11
10
|
PrivateCallData,
|
|
11
|
+
PrivateKernelCircuitPublicInputs,
|
|
12
|
+
PrivateKernelData,
|
|
12
13
|
PrivateKernelInitCircuitPrivateInputs,
|
|
13
14
|
PrivateKernelInnerCircuitPrivateInputs,
|
|
14
|
-
PrivateKernelInnerCircuitPublicInputs,
|
|
15
|
-
PrivateKernelInnerData,
|
|
16
15
|
PrivateKernelTailCircuitPrivateInputs,
|
|
17
16
|
type SideEffect,
|
|
18
17
|
type SideEffectLinkedToNoteHash,
|
|
@@ -26,43 +25,12 @@ import { padArrayEnd } from '@aztec/foundation/collection';
|
|
|
26
25
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
27
26
|
import { assertLength, mapTuple } from '@aztec/foundation/serialize';
|
|
28
27
|
import { pushTestData } from '@aztec/foundation/testing';
|
|
29
|
-
import { type ExecutionResult
|
|
28
|
+
import { type ExecutionResult } from '@aztec/simulator';
|
|
30
29
|
|
|
31
30
|
import { HintsBuilder } from './hints_builder.js';
|
|
32
31
|
import { KernelProofCreator, type ProofCreator, type ProofOutput, type ProofOutputFinal } from './proof_creator.js';
|
|
33
32
|
import { type ProvingDataOracle } from './proving_data_oracle.js';
|
|
34
33
|
|
|
35
|
-
/**
|
|
36
|
-
* Represents an output note data object.
|
|
37
|
-
* Contains the contract address, new note data and commitment for the note,
|
|
38
|
-
* resulting from the execution of a transaction in the Aztec network.
|
|
39
|
-
*/
|
|
40
|
-
export interface OutputNoteData {
|
|
41
|
-
/**
|
|
42
|
-
* The address of the contract the note was created in.
|
|
43
|
-
*/
|
|
44
|
-
contractAddress: AztecAddress;
|
|
45
|
-
/**
|
|
46
|
-
* The encrypted note data for an output note.
|
|
47
|
-
*/
|
|
48
|
-
data: NoteAndSlot;
|
|
49
|
-
/**
|
|
50
|
-
* The unique value representing the note.
|
|
51
|
-
*/
|
|
52
|
-
commitment: Fr;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Represents the output data of the Kernel Prover.
|
|
57
|
-
* Provides information about the newly created notes, along with the public inputs and proof.
|
|
58
|
-
*/
|
|
59
|
-
export interface KernelProverOutput extends ProofOutputFinal {
|
|
60
|
-
/**
|
|
61
|
-
* An array of output notes containing the contract address, note data, and commitment for each new note.
|
|
62
|
-
*/
|
|
63
|
-
outputNotes: OutputNoteData[];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
34
|
/**
|
|
67
35
|
* The KernelProver class is responsible for generating kernel proofs.
|
|
68
36
|
* It takes a transaction request, its signature, and the simulation result as inputs, and outputs a proof
|
|
@@ -87,14 +55,13 @@ export class KernelProver {
|
|
|
87
55
|
* @param executionResult - The execution result object containing nested executions and preimages.
|
|
88
56
|
* @returns A Promise that resolves to a KernelProverOutput object containing proof, public inputs, and output notes.
|
|
89
57
|
*/
|
|
90
|
-
async prove(txRequest: TxRequest, executionResult: ExecutionResult): Promise<
|
|
58
|
+
async prove(txRequest: TxRequest, executionResult: ExecutionResult): Promise<ProofOutputFinal> {
|
|
91
59
|
const executionStack = [executionResult];
|
|
92
|
-
const newNotes: { [commitmentStr: string]: OutputNoteData } = {};
|
|
93
60
|
let firstIteration = true;
|
|
94
61
|
let previousVerificationKey = VerificationKey.makeFake();
|
|
95
62
|
|
|
96
63
|
let output: ProofOutput = {
|
|
97
|
-
publicInputs:
|
|
64
|
+
publicInputs: PrivateKernelCircuitPublicInputs.empty(),
|
|
98
65
|
proof: makeEmptyProof(),
|
|
99
66
|
};
|
|
100
67
|
|
|
@@ -147,7 +114,7 @@ export class KernelProver {
|
|
|
147
114
|
output = await this.proofCreator.createProofInit(proofInput);
|
|
148
115
|
} else {
|
|
149
116
|
const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(previousVerificationKey);
|
|
150
|
-
const previousKernelData = new
|
|
117
|
+
const previousKernelData = new PrivateKernelData(
|
|
151
118
|
output.publicInputs,
|
|
152
119
|
output.proof,
|
|
153
120
|
previousVerificationKey,
|
|
@@ -158,15 +125,12 @@ export class KernelProver {
|
|
|
158
125
|
pushTestData('private-kernel-inputs-inner', proofInput);
|
|
159
126
|
output = await this.proofCreator.createProofInner(proofInput);
|
|
160
127
|
}
|
|
161
|
-
(await this.getNewNotes(currentExecution)).forEach(n => {
|
|
162
|
-
newNotes[n.commitment.toString()] = n;
|
|
163
|
-
});
|
|
164
128
|
firstIteration = false;
|
|
165
129
|
previousVerificationKey = privateCallData.vk;
|
|
166
130
|
}
|
|
167
131
|
|
|
168
132
|
const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(previousVerificationKey);
|
|
169
|
-
const previousKernelData = new
|
|
133
|
+
const previousKernelData = new PrivateKernelData(
|
|
170
134
|
output.publicInputs,
|
|
171
135
|
output.proof,
|
|
172
136
|
previousVerificationKey,
|
|
@@ -174,6 +138,20 @@ export class KernelProver {
|
|
|
174
138
|
assertLength<Fr, typeof VK_TREE_HEIGHT>(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT),
|
|
175
139
|
);
|
|
176
140
|
|
|
141
|
+
const readNoteHashHints = this.hintsBuilder.getNoteHashReadRequestHints(
|
|
142
|
+
output.publicInputs.validationRequests.noteHashReadRequests,
|
|
143
|
+
output.publicInputs.end.newNoteHashes,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints(
|
|
147
|
+
output.publicInputs.validationRequests.nullifierReadRequests,
|
|
148
|
+
output.publicInputs.end.newNullifiers,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const masterNullifierSecretKeys = await this.hintsBuilder.getMasterNullifierSecretKeys(
|
|
152
|
+
output.publicInputs.validationRequests.nullifierKeyValidationRequests,
|
|
153
|
+
);
|
|
154
|
+
|
|
177
155
|
const [sortedNoteHashes, sortedNoteHashesIndexes] = this.hintsBuilder.sortSideEffects<
|
|
178
156
|
SideEffect,
|
|
179
157
|
typeof MAX_NEW_NOTE_HASHES_PER_TX
|
|
@@ -184,25 +162,11 @@ export class KernelProver {
|
|
|
184
162
|
typeof MAX_NEW_NULLIFIERS_PER_TX
|
|
185
163
|
>(output.publicInputs.end.newNullifiers);
|
|
186
164
|
|
|
187
|
-
const readNoteHashHints = this.hintsBuilder.getNoteHashReadRequestHints(
|
|
188
|
-
output.publicInputs.validationRequests.noteHashReadRequests,
|
|
189
|
-
sortedNoteHashes,
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints(
|
|
193
|
-
output.publicInputs.validationRequests.nullifierReadRequests,
|
|
194
|
-
output.publicInputs.end.newNullifiers,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
165
|
const nullifierNoteHashHints = this.hintsBuilder.getNullifierHints(
|
|
198
166
|
mapTuple(sortedNullifiers, n => n.noteHash),
|
|
199
167
|
sortedNoteHashes,
|
|
200
168
|
);
|
|
201
169
|
|
|
202
|
-
const masterNullifierSecretKeys = await this.hintsBuilder.getMasterNullifierSecretKeys(
|
|
203
|
-
output.publicInputs.validationRequests.nullifierKeyValidationRequests,
|
|
204
|
-
);
|
|
205
|
-
|
|
206
170
|
this.log.debug(
|
|
207
171
|
`Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`,
|
|
208
172
|
);
|
|
@@ -219,13 +183,7 @@ export class KernelProver {
|
|
|
219
183
|
masterNullifierSecretKeys,
|
|
220
184
|
);
|
|
221
185
|
pushTestData('private-kernel-inputs-ordering', privateInputs);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// Only return the notes whose commitment is in the commitments of the final proof.
|
|
225
|
-
const finalNewCommitments = outputFinal.publicInputs.end.newNoteHashes;
|
|
226
|
-
const outputNotes = finalNewCommitments.map(c => newNotes[c.value.toString()]).filter(c => !!c);
|
|
227
|
-
|
|
228
|
-
return { ...outputFinal, outputNotes };
|
|
186
|
+
return await this.proofCreator.createProofTail(privateInputs);
|
|
229
187
|
}
|
|
230
188
|
|
|
231
189
|
private async createPrivateCallData(
|
|
@@ -282,28 +240,4 @@ export class KernelProver {
|
|
|
282
240
|
acirHash,
|
|
283
241
|
});
|
|
284
242
|
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Retrieves the new output notes for a given execution result.
|
|
288
|
-
* The function maps over the new notes and associates them with their corresponding
|
|
289
|
-
* commitments in the public inputs of the execution result. It also includes the contract address
|
|
290
|
-
* from the call context of the public inputs.
|
|
291
|
-
*
|
|
292
|
-
* @param executionResult - The execution result object containing notes and public inputs.
|
|
293
|
-
* @returns An array of OutputNoteData objects, each representing an output note with its associated data.
|
|
294
|
-
*/
|
|
295
|
-
private async getNewNotes(executionResult: ExecutionResult): Promise<OutputNoteData[]> {
|
|
296
|
-
const {
|
|
297
|
-
callStackItem: { publicInputs },
|
|
298
|
-
newNotes,
|
|
299
|
-
} = executionResult;
|
|
300
|
-
const contractAddress = publicInputs.callContext.storageContractAddress;
|
|
301
|
-
// Assuming that for each new commitment there's an output note added to the execution result.
|
|
302
|
-
const newNoteHashes = await this.proofCreator.getSiloedCommitments(publicInputs);
|
|
303
|
-
return newNotes.map((data, i) => ({
|
|
304
|
-
contractAddress,
|
|
305
|
-
data,
|
|
306
|
-
commitment: newNoteHashes[i],
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
243
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type CircuitSimulationStats } from '@aztec/circuit-types/stats';
|
|
2
2
|
import {
|
|
3
3
|
type PrivateCircuitPublicInputs,
|
|
4
|
+
type PrivateKernelCircuitPublicInputs,
|
|
4
5
|
type PrivateKernelInitCircuitPrivateInputs,
|
|
5
6
|
type PrivateKernelInnerCircuitPrivateInputs,
|
|
6
|
-
type PrivateKernelInnerCircuitPublicInputs,
|
|
7
7
|
type PrivateKernelTailCircuitPrivateInputs,
|
|
8
8
|
type PrivateKernelTailCircuitPublicInputs,
|
|
9
9
|
type Proof,
|
|
@@ -13,7 +13,7 @@ import { siloNoteHash } from '@aztec/circuits.js/hash';
|
|
|
13
13
|
import { type Fr } from '@aztec/foundation/fields';
|
|
14
14
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
15
15
|
import { elapsed } from '@aztec/foundation/timer';
|
|
16
|
-
import { executeInit, executeInner, executeTail } from '@aztec/noir-protocol-circuits-types';
|
|
16
|
+
import { executeInit, executeInner, executeTail, executeTailForPublic } from '@aztec/noir-protocol-circuits-types';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Represents the output of the proof creation process for init and inner private kernel circuit.
|
|
@@ -23,7 +23,7 @@ export interface ProofOutput {
|
|
|
23
23
|
/**
|
|
24
24
|
* The public inputs required for the proof generation process.
|
|
25
25
|
*/
|
|
26
|
-
publicInputs:
|
|
26
|
+
publicInputs: PrivateKernelCircuitPublicInputs;
|
|
27
27
|
/**
|
|
28
28
|
* The zk-SNARK proof for the kernel execution.
|
|
29
29
|
*/
|
|
@@ -138,7 +138,10 @@ export class KernelProofCreator implements ProofCreator {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
public async createProofTail(privateInputs: PrivateKernelTailCircuitPrivateInputs): Promise<ProofOutputFinal> {
|
|
141
|
-
const
|
|
141
|
+
const isForPublic = privateInputs.isForPublic();
|
|
142
|
+
const [duration, result] = await elapsed(() =>
|
|
143
|
+
isForPublic ? executeTailForPublic(privateInputs) : executeTail(privateInputs),
|
|
144
|
+
);
|
|
142
145
|
this.log(`Simulated private kernel ordering`, {
|
|
143
146
|
eventName: 'circuit-simulation',
|
|
144
147
|
circuitName: 'private-kernel-ordering',
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
type EncryptedL2BlockL2Logs,
|
|
4
4
|
type KeyStore,
|
|
5
5
|
L1NotePayload,
|
|
6
|
-
type
|
|
6
|
+
type L2Block,
|
|
7
7
|
TaggedNote,
|
|
8
8
|
} from '@aztec/circuit-types';
|
|
9
9
|
import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
|
|
@@ -25,9 +25,9 @@ import { produceNoteDao } from './produce_note_dao.js';
|
|
|
25
25
|
*/
|
|
26
26
|
interface ProcessedData {
|
|
27
27
|
/**
|
|
28
|
-
* Holds L2 block
|
|
28
|
+
* Holds L2 block.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
block: L2Block;
|
|
31
31
|
/**
|
|
32
32
|
* DAOs of processed notes.
|
|
33
33
|
*/
|
|
@@ -82,25 +82,20 @@ export class NoteProcessor {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
* It synchronizes the user's account by decrypting the encrypted logs and processing
|
|
87
|
-
* the transactions and auxiliary data associated with them.
|
|
88
|
-
* Throws an error if the number of block contexts and encrypted logs do not match.
|
|
85
|
+
* Extracts new user-relevant notes from the information contained in the provided L2 blocks and encrypted logs.
|
|
89
86
|
*
|
|
90
|
-
* @
|
|
91
|
-
* @param
|
|
87
|
+
* @throws If the number of blocks and encrypted logs do not match.
|
|
88
|
+
* @param l2Blocks - L2 blocks to be processed.
|
|
89
|
+
* @param encryptedL2BlockLogs - Encrypted logs associated with the L2 blocks.
|
|
92
90
|
* @returns A promise that resolves once the processing is completed.
|
|
93
91
|
*/
|
|
94
|
-
public async process(
|
|
95
|
-
|
|
96
|
-
encryptedL2BlockLogs: EncryptedL2BlockL2Logs[],
|
|
97
|
-
): Promise<void> {
|
|
98
|
-
if (l2BlockContexts.length !== encryptedL2BlockLogs.length) {
|
|
92
|
+
public async process(l2Blocks: L2Block[], encryptedL2BlockLogs: EncryptedL2BlockL2Logs[]): Promise<void> {
|
|
93
|
+
if (l2Blocks.length !== encryptedL2BlockLogs.length) {
|
|
99
94
|
throw new Error(
|
|
100
|
-
`Number of blocks and EncryptedLogs is not equal. Received ${
|
|
95
|
+
`Number of blocks and EncryptedLogs is not equal. Received ${l2Blocks.length} blocks, ${encryptedL2BlockLogs.length} encrypted logs.`,
|
|
101
96
|
);
|
|
102
97
|
}
|
|
103
|
-
if (
|
|
98
|
+
if (l2Blocks.length === 0) {
|
|
104
99
|
return;
|
|
105
100
|
}
|
|
106
101
|
|
|
@@ -113,9 +108,10 @@ export class NoteProcessor {
|
|
|
113
108
|
for (let blockIndex = 0; blockIndex < encryptedL2BlockLogs.length; ++blockIndex) {
|
|
114
109
|
this.stats.blocks++;
|
|
115
110
|
const { txLogs } = encryptedL2BlockLogs[blockIndex];
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
111
|
+
const block = l2Blocks[blockIndex];
|
|
112
|
+
const dataStartIndexForBlock =
|
|
113
|
+
block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
114
|
+
block.body.numberOfTxsIncludingPadded * MAX_NEW_NOTE_HASHES_PER_TX;
|
|
119
115
|
|
|
120
116
|
// We are using set for `userPertainingTxIndices` to avoid duplicates. This would happen in case there were
|
|
121
117
|
// multiple encrypted logs in a tx pertaining to a user.
|
|
@@ -125,8 +121,7 @@ export class NoteProcessor {
|
|
|
125
121
|
// Iterate over all the encrypted logs and try decrypting them. If successful, store the note.
|
|
126
122
|
for (let indexOfTxInABlock = 0; indexOfTxInABlock < txLogs.length; ++indexOfTxInABlock) {
|
|
127
123
|
this.stats.txs++;
|
|
128
|
-
const dataStartIndexForTx =
|
|
129
|
-
dataEndIndexForBlock - (txLogs.length - indexOfTxInABlock) * MAX_NEW_NOTE_HASHES_PER_TX;
|
|
124
|
+
const dataStartIndexForTx = dataStartIndexForBlock + indexOfTxInABlock * MAX_NEW_NOTE_HASHES_PER_TX;
|
|
130
125
|
const newNoteHashes = block.body.txEffects[indexOfTxInABlock].noteHashes;
|
|
131
126
|
// Note: Each tx generates a `TxL2Logs` object and for this reason we can rely on its index corresponding
|
|
132
127
|
// to the index of a tx in a block.
|
|
@@ -139,7 +134,7 @@ export class NoteProcessor {
|
|
|
139
134
|
if (taggedNote?.notePayload) {
|
|
140
135
|
const { notePayload: payload } = taggedNote;
|
|
141
136
|
// We have successfully decrypted the data.
|
|
142
|
-
const txHash =
|
|
137
|
+
const txHash = block.body.txEffects[indexOfTxInABlock].txHash;
|
|
143
138
|
try {
|
|
144
139
|
const noteDao = await produceNoteDao(
|
|
145
140
|
this.simulator,
|
|
@@ -178,7 +173,7 @@ export class NoteProcessor {
|
|
|
178
173
|
}
|
|
179
174
|
|
|
180
175
|
blocksAndNotes.push({
|
|
181
|
-
|
|
176
|
+
block: l2Blocks[blockIndex],
|
|
182
177
|
noteDaos,
|
|
183
178
|
});
|
|
184
179
|
}
|
|
@@ -186,7 +181,7 @@ export class NoteProcessor {
|
|
|
186
181
|
await this.processBlocksAndNotes(blocksAndNotes);
|
|
187
182
|
await this.processDeferredNotes(deferredNoteDaos);
|
|
188
183
|
|
|
189
|
-
const syncedToBlock =
|
|
184
|
+
const syncedToBlock = l2Blocks[l2Blocks.length - 1].number;
|
|
190
185
|
await this.db.setSynchedBlockNumberForPublicKey(this.publicKey, syncedToBlock);
|
|
191
186
|
|
|
192
187
|
this.log(`Synched block ${syncedToBlock}`);
|
|
@@ -199,7 +194,7 @@ export class NoteProcessor {
|
|
|
199
194
|
* transaction auxiliary data from the database. This function keeps track of new nullifiers
|
|
200
195
|
* and ensures all other transactions are updated with newly settled block information.
|
|
201
196
|
*
|
|
202
|
-
* @param blocksAndNotes - Array of objects containing
|
|
197
|
+
* @param blocksAndNotes - Array of objects containing L2 blocks, user-pertaining transaction indices, and NoteDaos.
|
|
203
198
|
*/
|
|
204
199
|
private async processBlocksAndNotes(blocksAndNotes: ProcessedData[]) {
|
|
205
200
|
const noteDaos = blocksAndNotes.flatMap(b => b.noteDaos);
|
|
@@ -215,7 +210,7 @@ export class NoteProcessor {
|
|
|
215
210
|
}
|
|
216
211
|
|
|
217
212
|
const newNullifiers: Fr[] = blocksAndNotes.flatMap(b =>
|
|
218
|
-
b.
|
|
213
|
+
b.block.body.txEffects.flatMap(txEffect => txEffect.nullifiers),
|
|
219
214
|
);
|
|
220
215
|
const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.publicKey);
|
|
221
216
|
removedNotes.forEach(noteDao => {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Note,
|
|
10
10
|
NullifierMembershipWitness,
|
|
11
11
|
type PXE,
|
|
12
|
+
SimulatedTx,
|
|
12
13
|
Tx,
|
|
13
14
|
TxEffect,
|
|
14
15
|
TxExecutionRequest,
|
|
@@ -49,7 +50,7 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer {
|
|
|
49
50
|
TxEffect,
|
|
50
51
|
LogId,
|
|
51
52
|
},
|
|
52
|
-
{ Tx, TxReceipt, EncryptedL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness },
|
|
53
|
+
{ SimulatedTx, Tx, TxReceipt, EncryptedL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness },
|
|
53
54
|
['start', 'stop'],
|
|
54
55
|
);
|
|
55
56
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
MerkleTreeId,
|
|
12
12
|
type NoteFilter,
|
|
13
13
|
type PXE,
|
|
14
|
+
SimulatedTx,
|
|
14
15
|
SimulationError,
|
|
15
16
|
Tx,
|
|
16
17
|
type TxEffect,
|
|
@@ -27,8 +28,7 @@ import {
|
|
|
27
28
|
CompleteAddress,
|
|
28
29
|
FunctionData,
|
|
29
30
|
type GrumpkinPrivateKey,
|
|
30
|
-
|
|
31
|
-
MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
31
|
+
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
32
32
|
type PartialAddress,
|
|
33
33
|
type PrivateKernelTailCircuitPublicInputs,
|
|
34
34
|
type PublicCallRequest,
|
|
@@ -71,7 +71,7 @@ export class PXEService implements PXE {
|
|
|
71
71
|
private simulator: AcirSimulator;
|
|
72
72
|
private log: DebugLogger;
|
|
73
73
|
private nodeVersion: string;
|
|
74
|
-
// serialize synchronizer and calls to
|
|
74
|
+
// serialize synchronizer and calls to proveTx.
|
|
75
75
|
// ensures that state is not changed while simulating
|
|
76
76
|
private jobQueue = new SerialQueue();
|
|
77
77
|
|
|
@@ -388,26 +388,43 @@ export class PXEService implements PXE {
|
|
|
388
388
|
return await this.node.getBlock(blockNumber);
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
public async
|
|
391
|
+
public async proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean) {
|
|
392
|
+
return (await this.simulateTx(txRequest, simulatePublic)).tx;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public async simulateTx(
|
|
396
|
+
txRequest: TxExecutionRequest,
|
|
397
|
+
simulatePublic: boolean,
|
|
398
|
+
msgSender: AztecAddress | undefined = undefined,
|
|
399
|
+
) {
|
|
392
400
|
if (!txRequest.functionData.isPrivate) {
|
|
393
401
|
throw new Error(`Public entrypoints are not allowed`);
|
|
394
402
|
}
|
|
395
|
-
|
|
396
|
-
// all simulations must be serialized w.r.t. the synchronizer
|
|
397
403
|
return await this.jobQueue.put(async () => {
|
|
398
404
|
const timer = new Timer();
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
+
const simulatedTx = await this.#simulateAndProve(txRequest, msgSender);
|
|
406
|
+
// We log only if the msgSender is undefined, as simulating with a different msgSender
|
|
407
|
+
// is unlikely to be a real transaction, and likely to be only used to read data.
|
|
408
|
+
// Meaning that it will not necessarily have produced a nullifier (and thus have no TxHash)
|
|
409
|
+
// If we log, the `getTxHash` function will throw.
|
|
410
|
+
|
|
411
|
+
if (!msgSender) {
|
|
412
|
+
this.log(`Processed private part of ${simulatedTx.tx.getTxHash()}`, {
|
|
413
|
+
eventName: 'tx-pxe-processing',
|
|
414
|
+
duration: timer.ms(),
|
|
415
|
+
...simulatedTx.tx.getStats(),
|
|
416
|
+
} satisfies TxPXEProcessingStats);
|
|
417
|
+
}
|
|
418
|
+
|
|
405
419
|
if (simulatePublic) {
|
|
406
|
-
|
|
420
|
+
// Only one transaction, so we can take index 0.
|
|
421
|
+
simulatedTx.publicReturnValues = (await this.#simulatePublicCalls(simulatedTx.tx))[0];
|
|
407
422
|
}
|
|
408
|
-
this.log.info(`Executed local simulation for ${tx.getTxHash()}`);
|
|
409
423
|
|
|
410
|
-
|
|
424
|
+
if (!msgSender) {
|
|
425
|
+
this.log.info(`Executed local simulation for ${simulatedTx.tx.getTxHash()}`);
|
|
426
|
+
}
|
|
427
|
+
return simulatedTx;
|
|
411
428
|
});
|
|
412
429
|
}
|
|
413
430
|
|
|
@@ -524,14 +541,14 @@ export class PXEService implements PXE {
|
|
|
524
541
|
};
|
|
525
542
|
}
|
|
526
543
|
|
|
527
|
-
async #simulate(txRequest: TxExecutionRequest): Promise<ExecutionResult> {
|
|
544
|
+
async #simulate(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise<ExecutionResult> {
|
|
528
545
|
// TODO - Pause syncing while simulating.
|
|
529
546
|
|
|
530
547
|
const { contractAddress, functionArtifact, portalContract } = await this.#getSimulationParameters(txRequest);
|
|
531
548
|
|
|
532
549
|
this.log('Executing simulator...');
|
|
533
550
|
try {
|
|
534
|
-
const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, portalContract);
|
|
551
|
+
const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, portalContract, msgSender);
|
|
535
552
|
this.log('Simulation completed!');
|
|
536
553
|
return result;
|
|
537
554
|
} catch (err) {
|
|
@@ -575,7 +592,7 @@ export class PXEService implements PXE {
|
|
|
575
592
|
*/
|
|
576
593
|
async #simulatePublicCalls(tx: Tx) {
|
|
577
594
|
try {
|
|
578
|
-
await this.node.simulatePublicCalls(tx);
|
|
595
|
+
return await this.node.simulatePublicCalls(tx);
|
|
579
596
|
} catch (err) {
|
|
580
597
|
// Try to fill in the noir call stack since the PXE may have access to the debug metadata
|
|
581
598
|
if (err instanceof SimulationError) {
|
|
@@ -599,28 +616,26 @@ export class PXEService implements PXE {
|
|
|
599
616
|
|
|
600
617
|
/**
|
|
601
618
|
* Simulate a transaction, generate a kernel proof, and create a private transaction object.
|
|
602
|
-
* The function takes in a transaction request
|
|
603
|
-
*
|
|
619
|
+
* The function takes in a transaction request, simulates it, and then generates a kernel proof
|
|
620
|
+
* using the simulation result. Finally, it creates a private
|
|
604
621
|
* transaction object with the generated proof and public inputs. If a new contract address is provided,
|
|
605
622
|
* the function will also include the new contract's public functions in the transaction object.
|
|
606
623
|
*
|
|
607
624
|
* @param txExecutionRequest - The transaction request to be simulated and proved.
|
|
608
625
|
* @param signature - The ECDSA signature for the transaction request.
|
|
609
|
-
* @
|
|
626
|
+
* @param msgSender - (Optional) The message sender to use for the simulation.
|
|
627
|
+
* @returns An object tract contains:
|
|
628
|
+
* A private transaction object containing the proof, public inputs, and encrypted logs.
|
|
629
|
+
* The return values of the private execution
|
|
610
630
|
*/
|
|
611
|
-
async #simulateAndProve(txExecutionRequest: TxExecutionRequest) {
|
|
612
|
-
// TODO - Pause syncing while simulating.
|
|
613
|
-
|
|
631
|
+
async #simulateAndProve(txExecutionRequest: TxExecutionRequest, msgSender?: AztecAddress) {
|
|
614
632
|
// Get values that allow us to reconstruct the block hash
|
|
615
|
-
const executionResult = await this.#simulate(txExecutionRequest);
|
|
633
|
+
const executionResult = await this.#simulate(txExecutionRequest, msgSender);
|
|
616
634
|
|
|
617
635
|
const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node);
|
|
618
636
|
const kernelProver = new KernelProver(kernelOracle);
|
|
619
637
|
this.log(`Executing kernel prover...`);
|
|
620
638
|
const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
|
|
621
|
-
this.log(
|
|
622
|
-
`Needs setup: ${publicInputs.needsSetup}, needs app logic: ${publicInputs.needsAppLogic}, needs teardown: ${publicInputs.needsTeardown}`,
|
|
623
|
-
);
|
|
624
639
|
|
|
625
640
|
const encryptedLogs = new EncryptedTxL2Logs(collectEncryptedLogs(executionResult));
|
|
626
641
|
const unencryptedLogs = new UnencryptedTxL2Logs(collectUnencryptedLogs(executionResult));
|
|
@@ -630,7 +645,8 @@ export class PXEService implements PXE {
|
|
|
630
645
|
// TODO(#757): Enforce proper ordering of enqueued public calls
|
|
631
646
|
await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);
|
|
632
647
|
|
|
633
|
-
|
|
648
|
+
const tx = new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions);
|
|
649
|
+
return new SimulatedTx(tx, [executionResult.returnValues]);
|
|
634
650
|
}
|
|
635
651
|
|
|
636
652
|
/**
|
|
@@ -681,55 +697,62 @@ export class PXEService implements PXE {
|
|
|
681
697
|
publicInputs: PrivateKernelTailCircuitPublicInputs,
|
|
682
698
|
enqueuedPublicCalls: PublicCallRequest[],
|
|
683
699
|
) {
|
|
700
|
+
if (!publicInputs.forPublic) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
684
704
|
const enqueuedPublicCallStackItems = await Promise.all(enqueuedPublicCalls.map(c => c.toCallRequest()));
|
|
685
705
|
|
|
686
706
|
// Validate all items in enqueued public calls are in the kernel emitted stack
|
|
687
707
|
const enqueuedRevertiblePublicCallStackItems = enqueuedPublicCallStackItems.filter(enqueued =>
|
|
688
|
-
publicInputs.end.publicCallStack.find(item => item.equals(enqueued)),
|
|
708
|
+
publicInputs.forPublic!.end.publicCallStack.find(item => item.equals(enqueued)),
|
|
689
709
|
);
|
|
690
710
|
|
|
691
|
-
const revertibleStackSize = arrayNonEmptyLength(publicInputs.end.publicCallStack, item => item.isEmpty());
|
|
711
|
+
const revertibleStackSize = arrayNonEmptyLength(publicInputs.forPublic.end.publicCallStack, item => item.isEmpty());
|
|
692
712
|
|
|
693
713
|
if (enqueuedRevertiblePublicCallStackItems.length !== revertibleStackSize) {
|
|
694
714
|
throw new Error(
|
|
695
715
|
`Enqueued revertible public function calls and revertible public call stack do not match.\nEnqueued calls: ${enqueuedRevertiblePublicCallStackItems
|
|
696
716
|
.map(h => h.hash.toString())
|
|
697
|
-
.join(', ')}\nPublic call stack: ${publicInputs.end.publicCallStack
|
|
717
|
+
.join(', ')}\nPublic call stack: ${publicInputs.forPublic.end.publicCallStack
|
|
718
|
+
.map(i => i.toString())
|
|
719
|
+
.join(', ')}`,
|
|
698
720
|
);
|
|
699
721
|
}
|
|
700
722
|
|
|
701
723
|
// Override kernel output
|
|
702
|
-
publicInputs.end.publicCallStack = padArrayEnd(
|
|
724
|
+
publicInputs.forPublic.end.publicCallStack = padArrayEnd(
|
|
703
725
|
enqueuedRevertiblePublicCallStackItems,
|
|
704
726
|
CallRequest.empty(),
|
|
705
|
-
|
|
727
|
+
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
706
728
|
);
|
|
707
729
|
|
|
708
730
|
// Do the same for non-revertible
|
|
709
731
|
|
|
710
732
|
const enqueuedNonRevertiblePublicCallStackItems = enqueuedPublicCallStackItems.filter(enqueued =>
|
|
711
|
-
publicInputs.endNonRevertibleData.publicCallStack.find(item => item.equals(enqueued)),
|
|
733
|
+
publicInputs.forPublic!.endNonRevertibleData.publicCallStack.find(item => item.equals(enqueued)),
|
|
712
734
|
);
|
|
713
735
|
|
|
714
|
-
const nonRevertibleStackSize = arrayNonEmptyLength(
|
|
715
|
-
|
|
736
|
+
const nonRevertibleStackSize = arrayNonEmptyLength(
|
|
737
|
+
publicInputs.forPublic.endNonRevertibleData.publicCallStack,
|
|
738
|
+
item => item.isEmpty(),
|
|
716
739
|
);
|
|
717
740
|
|
|
718
741
|
if (enqueuedNonRevertiblePublicCallStackItems.length !== nonRevertibleStackSize) {
|
|
719
742
|
throw new Error(
|
|
720
743
|
`Enqueued non-revertible public function calls and non-revertible public call stack do not match.\nEnqueued calls: ${enqueuedNonRevertiblePublicCallStackItems
|
|
721
744
|
.map(h => h.hash.toString())
|
|
722
|
-
.join(', ')}\nPublic call stack: ${publicInputs.endNonRevertibleData.publicCallStack
|
|
745
|
+
.join(', ')}\nPublic call stack: ${publicInputs.forPublic.endNonRevertibleData.publicCallStack
|
|
723
746
|
.map(i => i.toString())
|
|
724
747
|
.join(', ')}`,
|
|
725
748
|
);
|
|
726
749
|
}
|
|
727
750
|
|
|
728
751
|
// Override kernel output
|
|
729
|
-
publicInputs.endNonRevertibleData.publicCallStack = padArrayEnd(
|
|
752
|
+
publicInputs.forPublic.endNonRevertibleData.publicCallStack = padArrayEnd(
|
|
730
753
|
enqueuedNonRevertiblePublicCallStackItems,
|
|
731
754
|
CallRequest.empty(),
|
|
732
|
-
|
|
755
|
+
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
733
756
|
);
|
|
734
757
|
}
|
|
735
758
|
|
|
@@ -135,12 +135,12 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
|
|
|
135
135
|
authWitnesses: [],
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
await expect(async () => await pxe.
|
|
138
|
+
await expect(async () => await pxe.proveTx(txExecutionRequest, false)).rejects.toThrow(
|
|
139
139
|
'Public entrypoints are not allowed',
|
|
140
140
|
);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
// Note: Not testing a successful run of `
|
|
143
|
+
// Note: Not testing a successful run of `proveTx`, `sendTx`, `getTxReceipt` and `viewTx` here as it requires
|
|
144
144
|
// a larger setup and it's sufficiently tested in the e2e tests.
|
|
145
145
|
|
|
146
146
|
it('throws when getting public storage for non-existent contract', async () => {
|