@aztec/pxe 0.16.4 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/bin/index.js +0 -0
- package/dest/database/memory_db.js +3 -3
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +2 -2
- package/dest/simulator_oracle/index.js +3 -3
- package/dest/synchronizer/synchronizer.js +2 -2
- package/package.json +9 -9
- package/src/bin/index.ts +0 -39
- package/src/config/index.ts +0 -37
- package/src/contract_data_oracle/index.ts +0 -163
- package/src/contract_database/index.ts +0 -1
- package/src/contract_database/memory_contract_database.ts +0 -58
- package/src/contract_tree/index.ts +0 -241
- package/src/database/database.ts +0 -140
- package/src/database/index.ts +0 -2
- package/src/database/memory_db.ts +0 -181
- package/src/database/note_dao.ts +0 -90
- package/src/index.ts +0 -11
- package/src/kernel_oracle/index.ts +0 -39
- package/src/kernel_prover/index.ts +0 -2
- package/src/kernel_prover/kernel_prover.ts +0 -325
- package/src/kernel_prover/proof_creator.ts +0 -155
- package/src/kernel_prover/proving_data_oracle.ts +0 -69
- package/src/note_processor/index.ts +0 -1
- package/src/note_processor/note_processor.ts +0 -286
- package/src/pxe_http/index.ts +0 -1
- package/src/pxe_http/pxe_http_server.ts +0 -78
- package/src/pxe_service/create_pxe_service.ts +0 -52
- package/src/pxe_service/index.ts +0 -3
- package/src/pxe_service/pxe_service.ts +0 -679
- package/src/pxe_service/test/pxe_test_suite.ts +0 -132
- package/src/simulator/index.ts +0 -24
- package/src/simulator_oracle/index.ts +0 -186
- package/src/synchronizer/index.ts +0 -1
- package/src/synchronizer/synchronizer.ts +0 -296
|
@@ -1,679 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AcirSimulator,
|
|
3
|
-
ExecutionResult,
|
|
4
|
-
collectEncryptedLogs,
|
|
5
|
-
collectEnqueuedPublicFunctionCalls,
|
|
6
|
-
collectUnencryptedLogs,
|
|
7
|
-
resolveOpcodeLocations,
|
|
8
|
-
} from '@aztec/acir-simulator';
|
|
9
|
-
import {
|
|
10
|
-
AztecAddress,
|
|
11
|
-
CallRequest,
|
|
12
|
-
CompleteAddress,
|
|
13
|
-
FunctionData,
|
|
14
|
-
GrumpkinPrivateKey,
|
|
15
|
-
KernelCircuitPublicInputsFinal,
|
|
16
|
-
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
17
|
-
PartialAddress,
|
|
18
|
-
PublicCallRequest,
|
|
19
|
-
} from '@aztec/circuits.js';
|
|
20
|
-
import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/abis';
|
|
21
|
-
import { encodeArguments } from '@aztec/foundation/abi';
|
|
22
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
23
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
24
|
-
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
25
|
-
import { NoirWasmVersion } from '@aztec/noir-compiler/versions';
|
|
26
|
-
import {
|
|
27
|
-
AuthWitness,
|
|
28
|
-
AztecNode,
|
|
29
|
-
ContractDao,
|
|
30
|
-
ContractData,
|
|
31
|
-
DeployedContract,
|
|
32
|
-
ExtendedContractData,
|
|
33
|
-
ExtendedNote,
|
|
34
|
-
FunctionCall,
|
|
35
|
-
GetUnencryptedLogsResponse,
|
|
36
|
-
KeyStore,
|
|
37
|
-
L2Block,
|
|
38
|
-
L2Tx,
|
|
39
|
-
LogFilter,
|
|
40
|
-
MerkleTreeId,
|
|
41
|
-
NodeInfo,
|
|
42
|
-
NoteFilter,
|
|
43
|
-
PXE,
|
|
44
|
-
SimulationError,
|
|
45
|
-
Tx,
|
|
46
|
-
TxExecutionRequest,
|
|
47
|
-
TxHash,
|
|
48
|
-
TxL2Logs,
|
|
49
|
-
TxReceipt,
|
|
50
|
-
TxStatus,
|
|
51
|
-
getNewContractPublicFunctions,
|
|
52
|
-
isNoirCallStackUnresolved,
|
|
53
|
-
toContractDao,
|
|
54
|
-
} from '@aztec/types';
|
|
55
|
-
|
|
56
|
-
import { PXEServiceConfig, getPackageInfo } from '../config/index.js';
|
|
57
|
-
import { ContractDataOracle } from '../contract_data_oracle/index.js';
|
|
58
|
-
import { Database } from '../database/index.js';
|
|
59
|
-
import { NoteDao } from '../database/note_dao.js';
|
|
60
|
-
import { KernelOracle } from '../kernel_oracle/index.js';
|
|
61
|
-
import { KernelProver } from '../kernel_prover/kernel_prover.js';
|
|
62
|
-
import { getAcirSimulator } from '../simulator/index.js';
|
|
63
|
-
import { Synchronizer } from '../synchronizer/index.js';
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* A Private eXecution Environment (PXE) implementation.
|
|
67
|
-
*/
|
|
68
|
-
export class PXEService implements PXE {
|
|
69
|
-
private synchronizer: Synchronizer;
|
|
70
|
-
private contractDataOracle: ContractDataOracle;
|
|
71
|
-
private simulator: AcirSimulator;
|
|
72
|
-
private log: DebugLogger;
|
|
73
|
-
private sandboxVersion: string;
|
|
74
|
-
|
|
75
|
-
constructor(
|
|
76
|
-
private keyStore: KeyStore,
|
|
77
|
-
private node: AztecNode,
|
|
78
|
-
private db: Database,
|
|
79
|
-
private config: PXEServiceConfig,
|
|
80
|
-
logSuffix?: string,
|
|
81
|
-
) {
|
|
82
|
-
this.log = createDebugLogger(logSuffix ? `aztec:pxe_service_${logSuffix}` : `aztec:pxe_service`);
|
|
83
|
-
this.synchronizer = new Synchronizer(node, db, logSuffix);
|
|
84
|
-
this.contractDataOracle = new ContractDataOracle(db, node);
|
|
85
|
-
this.simulator = getAcirSimulator(db, node, keyStore, this.contractDataOracle);
|
|
86
|
-
|
|
87
|
-
this.sandboxVersion = getPackageInfo().version;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Starts the PXE Service by beginning the synchronization process between the Aztec node and the database.
|
|
92
|
-
*
|
|
93
|
-
* @returns A promise that resolves when the server has started successfully.
|
|
94
|
-
*/
|
|
95
|
-
public async start() {
|
|
96
|
-
const { l2BlockPollingIntervalMS, l2StartingBlock } = this.config;
|
|
97
|
-
await this.synchronizer.start(l2StartingBlock, 1, l2BlockPollingIntervalMS);
|
|
98
|
-
const info = await this.getNodeInfo();
|
|
99
|
-
this.log.info(`Started PXE connected to chain ${info.chainId} version ${info.protocolVersion}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Stops the PXE Service, halting processing of new transactions and shutting down the synchronizer.
|
|
104
|
-
* This function ensures that all ongoing tasks are completed before stopping the server.
|
|
105
|
-
* It is useful for gracefully shutting down the server during maintenance or restarts.
|
|
106
|
-
*
|
|
107
|
-
* @returns A Promise resolving once the server has been stopped successfully.
|
|
108
|
-
*/
|
|
109
|
-
public async stop() {
|
|
110
|
-
await this.synchronizer.stop();
|
|
111
|
-
this.log.info('Stopped');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Returns an estimate of the db size in bytes. */
|
|
115
|
-
public estimateDbSize() {
|
|
116
|
-
return this.db.estimateSize();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public addAuthWitness(witness: AuthWitness) {
|
|
120
|
-
return this.db.addAuthWitness(witness.requestHash, witness.witness);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public addCapsule(capsule: Fr[]) {
|
|
124
|
-
return this.db.addCapsule(capsule);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise<CompleteAddress> {
|
|
128
|
-
const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress);
|
|
129
|
-
const wasAdded = await this.db.addCompleteAddress(completeAddress);
|
|
130
|
-
if (wasAdded) {
|
|
131
|
-
const pubKey = this.keyStore.addAccount(privKey);
|
|
132
|
-
this.synchronizer.addAccount(pubKey, this.keyStore, this.config.l2StartingBlock);
|
|
133
|
-
this.log.info(`Registered account ${completeAddress.address.toString()}`);
|
|
134
|
-
this.log.debug(`Registered account\n ${completeAddress.toReadableString()}`);
|
|
135
|
-
} else {
|
|
136
|
-
this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
|
|
137
|
-
}
|
|
138
|
-
return completeAddress;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
|
|
142
|
-
// Get complete addresses of both the recipients and the accounts
|
|
143
|
-
const addresses = await this.db.getCompleteAddresses();
|
|
144
|
-
// Filter out the addresses not corresponding to accounts
|
|
145
|
-
const accountPubKeys = await this.keyStore.getAccounts();
|
|
146
|
-
const accounts = addresses.filter(address => accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
|
|
147
|
-
return accounts;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
|
|
151
|
-
const result = await this.getRegisteredAccounts();
|
|
152
|
-
const account = result.find(r => r.address.equals(address));
|
|
153
|
-
return Promise.resolve(account);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
public async registerRecipient(recipient: CompleteAddress): Promise<void> {
|
|
157
|
-
const wasAdded = await this.db.addCompleteAddress(recipient);
|
|
158
|
-
if (wasAdded) {
|
|
159
|
-
this.log.info(`Added recipient:\n ${recipient.toReadableString()}`);
|
|
160
|
-
} else {
|
|
161
|
-
this.log.info(`Recipient:\n "${recipient.toReadableString()}"\n already registered.`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
public async getRecipients(): Promise<CompleteAddress[]> {
|
|
166
|
-
// Get complete addresses of both the recipients and the accounts
|
|
167
|
-
const addresses = await this.db.getCompleteAddresses();
|
|
168
|
-
// Filter out the addresses corresponding to accounts
|
|
169
|
-
const accountPubKeys = await this.keyStore.getAccounts();
|
|
170
|
-
const recipients = addresses.filter(address => !accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
|
|
171
|
-
return recipients;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
public async getRecipient(address: AztecAddress): Promise<CompleteAddress | undefined> {
|
|
175
|
-
const result = await this.getRecipients();
|
|
176
|
-
const recipient = result.find(r => r.address.equals(address));
|
|
177
|
-
return Promise.resolve(recipient);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
public async addContracts(contracts: DeployedContract[]) {
|
|
181
|
-
const contractDaos = contracts.map(c => toContractDao(c.artifact, c.completeAddress, c.portalContract));
|
|
182
|
-
await Promise.all(contractDaos.map(c => this.db.addContract(c)));
|
|
183
|
-
for (const contract of contractDaos) {
|
|
184
|
-
const portalInfo =
|
|
185
|
-
contract.portalContract && !contract.portalContract.isZero() ? ` with portal ${contract.portalContract}` : '';
|
|
186
|
-
this.log.info(`Added contract ${contract.name} at ${contract.completeAddress.address}${portalInfo}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
public async getContracts(): Promise<AztecAddress[]> {
|
|
191
|
-
return (await this.db.getContracts()).map(c => c.completeAddress.address);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
public async getPublicStorageAt(contract: AztecAddress, slot: Fr) {
|
|
195
|
-
if ((await this.getContractData(contract)) === undefined) {
|
|
196
|
-
throw new Error(`Contract ${contract.toString()} is not deployed`);
|
|
197
|
-
}
|
|
198
|
-
return await this.node.getPublicStorageAt(contract, slot);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public async getNotes(filter: NoteFilter): Promise<ExtendedNote[]> {
|
|
202
|
-
const noteDaos = await this.db.getNotes(filter);
|
|
203
|
-
|
|
204
|
-
// TODO(benesjan): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
|
|
205
|
-
// key derivation will affect all this
|
|
206
|
-
const extendedNotes = noteDaos.map(async dao => {
|
|
207
|
-
let owner = filter.owner;
|
|
208
|
-
if (owner === undefined) {
|
|
209
|
-
const completeAddresses = (await this.db.getCompleteAddresses()).find(address =>
|
|
210
|
-
address.publicKey.equals(dao.publicKey),
|
|
211
|
-
);
|
|
212
|
-
if (completeAddresses === undefined) {
|
|
213
|
-
throw new Error(`Cannot find complete address for public key ${dao.publicKey.toString()}`);
|
|
214
|
-
}
|
|
215
|
-
owner = completeAddresses.address;
|
|
216
|
-
}
|
|
217
|
-
return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.txHash);
|
|
218
|
-
});
|
|
219
|
-
return Promise.all(extendedNotes);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public async addNote(note: ExtendedNote) {
|
|
223
|
-
const { publicKey } = (await this.db.getCompleteAddress(note.owner)) ?? {};
|
|
224
|
-
if (!publicKey) {
|
|
225
|
-
throw new Error('Unknown account.');
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const nonces = await this.getNoteNonces(note);
|
|
229
|
-
if (nonces.length === 0) {
|
|
230
|
-
throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
for (const nonce of nonces) {
|
|
234
|
-
const { innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier } =
|
|
235
|
-
await this.simulator.computeNoteHashAndNullifier(note.contractAddress, nonce, note.storageSlot, note.note);
|
|
236
|
-
|
|
237
|
-
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)
|
|
238
|
-
// This can always be `uniqueSiloedNoteHash` once notes added from public also include nonces.
|
|
239
|
-
const noteHashToLookUp = nonce.isZero() ? siloedNoteHash : uniqueSiloedNoteHash;
|
|
240
|
-
const index = await this.node.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, noteHashToLookUp);
|
|
241
|
-
if (index === undefined) {
|
|
242
|
-
throw new Error('Note does not exist.');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const siloedNullifier = siloNullifier(note.contractAddress, innerNullifier!);
|
|
246
|
-
const nullifierIndex = await this.node.findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, siloedNullifier);
|
|
247
|
-
if (nullifierIndex !== undefined) {
|
|
248
|
-
throw new Error('The note has been destroyed.');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
await this.db.addNote(
|
|
252
|
-
new NoteDao(
|
|
253
|
-
note.note,
|
|
254
|
-
note.contractAddress,
|
|
255
|
-
note.storageSlot,
|
|
256
|
-
note.txHash,
|
|
257
|
-
nonce,
|
|
258
|
-
innerNoteHash,
|
|
259
|
-
siloedNullifier,
|
|
260
|
-
index,
|
|
261
|
-
publicKey,
|
|
262
|
-
),
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Finds the nonce(s) for a given note.
|
|
269
|
-
* @param note - The note to find the nonces for.
|
|
270
|
-
* @returns The nonces of the note.
|
|
271
|
-
* @remarks More than a single nonce may be returned since there might be more than one nonce for a given note.
|
|
272
|
-
*/
|
|
273
|
-
private async getNoteNonces(note: ExtendedNote): Promise<Fr[]> {
|
|
274
|
-
const tx = await this.node.getTx(note.txHash);
|
|
275
|
-
if (!tx) {
|
|
276
|
-
throw new Error(`Unknown tx: ${note.txHash}`);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const nonces: Fr[] = [];
|
|
280
|
-
const firstNullifier = tx.newNullifiers[0];
|
|
281
|
-
const commitments = tx.newCommitments;
|
|
282
|
-
for (let i = 0; i < commitments.length; ++i) {
|
|
283
|
-
const commitment = commitments[i];
|
|
284
|
-
if (commitment.equals(Fr.ZERO)) {
|
|
285
|
-
break;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const nonce = computeCommitmentNonce(firstNullifier, i);
|
|
289
|
-
const { siloedNoteHash, uniqueSiloedNoteHash } = await this.simulator.computeNoteHashAndNullifier(
|
|
290
|
-
note.contractAddress,
|
|
291
|
-
nonce,
|
|
292
|
-
note.storageSlot,
|
|
293
|
-
note.note,
|
|
294
|
-
);
|
|
295
|
-
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)
|
|
296
|
-
// Remove this once notes added from public also include nonces.
|
|
297
|
-
if (commitment.equals(siloedNoteHash)) {
|
|
298
|
-
nonces.push(Fr.ZERO);
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
if (commitment.equals(uniqueSiloedNoteHash)) {
|
|
302
|
-
nonces.push(nonce);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return nonces;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
public async getBlock(blockNumber: number): Promise<L2Block | undefined> {
|
|
310
|
-
// If a negative block number is provided the current block number is fetched.
|
|
311
|
-
if (blockNumber < 0) {
|
|
312
|
-
blockNumber = await this.node.getBlockNumber();
|
|
313
|
-
}
|
|
314
|
-
return await this.node.getBlock(blockNumber);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
public async simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean) {
|
|
318
|
-
if (!txRequest.functionData.isPrivate) {
|
|
319
|
-
throw new Error(`Public entrypoints are not allowed`);
|
|
320
|
-
}
|
|
321
|
-
if (txRequest.functionData.isInternal === undefined) {
|
|
322
|
-
throw new Error(`Unspecified internal are not allowed`);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// We get the contract address from origin, since contract deployments are signalled as origin from their own address
|
|
326
|
-
// TODO: Is this ok? Should it be changed to be from ZERO?
|
|
327
|
-
const deployedContractAddress = txRequest.txContext.isContractDeploymentTx ? txRequest.origin : undefined;
|
|
328
|
-
const newContract = deployedContractAddress ? await this.db.getContract(deployedContractAddress) : undefined;
|
|
329
|
-
|
|
330
|
-
const tx = await this.#simulateAndProve(txRequest, newContract);
|
|
331
|
-
if (simulatePublic) {
|
|
332
|
-
await this.#simulatePublicCalls(tx);
|
|
333
|
-
}
|
|
334
|
-
this.log.info(`Executed local simulation for ${await tx.getTxHash()}`);
|
|
335
|
-
|
|
336
|
-
return tx;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
public async sendTx(tx: Tx): Promise<TxHash> {
|
|
340
|
-
const txHash = await tx.getTxHash();
|
|
341
|
-
if (await this.node.getTx(txHash)) {
|
|
342
|
-
throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`);
|
|
343
|
-
}
|
|
344
|
-
this.log.info(`Sending transaction ${txHash}`);
|
|
345
|
-
await this.node.sendTx(tx);
|
|
346
|
-
return txHash;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
public async viewTx(functionName: string, args: any[], to: AztecAddress, _from?: AztecAddress) {
|
|
350
|
-
// TODO - Should check if `from` has the permission to call the view function.
|
|
351
|
-
const functionCall = await this.#getFunctionCall(functionName, args, to);
|
|
352
|
-
const executionResult = await this.#simulateUnconstrained(functionCall);
|
|
353
|
-
|
|
354
|
-
// TODO - Return typed result based on the function artifact.
|
|
355
|
-
return executionResult;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
|
|
359
|
-
let txReceipt = new TxReceipt(txHash, TxStatus.DROPPED, 'Tx dropped by P2P node.');
|
|
360
|
-
|
|
361
|
-
// We first check if the tx is in pending (instead of first checking if it is mined) because if we first check
|
|
362
|
-
// for mined and then for pending there could be a race condition where the tx is mined between the two checks
|
|
363
|
-
// and we would incorrectly return a TxReceipt with status DROPPED
|
|
364
|
-
const pendingTx = await this.node.getPendingTxByHash(txHash);
|
|
365
|
-
if (pendingTx) {
|
|
366
|
-
txReceipt = new TxReceipt(txHash, TxStatus.PENDING, '');
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const settledTx = await this.node.getTx(txHash);
|
|
370
|
-
if (settledTx) {
|
|
371
|
-
const deployedContractAddress = settledTx.newContractData.find(
|
|
372
|
-
c => !c.contractAddress.equals(AztecAddress.ZERO),
|
|
373
|
-
)?.contractAddress;
|
|
374
|
-
|
|
375
|
-
txReceipt = new TxReceipt(
|
|
376
|
-
txHash,
|
|
377
|
-
TxStatus.MINED,
|
|
378
|
-
'',
|
|
379
|
-
settledTx.blockHash,
|
|
380
|
-
settledTx.blockNumber,
|
|
381
|
-
deployedContractAddress,
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return txReceipt;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
public async getTx(txHash: TxHash): Promise<L2Tx | undefined> {
|
|
389
|
-
return await this.node.getTx(txHash);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async getBlockNumber(): Promise<number> {
|
|
393
|
-
return await this.node.getBlockNumber();
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
public async getExtendedContractData(contractAddress: AztecAddress): Promise<ExtendedContractData | undefined> {
|
|
397
|
-
return await this.node.getExtendedContractData(contractAddress);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
public async getContractData(contractAddress: AztecAddress): Promise<ContractData | undefined> {
|
|
401
|
-
return await this.node.getContractData(contractAddress);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Gets unencrypted logs based on the provided filter.
|
|
406
|
-
* @param filter - The filter to apply to the logs.
|
|
407
|
-
* @returns The requested logs.
|
|
408
|
-
*/
|
|
409
|
-
public getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
|
|
410
|
-
return this.node.getUnencryptedLogs(filter);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
async #getFunctionCall(functionName: string, args: any[], to: AztecAddress): Promise<FunctionCall> {
|
|
414
|
-
const contract = await this.db.getContract(to);
|
|
415
|
-
if (!contract) {
|
|
416
|
-
throw new Error(
|
|
417
|
-
`Unknown contract ${to}: add it to PXE Service by calling server.addContracts(...).\nSee docs for context: https://docs.aztec.network/dev_docs/contracts/common_errors#unknown-contract-error`,
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const functionDao = contract.functions.find(f => f.name === functionName);
|
|
422
|
-
if (!functionDao) {
|
|
423
|
-
throw new Error(`Unknown function ${functionName} in contract ${contract.name}.`);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return {
|
|
427
|
-
args: encodeArguments(functionDao, args),
|
|
428
|
-
functionData: FunctionData.fromAbi(functionDao),
|
|
429
|
-
to,
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
public async getNodeInfo(): Promise<NodeInfo> {
|
|
434
|
-
const [version, chainId, contractAddresses] = await Promise.all([
|
|
435
|
-
this.node.getVersion(),
|
|
436
|
-
this.node.getChainId(),
|
|
437
|
-
this.node.getL1ContractAddresses(),
|
|
438
|
-
]);
|
|
439
|
-
|
|
440
|
-
const nodeInfo: NodeInfo = {
|
|
441
|
-
sandboxVersion: this.sandboxVersion,
|
|
442
|
-
compatibleNargoVersion: NoirWasmVersion,
|
|
443
|
-
chainId,
|
|
444
|
-
protocolVersion: version,
|
|
445
|
-
l1ContractAddresses: contractAddresses,
|
|
446
|
-
};
|
|
447
|
-
return nodeInfo;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Retrieves the simulation parameters required to run an ACIR simulation.
|
|
452
|
-
* This includes the contract address, function artifact, portal contract address, and historical tree roots.
|
|
453
|
-
*
|
|
454
|
-
* @param execRequest - The transaction request object containing details of the contract call.
|
|
455
|
-
* @returns An object containing the contract address, function artifact, portal contract address, and historical tree roots.
|
|
456
|
-
*/
|
|
457
|
-
async #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) {
|
|
458
|
-
const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin;
|
|
459
|
-
const functionArtifact = await this.contractDataOracle.getFunctionArtifact(
|
|
460
|
-
contractAddress,
|
|
461
|
-
execRequest.functionData.selector,
|
|
462
|
-
);
|
|
463
|
-
const debug = await this.contractDataOracle.getFunctionDebugMetadata(
|
|
464
|
-
contractAddress,
|
|
465
|
-
execRequest.functionData.selector,
|
|
466
|
-
);
|
|
467
|
-
const portalContract = await this.contractDataOracle.getPortalContractAddress(contractAddress);
|
|
468
|
-
|
|
469
|
-
return {
|
|
470
|
-
contractAddress,
|
|
471
|
-
functionArtifact: {
|
|
472
|
-
...functionArtifact,
|
|
473
|
-
debug,
|
|
474
|
-
},
|
|
475
|
-
portalContract,
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async #simulate(txRequest: TxExecutionRequest): Promise<ExecutionResult> {
|
|
480
|
-
// TODO - Pause syncing while simulating.
|
|
481
|
-
|
|
482
|
-
const { contractAddress, functionArtifact, portalContract } = await this.#getSimulationParameters(txRequest);
|
|
483
|
-
|
|
484
|
-
this.log('Executing simulator...');
|
|
485
|
-
try {
|
|
486
|
-
const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, portalContract);
|
|
487
|
-
this.log('Simulation completed!');
|
|
488
|
-
return result;
|
|
489
|
-
} catch (err) {
|
|
490
|
-
if (err instanceof SimulationError) {
|
|
491
|
-
await this.#enrichSimulationError(err);
|
|
492
|
-
}
|
|
493
|
-
throw err;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Simulate an unconstrained transaction on the given contract, without considering constraints set by ACIR.
|
|
499
|
-
* The simulation parameters are fetched using ContractDataOracle and executed using AcirSimulator.
|
|
500
|
-
* Returns the simulation result containing the outputs of the unconstrained function.
|
|
501
|
-
*
|
|
502
|
-
* @param execRequest - The transaction request object containing the target contract and function data.
|
|
503
|
-
* @returns The simulation result containing the outputs of the unconstrained function.
|
|
504
|
-
*/
|
|
505
|
-
async #simulateUnconstrained(execRequest: FunctionCall) {
|
|
506
|
-
const { contractAddress, functionArtifact } = await this.#getSimulationParameters(execRequest);
|
|
507
|
-
|
|
508
|
-
this.log('Executing unconstrained simulator...');
|
|
509
|
-
try {
|
|
510
|
-
const result = await this.simulator.runUnconstrained(execRequest, functionArtifact, contractAddress, this.node);
|
|
511
|
-
this.log('Unconstrained simulation completed!');
|
|
512
|
-
|
|
513
|
-
return result;
|
|
514
|
-
} catch (err) {
|
|
515
|
-
if (err instanceof SimulationError) {
|
|
516
|
-
await this.#enrichSimulationError(err);
|
|
517
|
-
}
|
|
518
|
-
throw err;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Simulate the public part of a transaction.
|
|
524
|
-
* This allows to catch public execution errors before submitting the transaction.
|
|
525
|
-
* It can also be used for estimating gas in the future.
|
|
526
|
-
* @param tx - The transaction to be simulated.
|
|
527
|
-
*/
|
|
528
|
-
async #simulatePublicCalls(tx: Tx) {
|
|
529
|
-
try {
|
|
530
|
-
await this.node.simulatePublicCalls(tx);
|
|
531
|
-
} catch (err) {
|
|
532
|
-
// Try to fill in the noir call stack since the PXE may have access to the debug metadata
|
|
533
|
-
if (err instanceof SimulationError) {
|
|
534
|
-
const callStack = err.getCallStack();
|
|
535
|
-
const originalFailingFunction = callStack[callStack.length - 1];
|
|
536
|
-
const debugInfo = await this.contractDataOracle.getFunctionDebugMetadata(
|
|
537
|
-
originalFailingFunction.contractAddress,
|
|
538
|
-
originalFailingFunction.functionSelector,
|
|
539
|
-
);
|
|
540
|
-
const noirCallStack = err.getNoirCallStack();
|
|
541
|
-
if (debugInfo && isNoirCallStackUnresolved(noirCallStack)) {
|
|
542
|
-
const parsedCallStack = resolveOpcodeLocations(noirCallStack, debugInfo);
|
|
543
|
-
err.setNoirCallStack(parsedCallStack);
|
|
544
|
-
}
|
|
545
|
-
await this.#enrichSimulationError(err);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
throw err;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Simulate a transaction, generate a kernel proof, and create a private transaction object.
|
|
554
|
-
* The function takes in a transaction request and an ECDSA signature. It simulates the transaction,
|
|
555
|
-
* then generates a kernel proof using the simulation result. Finally, it creates a private
|
|
556
|
-
* transaction object with the generated proof and public inputs. If a new contract address is provided,
|
|
557
|
-
* the function will also include the new contract's public functions in the transaction object.
|
|
558
|
-
*
|
|
559
|
-
* @param txExecutionRequest - The transaction request to be simulated and proved.
|
|
560
|
-
* @param signature - The ECDSA signature for the transaction request.
|
|
561
|
-
* @param newContract - Optional. The address of a new contract to be included in the transaction object.
|
|
562
|
-
* @returns A private transaction object containing the proof, public inputs, and encrypted logs.
|
|
563
|
-
*/
|
|
564
|
-
async #simulateAndProve(txExecutionRequest: TxExecutionRequest, newContract: ContractDao | undefined) {
|
|
565
|
-
// TODO - Pause syncing while simulating.
|
|
566
|
-
|
|
567
|
-
// Get values that allow us to reconstruct the block hash
|
|
568
|
-
const executionResult = await this.#simulate(txExecutionRequest);
|
|
569
|
-
|
|
570
|
-
const kernelOracle = new KernelOracle(this.contractDataOracle, this.node);
|
|
571
|
-
const kernelProver = new KernelProver(kernelOracle);
|
|
572
|
-
this.log(`Executing kernel prover...`);
|
|
573
|
-
const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
|
|
574
|
-
|
|
575
|
-
const encryptedLogs = new TxL2Logs(collectEncryptedLogs(executionResult));
|
|
576
|
-
const unencryptedLogs = new TxL2Logs(collectUnencryptedLogs(executionResult));
|
|
577
|
-
const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);
|
|
578
|
-
|
|
579
|
-
const extendedContractData = newContract
|
|
580
|
-
? new ExtendedContractData(
|
|
581
|
-
new ContractData(newContract.completeAddress.address, newContract.portalContract),
|
|
582
|
-
getNewContractPublicFunctions(newContract),
|
|
583
|
-
newContract.completeAddress.partialAddress,
|
|
584
|
-
newContract.completeAddress.publicKey,
|
|
585
|
-
)
|
|
586
|
-
: ExtendedContractData.empty();
|
|
587
|
-
|
|
588
|
-
// HACK(#1639): Manually patches the ordering of the public call stack
|
|
589
|
-
// TODO(#757): Enforce proper ordering of enqueued public calls
|
|
590
|
-
await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);
|
|
591
|
-
|
|
592
|
-
return new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions, [extendedContractData]);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Adds contract and function names to a simulation error.
|
|
597
|
-
* @param err - The error to enrich.
|
|
598
|
-
*/
|
|
599
|
-
async #enrichSimulationError(err: SimulationError) {
|
|
600
|
-
// Maps contract addresses to the set of functions selectors that were in error.
|
|
601
|
-
// Using strings because map and set don't use .equals()
|
|
602
|
-
const mentionedFunctions: Map<string, Set<string>> = new Map();
|
|
603
|
-
|
|
604
|
-
err.getCallStack().forEach(({ contractAddress, functionSelector }) => {
|
|
605
|
-
if (!mentionedFunctions.has(contractAddress.toString())) {
|
|
606
|
-
mentionedFunctions.set(contractAddress.toString(), new Set());
|
|
607
|
-
}
|
|
608
|
-
mentionedFunctions.get(contractAddress.toString())!.add(functionSelector.toString());
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
await Promise.all(
|
|
612
|
-
[...mentionedFunctions.entries()].map(async ([contractAddress, selectors]) => {
|
|
613
|
-
const parsedContractAddress = AztecAddress.fromString(contractAddress);
|
|
614
|
-
const contract = await this.db.getContract(parsedContractAddress);
|
|
615
|
-
if (contract) {
|
|
616
|
-
err.enrichWithContractName(parsedContractAddress, contract.name);
|
|
617
|
-
selectors.forEach(selector => {
|
|
618
|
-
const functionArtifact = contract.functions.find(f => f.selector.toString() === selector);
|
|
619
|
-
if (functionArtifact) {
|
|
620
|
-
err.enrichWithFunctionName(parsedContractAddress, functionArtifact.selector, functionArtifact.name);
|
|
621
|
-
}
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
}),
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// HACK(#1639): this is a hack to fix ordering of public calls enqueued in the call stack. Since the private kernel
|
|
629
|
-
// cannot keep track of side effects that happen after or before a nested call, we override the public call stack
|
|
630
|
-
// it emits with whatever we got from the simulator collected enqueued calls. As a sanity check, we at least verify
|
|
631
|
-
// that the elements are the same, so we are only tweaking their ordering.
|
|
632
|
-
// See yarn-project/end-to-end/src/e2e_ordering.test.ts
|
|
633
|
-
// See https://github.com/AztecProtocol/aztec-packages/issues/1615
|
|
634
|
-
// TODO(#757): Enforce proper ordering of enqueued public calls
|
|
635
|
-
private async patchPublicCallStackOrdering(
|
|
636
|
-
publicInputs: KernelCircuitPublicInputsFinal,
|
|
637
|
-
enqueuedPublicCalls: PublicCallRequest[],
|
|
638
|
-
) {
|
|
639
|
-
const enqueuedPublicCallStackItems = await Promise.all(enqueuedPublicCalls.map(c => c.toCallRequest()));
|
|
640
|
-
const { publicCallStack } = publicInputs.end;
|
|
641
|
-
|
|
642
|
-
// Validate all items in enqueued public calls are in the kernel emitted stack
|
|
643
|
-
const areEqual = enqueuedPublicCallStackItems.reduce(
|
|
644
|
-
(accum, enqueued) => accum && !!publicCallStack.find(item => item.equals(enqueued)),
|
|
645
|
-
true,
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
if (!areEqual) {
|
|
649
|
-
throw new Error(
|
|
650
|
-
`Enqueued public function calls and public call stack do not match.\nEnqueued calls: ${enqueuedPublicCallStackItems
|
|
651
|
-
.map(h => h.hash.toString())
|
|
652
|
-
.join(', ')}\nPublic call stack: ${publicCallStack.map(i => i.toString()).join(', ')}`,
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
// Override kernel output
|
|
657
|
-
publicInputs.end.publicCallStack = padArrayEnd(
|
|
658
|
-
enqueuedPublicCallStackItems,
|
|
659
|
-
CallRequest.empty(),
|
|
660
|
-
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
public async isGlobalStateSynchronized() {
|
|
665
|
-
return await this.synchronizer.isGlobalStateSynchronized();
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
public async isAccountStateSynchronized(account: AztecAddress) {
|
|
669
|
-
return await this.synchronizer.isAccountStateSynchronized(account);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
public getSyncStatus() {
|
|
673
|
-
return Promise.resolve(this.synchronizer.getSyncStatus());
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
public getKeyStore() {
|
|
677
|
-
return this.keyStore;
|
|
678
|
-
}
|
|
679
|
-
}
|