@aztec/pxe 0.0.1-commit.a5db02d → 0.0.1-commit.aa0c64f
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/check_oracle_version.js +3 -8
- package/dest/bin/index.d.ts +2 -2
- package/dest/bin/index.d.ts.map +1 -1
- package/dest/bin/index.js +1 -1
- package/dest/bin/oracle_version_helpers.d.ts +8 -25
- package/dest/bin/oracle_version_helpers.d.ts.map +1 -1
- package/dest/bin/oracle_version_helpers.js +11 -219
- package/dest/config/package_info.js +1 -1
- package/dest/contract/helpers.d.ts +1 -1
- package/dest/contract/helpers.d.ts.map +1 -1
- package/dest/contract/helpers.js +3 -3
- package/dest/contract/skip_sync_contracts.d.ts +11 -0
- package/dest/contract/skip_sync_contracts.d.ts.map +1 -0
- package/dest/contract/skip_sync_contracts.js +18 -0
- package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
- package/dest/contract_function_simulator/contract_function_simulator.js +5 -3
- package/dest/contract_function_simulator/index.d.ts +3 -3
- package/dest/contract_function_simulator/index.d.ts.map +1 -1
- package/dest/contract_function_simulator/index.js +1 -2
- package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts +7 -9
- package/dest/contract_function_simulator/noir-structs/resolved_tx.d.ts.map +1 -1
- package/dest/contract_function_simulator/noir-structs/resolved_tx.js +3 -44
- package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts +5 -4
- package/dest/contract_function_simulator/noir-structs/tx_effect_data.d.ts.map +1 -1
- package/dest/contract_function_simulator/noir-structs/tx_effect_data.js +2 -2
- package/dest/contract_function_simulator/oracle/oracle_registry.d.ts +3 -3
- package/dest/contract_function_simulator/oracle/oracle_registry.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle_registry.js +6 -6
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts +87 -35
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/oracle_type_mappings.js +286 -286
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/private_execution_oracle.js +5 -6
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +2 -2
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
- package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +2 -1
- package/dest/entrypoints/client/store.d.ts +1 -1
- package/dest/entrypoints/client/store.d.ts.map +1 -1
- package/dest/entrypoints/client/store.js +16 -2
- package/dest/logs/log_service.d.ts +1 -1
- package/dest/logs/log_service.d.ts.map +1 -1
- package/dest/logs/log_service.js +7 -2
- package/dest/messages/tx_resolver_service.d.ts +2 -2
- package/dest/messages/tx_resolver_service.d.ts.map +1 -1
- package/dest/messages/tx_resolver_service.js +7 -2
- package/dest/oracle_version.d.ts +1 -1
- package/dest/oracle_version.js +2 -2
- package/dest/private_kernel/batch_planner.d.ts +1 -1
- package/dest/private_kernel/batch_planner.d.ts.map +1 -1
- package/dest/private_kernel/batch_planner.js +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
- package/dest/private_kernel/private_kernel_execution_prover.js +45 -5
- package/dest/storage/fact_store/fact_store.d.ts +1 -1
- package/dest/storage/fact_store/fact_store.js +1 -1
- package/dest/storage/fact_store/index.d.ts +2 -2
- package/dest/storage/fact_store/index.d.ts.map +1 -1
- package/dest/storage/fact_store/index.js +1 -1
- package/dest/storage/fact_store/origin_state.d.ts +3 -1
- package/dest/storage/fact_store/origin_state.d.ts.map +1 -1
- package/dest/storage/fact_store/origin_state.js +7 -0
- package/dest/tagging/constants.d.ts +2 -1
- package/dest/tagging/constants.d.ts.map +1 -1
- package/dest/tagging/constants.js +9 -0
- package/dest/tagging/index.d.ts +2 -2
- package/dest/tagging/index.d.ts.map +1 -1
- package/dest/tagging/index.js +1 -1
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts +9 -4
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.d.ts.map +1 -1
- package/dest/tagging/recipient_sync/sync_tagged_private_logs.js +60 -25
- package/dest/tagging/testing/tag_query_test_utils.d.ts +6 -0
- package/dest/tagging/testing/tag_query_test_utils.d.ts.map +1 -0
- package/dest/tagging/testing/tag_query_test_utils.js +10 -0
- package/package.json +17 -17
- package/src/bin/check_oracle_version.ts +3 -10
- package/src/bin/index.ts +1 -5
- package/src/bin/oracle_version_helpers.ts +11 -268
- package/src/config/package_info.ts +1 -1
- package/src/contract/helpers.ts +3 -3
- package/src/contract/skip_sync_contracts.ts +23 -0
- package/src/contract_function_simulator/contract_function_simulator.ts +5 -3
- package/src/contract_function_simulator/index.ts +23 -3
- package/src/contract_function_simulator/noir-structs/resolved_tx.ts +11 -45
- package/src/contract_function_simulator/noir-structs/tx_effect_data.ts +4 -3
- package/src/contract_function_simulator/oracle/oracle_registry.ts +7 -8
- package/src/contract_function_simulator/oracle/oracle_type_mappings.ts +285 -197
- package/src/contract_function_simulator/oracle/private_execution_oracle.ts +12 -7
- package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +8 -3
- package/src/entrypoints/client/store.ts +23 -7
- package/src/logs/log_service.ts +7 -2
- package/src/messages/tx_resolver_service.ts +8 -8
- package/src/oracle_version.ts +2 -2
- package/src/private_kernel/batch_planner.ts +0 -1
- package/src/private_kernel/private_kernel_execution_prover.ts +90 -4
- package/src/storage/fact_store/fact_store.ts +1 -1
- package/src/storage/fact_store/index.ts +1 -0
- package/src/storage/fact_store/origin_state.ts +9 -0
- package/src/tagging/constants.ts +10 -0
- package/src/tagging/index.ts +1 -1
- package/src/tagging/recipient_sync/sync_tagged_private_logs.ts +103 -46
- package/src/tagging/testing/tag_query_test_utils.ts +11 -0
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
type TaggingSecretStrategy,
|
|
37
37
|
} from '../../hooks/resolve_tagging_secret_strategy.js';
|
|
38
38
|
import { NoteService } from '../../notes/note_service.js';
|
|
39
|
+
import { assertAllowedScope } from '../../storage/allowed_scopes.js';
|
|
39
40
|
import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js';
|
|
40
41
|
import { syncSenderTaggingIndexes } from '../../tagging/index.js';
|
|
41
42
|
import type { ExecutionNoteCache } from '../execution_note_cache.js';
|
|
@@ -321,7 +322,17 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
321
322
|
* @returns The app tagging secret, or `None` if the recipient is invalid.
|
|
322
323
|
*/
|
|
323
324
|
public async getAppTaggingSecret(sender: AztecAddress, recipient: AztecAddress): Promise<Option<Fr>> {
|
|
324
|
-
|
|
325
|
+
assertAllowedScope(sender, this.scopes);
|
|
326
|
+
|
|
327
|
+
const senderCompleteAddress = await this.getCompleteAddressOrFail(sender);
|
|
328
|
+
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
329
|
+
const extendedSecret = await AppTaggingSecret.computeViaEcdh(
|
|
330
|
+
senderCompleteAddress,
|
|
331
|
+
senderIvsk,
|
|
332
|
+
recipient,
|
|
333
|
+
this.contractAddress,
|
|
334
|
+
recipient,
|
|
335
|
+
);
|
|
325
336
|
|
|
326
337
|
if (!extendedSecret) {
|
|
327
338
|
this.logger.warn(`Computing a tagging secret for invalid recipient ${recipient} - returning no secret`, {
|
|
@@ -354,12 +365,6 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
|
|
|
354
365
|
return index;
|
|
355
366
|
}
|
|
356
367
|
|
|
357
|
-
async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
|
|
358
|
-
const senderCompleteAddress = await this.getCompleteAddressOrFail(sender);
|
|
359
|
-
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
|
|
360
|
-
return AppTaggingSecret.computeViaEcdh(senderCompleteAddress, senderIvsk, recipient, contractAddress, recipient);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
368
|
async #getIndexToUseForSecret(secret: AppTaggingSecret): Promise<number> {
|
|
364
369
|
// If we have the tagging index in the cache, we use it. If not we obtain it from the execution data provider.
|
|
365
370
|
const lastUsedIndexInTx = this.taggingIndexCache.getLastUsedIndex(secret);
|
|
@@ -859,13 +859,17 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
859
859
|
// TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
|
|
860
860
|
public async decryptAes128(
|
|
861
861
|
ciphertext: BoundedVec<number>,
|
|
862
|
-
iv:
|
|
863
|
-
symKey:
|
|
862
|
+
iv: number[],
|
|
863
|
+
symKey: number[],
|
|
864
864
|
): Promise<Option<BoundedVec<number>>> {
|
|
865
865
|
const capacity = ciphertext.maxLength;
|
|
866
866
|
try {
|
|
867
867
|
const aes128 = new Aes128();
|
|
868
|
-
const plaintext = await aes128.decryptBufferCBC(
|
|
868
|
+
const plaintext = await aes128.decryptBufferCBC(
|
|
869
|
+
Buffer.from(ciphertext.data),
|
|
870
|
+
Buffer.from(iv),
|
|
871
|
+
Buffer.from(symKey),
|
|
872
|
+
);
|
|
869
873
|
return Option.some(BoundedVec.from<number>({ data: [...plaintext], maxLength: capacity }));
|
|
870
874
|
} catch {
|
|
871
875
|
return Option.none({ maxLength: capacity });
|
|
@@ -1143,6 +1147,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
|
|
|
1143
1147
|
#toTxEffectData(txEffect: TxEffect): TxEffectData {
|
|
1144
1148
|
return {
|
|
1145
1149
|
...txEffect,
|
|
1150
|
+
revertCode: txEffect.revertCode.getCode(),
|
|
1146
1151
|
publicLogs: FlatPublicLogs.fromLogs(txEffect.publicLogs),
|
|
1147
1152
|
contractClassLogs: txEffect.contractClassLogs.map(log => ({
|
|
1148
1153
|
contractAddress: log.contractAddress,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
2
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AztecSQLiteOPFSStore,
|
|
5
|
+
SqliteCorruptionError,
|
|
6
|
+
deleteStore,
|
|
7
|
+
storePoolDirectory,
|
|
8
|
+
} from '@aztec/kv-store/sqlite-opfs';
|
|
4
9
|
|
|
5
10
|
import { assertStoreIdentity, effectiveStoreName } from '../../storage/store_identity.js';
|
|
6
11
|
|
|
@@ -21,12 +26,23 @@ export async function openBrowserStore(
|
|
|
21
26
|
storeName,
|
|
22
27
|
dataStoreMapSizeKb: config.dataStoreMapSizeKb,
|
|
23
28
|
});
|
|
24
|
-
const
|
|
25
|
-
createLogger('kv-store:sqlite-opfs'),
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const openStore = () =>
|
|
30
|
+
AztecSQLiteOPFSStore.open(createLogger('kv-store:sqlite-opfs'), storeName, false, storePoolDirectory(storeName));
|
|
31
|
+
|
|
32
|
+
let store: AztecSQLiteOPFSStore;
|
|
33
|
+
try {
|
|
34
|
+
store = await openStore();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (!(err instanceof SqliteCorruptionError)) {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
// A corrupt image is unrecoverable — no retry against the same bytes helps. Wipe the store's OPFS directory
|
|
40
|
+
// (safe: the failed open left no SAH-pool lock behind) and reopen a fresh, empty one, so the browser self-heals
|
|
41
|
+
// into a normal first-run rather than dead-ending on "database disk image is malformed" every load.
|
|
42
|
+
log.warn(`${storeName} store is corrupt (${err.message}); wiping and reopening fresh`);
|
|
43
|
+
await deleteStore(storeName);
|
|
44
|
+
store = await openStore();
|
|
45
|
+
}
|
|
30
46
|
try {
|
|
31
47
|
await assertStoreIdentity(store, storeName, identity);
|
|
32
48
|
} catch (err) {
|
package/src/logs/log_service.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
} from '../contract_function_simulator/noir-structs/log_retrieval_request.js';
|
|
23
23
|
import type { LogRetrievalResponse } from '../contract_function_simulator/noir-structs/log_retrieval_response.js';
|
|
24
24
|
import type { PendingTaggedLog } from '../contract_function_simulator/noir-structs/pending_tagged_log.js';
|
|
25
|
-
import { ResolvedTx } from '../contract_function_simulator/noir-structs/resolved_tx.js';
|
|
26
25
|
import { AddressStore } from '../storage/address_store/address_store.js';
|
|
27
26
|
import { assertAllowedScope } from '../storage/allowed_scopes.js';
|
|
28
27
|
import type { RecipientTaggingStore } from '../storage/tagging_store/recipient_tagging_store.js';
|
|
@@ -225,7 +224,13 @@ export class LogService {
|
|
|
225
224
|
}
|
|
226
225
|
return {
|
|
227
226
|
log: log.logData,
|
|
228
|
-
context:
|
|
227
|
+
context: {
|
|
228
|
+
txHash: log.txHash,
|
|
229
|
+
uniqueNoteHashesInTx: noteHashes,
|
|
230
|
+
firstNullifierInTx: nullifiers[0],
|
|
231
|
+
blockNumber: log.blockNumber,
|
|
232
|
+
blockHash: log.blockHash.toFr(),
|
|
233
|
+
},
|
|
229
234
|
};
|
|
230
235
|
});
|
|
231
236
|
}
|
|
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
3
3
|
import type { AztecNode } from '@aztec/stdlib/interfaces/server';
|
|
4
4
|
import { type IndexedTxEffect, TxHash } from '@aztec/stdlib/tx';
|
|
5
5
|
|
|
6
|
-
import { ResolvedTx } from '../contract_function_simulator/noir-structs/resolved_tx.js';
|
|
6
|
+
import type { ResolvedTx } from '../contract_function_simulator/noir-structs/resolved_tx.js';
|
|
7
7
|
|
|
8
8
|
/** Resolves transaction hashes into their on-chain context (note hashes, first nullifier, and mined block). */
|
|
9
9
|
export class TxResolverService {
|
|
@@ -57,13 +57,13 @@ export class TxResolverService {
|
|
|
57
57
|
throw new Error(`Tx effect for ${txHash} has no nullifiers`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
return
|
|
61
|
-
data.txHash,
|
|
62
|
-
data.noteHashes,
|
|
63
|
-
data.nullifiers[0],
|
|
64
|
-
txEffect.l2BlockNumber,
|
|
65
|
-
txEffect.l2BlockHash.toFr(),
|
|
66
|
-
|
|
60
|
+
return {
|
|
61
|
+
txHash: data.txHash,
|
|
62
|
+
uniqueNoteHashesInTx: data.noteHashes,
|
|
63
|
+
firstNullifierInTx: data.nullifiers[0],
|
|
64
|
+
blockNumber: txEffect.l2BlockNumber,
|
|
65
|
+
blockHash: txEffect.l2BlockHash.toFr(),
|
|
66
|
+
};
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
}
|
package/src/oracle_version.ts
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
export const ORACLE_VERSION_MAJOR = 30;
|
|
14
14
|
export const ORACLE_VERSION_MINOR = 8;
|
|
15
15
|
|
|
16
|
-
/// This hash is computed from
|
|
16
|
+
/// This hash is computed from `ORACLE_REGISTRY` (each oracle's name, ordered parameter names and
|
|
17
17
|
/// types, and return type) and is used to detect when the oracle interface changes. When it does, you need to either:
|
|
18
18
|
/// - increment `ORACLE_VERSION_MAJOR` and reset `ORACLE_VERSION_MINOR` to zero if the change is breaking, or
|
|
19
19
|
/// - increment only `ORACLE_VERSION_MINOR` if the change is additive (a new oracle was added).
|
|
20
20
|
///
|
|
21
21
|
/// These constants must be kept in sync between this file and `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
|
|
22
|
-
export const ORACLE_INTERFACE_HASH = '
|
|
22
|
+
export const ORACLE_INTERFACE_HASH = 'fec4d3a95fc57a62a20bfeb659bb3bfd91b31cd93e07f8cf97b368f9f55cc67b';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { CircuitKind } from '@aztec/bb.js';
|
|
1
2
|
import { MAX_APPS_PER_KERNEL } from '@aztec/constants';
|
|
2
3
|
import { uniqueBy } from '@aztec/foundation/collection';
|
|
3
|
-
import {
|
|
4
|
+
import { vkAsFields } from '@aztec/foundation/crypto/keys';
|
|
4
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
6
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
6
7
|
import { pushTestData } from '@aztec/foundation/testing';
|
|
@@ -20,9 +21,13 @@ import {
|
|
|
20
21
|
type PrivateKernelExecutionProofOutput,
|
|
21
22
|
PrivateKernelInit2CircuitPrivateInputs,
|
|
22
23
|
PrivateKernelInit3CircuitPrivateInputs,
|
|
24
|
+
PrivateKernelInit4CircuitPrivateInputs,
|
|
25
|
+
PrivateKernelInit5CircuitPrivateInputs,
|
|
23
26
|
PrivateKernelInitCircuitPrivateInputs,
|
|
24
27
|
PrivateKernelInner2CircuitPrivateInputs,
|
|
25
28
|
PrivateKernelInner3CircuitPrivateInputs,
|
|
29
|
+
PrivateKernelInner4CircuitPrivateInputs,
|
|
30
|
+
PrivateKernelInner5CircuitPrivateInputs,
|
|
26
31
|
PrivateKernelInnerCircuitPrivateInputs,
|
|
27
32
|
PrivateKernelResetTailCircuitPrivateInputs,
|
|
28
33
|
type PrivateKernelSimulateOutput,
|
|
@@ -145,6 +150,7 @@ export class PrivateKernelExecutionProver {
|
|
|
145
150
|
bytecode: output.bytecode,
|
|
146
151
|
witness: output.outputWitness,
|
|
147
152
|
vk: output.verificationKey.keyAsBytes,
|
|
153
|
+
kind: CircuitKind.Kernel,
|
|
148
154
|
timings: {
|
|
149
155
|
witgen: witgenTimer.ms(),
|
|
150
156
|
},
|
|
@@ -230,6 +236,7 @@ export class PrivateKernelExecutionProver {
|
|
|
230
236
|
bytecode: tailOutput.bytecode,
|
|
231
237
|
witness: tailOutput.outputWitness,
|
|
232
238
|
vk: tailOutput.verificationKey.keyAsBytes,
|
|
239
|
+
kind: CircuitKind.Kernel,
|
|
233
240
|
timings: {
|
|
234
241
|
witgen: witgenTimer.ms(),
|
|
235
242
|
},
|
|
@@ -261,6 +268,7 @@ export class PrivateKernelExecutionProver {
|
|
|
261
268
|
bytecode: hidingOutput.bytecode,
|
|
262
269
|
witness: hidingOutput.outputWitness,
|
|
263
270
|
vk: hidingOutput.verificationKey.keyAsBytes,
|
|
271
|
+
kind: CircuitKind.HidingKernel,
|
|
264
272
|
timings: {
|
|
265
273
|
witgen: witgenTimer.ms(),
|
|
266
274
|
},
|
|
@@ -270,7 +278,11 @@ export class PrivateKernelExecutionProver {
|
|
|
270
278
|
if (profileMode == 'gates' || profileMode == 'full') {
|
|
271
279
|
for (const entry of executionSteps) {
|
|
272
280
|
const gateCountTimer = new Timer();
|
|
273
|
-
const gateCount = await this.proofCreator.computeGateCountForCircuit(
|
|
281
|
+
const gateCount = await this.proofCreator.computeGateCountForCircuit(
|
|
282
|
+
entry.bytecode,
|
|
283
|
+
entry.functionName,
|
|
284
|
+
entry.kind,
|
|
285
|
+
);
|
|
274
286
|
entry.gateCount = gateCount;
|
|
275
287
|
entry.timings.gateCount = gateCountTimer.ms();
|
|
276
288
|
}
|
|
@@ -366,6 +378,7 @@ export class PrivateKernelExecutionProver {
|
|
|
366
378
|
bytecode: next.acir,
|
|
367
379
|
witness: next.partialWitness,
|
|
368
380
|
vk: next.vk,
|
|
381
|
+
kind: CircuitKind.App,
|
|
369
382
|
timings: {
|
|
370
383
|
witgen: next.profileResult?.timings.witgen ?? 0,
|
|
371
384
|
oracles: next.profileResult?.timings.oracles,
|
|
@@ -399,8 +412,8 @@ export class PrivateKernelExecutionProver {
|
|
|
399
412
|
) {
|
|
400
413
|
const { contractAddress, functionSelector } = publicInputs.callContext;
|
|
401
414
|
|
|
402
|
-
const
|
|
403
|
-
const vk = await VerificationKeyAsFields.fromKey(
|
|
415
|
+
const vkFields = await vkAsFields(vkAsBuffer, CircuitKind.App);
|
|
416
|
+
const vk = await VerificationKeyAsFields.fromKey(vkFields);
|
|
404
417
|
|
|
405
418
|
const { currentContractClassId, publicKeys, saltedInitializationHash } =
|
|
406
419
|
await this.oracle.getContractAddressPreimage(contractAddress);
|
|
@@ -514,6 +527,47 @@ export class PrivateKernelExecutionProver {
|
|
|
514
527
|
functionName = 'private_kernel_init_3';
|
|
515
528
|
break;
|
|
516
529
|
}
|
|
530
|
+
case 4: {
|
|
531
|
+
const proofInput = new PrivateKernelInit4CircuitPrivateInputs(
|
|
532
|
+
txRequest,
|
|
533
|
+
vkTreeRoot,
|
|
534
|
+
ProtocolContractsList,
|
|
535
|
+
apps[0],
|
|
536
|
+
apps[1],
|
|
537
|
+
apps[2],
|
|
538
|
+
apps[3],
|
|
539
|
+
isPrivateOnlyTx,
|
|
540
|
+
minRevertibleSideEffectCounter,
|
|
541
|
+
);
|
|
542
|
+
this.log.debug(`Calling private kernel init_4 with isPrivateOnly ${isPrivateOnlyTx}`);
|
|
543
|
+
pushTestData('private-kernel-inputs-init-4', proofInput);
|
|
544
|
+
output = generateWitnesses
|
|
545
|
+
? await this.proofCreator.generateInit4Output(proofInput)
|
|
546
|
+
: await this.proofCreator.simulateInit4(proofInput);
|
|
547
|
+
functionName = 'private_kernel_init_4';
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
case 5: {
|
|
551
|
+
const proofInput = new PrivateKernelInit5CircuitPrivateInputs(
|
|
552
|
+
txRequest,
|
|
553
|
+
vkTreeRoot,
|
|
554
|
+
ProtocolContractsList,
|
|
555
|
+
apps[0],
|
|
556
|
+
apps[1],
|
|
557
|
+
apps[2],
|
|
558
|
+
apps[3],
|
|
559
|
+
apps[4],
|
|
560
|
+
isPrivateOnlyTx,
|
|
561
|
+
minRevertibleSideEffectCounter,
|
|
562
|
+
);
|
|
563
|
+
this.log.debug(`Calling private kernel init_5 with isPrivateOnly ${isPrivateOnlyTx}`);
|
|
564
|
+
pushTestData('private-kernel-inputs-init-5', proofInput);
|
|
565
|
+
output = generateWitnesses
|
|
566
|
+
? await this.proofCreator.generateInit5Output(proofInput)
|
|
567
|
+
: await this.proofCreator.simulateInit5(proofInput);
|
|
568
|
+
functionName = 'private_kernel_init_5';
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
517
571
|
default:
|
|
518
572
|
throw new Error(`Unsupported init kernel batch size: ${apps.length}`);
|
|
519
573
|
}
|
|
@@ -548,6 +602,37 @@ export class PrivateKernelExecutionProver {
|
|
|
548
602
|
functionName = 'private_kernel_inner_3';
|
|
549
603
|
break;
|
|
550
604
|
}
|
|
605
|
+
case 4: {
|
|
606
|
+
const proofInput = new PrivateKernelInner4CircuitPrivateInputs(
|
|
607
|
+
previousKernelData,
|
|
608
|
+
apps[0],
|
|
609
|
+
apps[1],
|
|
610
|
+
apps[2],
|
|
611
|
+
apps[3],
|
|
612
|
+
);
|
|
613
|
+
pushTestData('private-kernel-inputs-inner-4', proofInput);
|
|
614
|
+
output = generateWitnesses
|
|
615
|
+
? await this.proofCreator.generateInner4Output(proofInput)
|
|
616
|
+
: await this.proofCreator.simulateInner4(proofInput);
|
|
617
|
+
functionName = 'private_kernel_inner_4';
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
case 5: {
|
|
621
|
+
const proofInput = new PrivateKernelInner5CircuitPrivateInputs(
|
|
622
|
+
previousKernelData,
|
|
623
|
+
apps[0],
|
|
624
|
+
apps[1],
|
|
625
|
+
apps[2],
|
|
626
|
+
apps[3],
|
|
627
|
+
apps[4],
|
|
628
|
+
);
|
|
629
|
+
pushTestData('private-kernel-inputs-inner-5', proofInput);
|
|
630
|
+
output = generateWitnesses
|
|
631
|
+
? await this.proofCreator.generateInner5Output(proofInput)
|
|
632
|
+
: await this.proofCreator.simulateInner5(proofInput);
|
|
633
|
+
functionName = 'private_kernel_inner_5';
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
551
636
|
default:
|
|
552
637
|
throw new Error(`Unsupported inner kernel batch size: ${apps.length}`);
|
|
553
638
|
}
|
|
@@ -558,6 +643,7 @@ export class PrivateKernelExecutionProver {
|
|
|
558
643
|
bytecode: output.bytecode,
|
|
559
644
|
witness: output.outputWitness,
|
|
560
645
|
vk: output.verificationKey.keyAsBytes,
|
|
646
|
+
kind: CircuitKind.Kernel,
|
|
561
647
|
timings: {
|
|
562
648
|
witgen: witgenTimer.ms(),
|
|
563
649
|
},
|
|
@@ -82,7 +82,7 @@ export class FactStore implements StagedStore {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Records a fact in a collection
|
|
85
|
+
* Records a fact in a collection.
|
|
86
86
|
*
|
|
87
87
|
* The collection is created implicitly on the first fact recorded for its key: recording into an existing collection
|
|
88
88
|
* just adds to it.
|
|
@@ -21,6 +21,15 @@ export enum OriginBlockState {
|
|
|
21
21
|
Finalized = 3,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** Parses a numeric origin-block-state discriminant, rejecting unknown values. */
|
|
25
|
+
export function originBlockStateFromNumber(value: number): OriginBlockState {
|
|
26
|
+
if (!(value in OriginBlockState)) {
|
|
27
|
+
const validNames = Object.keys(OriginBlockState).filter(k => isNaN(Number(k)));
|
|
28
|
+
throw new Error(`Invalid OriginBlockState value ${value}, expected one of ${validNames.join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
return value as OriginBlockState;
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
/** The two chain-tip block numbers needed to classify an origin block (`finalized <= proven` always holds). */
|
|
25
34
|
export type TipBlockNumbers = { provenBlockNumber: number; finalizedBlockNumber: number };
|
|
26
35
|
|
package/src/tagging/constants.ts
CHANGED
|
@@ -18,3 +18,13 @@ import { MAX_PRIVATE_LOGS_PER_TX } from '@aztec/constants';
|
|
|
18
18
|
// reserved at log emission time, before squashing is decided, and the kernel's reset/squash loop is not bounded by
|
|
19
19
|
// MAX_PRIVATE_LOGS_PER_TX. No fixed window value closes that gap.
|
|
20
20
|
export const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN = MAX_PRIVATE_LOGS_PER_TX + 20;
|
|
21
|
+
|
|
22
|
+
// The number of tags probed per constrained secret in the first round.
|
|
23
|
+
//
|
|
24
|
+
// The probe doubles each round (2, 4, 8, ..., capped at UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN) while every probed
|
|
25
|
+
// index is a hit, stopping at the first missing tag. Constrained delivery is gapless, so a single missing tag proves
|
|
26
|
+
// the stream has ended: at steady state this turns a full WINDOW_LEN probe into two tags. A secret K logs behind
|
|
27
|
+
// catches up in ~log2(K) round-trips while the probe is still doubling (2, 4, 8, 16, 32), but once it saturates the cap
|
|
28
|
+
// and advances WINDOW_LEN tags per round, deeper catch-up is linear at ~K/WINDOW_LEN rounds. Either way it beats both
|
|
29
|
+
// the full window every round and one round per log.
|
|
30
|
+
export const INITIAL_CONSTRAINED_PROBE_LEN = 2;
|
package/src/tagging/index.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { syncTaggedPrivateLogs } from './recipient_sync/sync_tagged_private_logs.js';
|
|
13
13
|
export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
|
|
14
14
|
export { persistSenderTaggingIndexRangesForTx } from './persist_sender_tagging_index_ranges.js';
|
|
15
|
-
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
|
|
15
|
+
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, INITIAL_CONSTRAINED_PROBE_LEN } from './constants.js';
|
|
16
16
|
export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
|
|
17
17
|
|
|
18
18
|
// Re-export tagging-related types from stdlib
|
|
@@ -7,7 +7,7 @@ import { AppTaggingSecretKind, SiloedTag } from '@aztec/stdlib/logs';
|
|
|
7
7
|
import type { BlockHeader } from '@aztec/stdlib/tx';
|
|
8
8
|
|
|
9
9
|
import type { RecipientTaggingStore } from '../../storage/tagging_store/recipient_tagging_store.js';
|
|
10
|
-
import { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from '../constants.js';
|
|
10
|
+
import { INITIAL_CONSTRAINED_PROBE_LEN, UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from '../constants.js';
|
|
11
11
|
import { getAllPrivateLogsByTags } from '../get_all_logs_by_tags.js';
|
|
12
12
|
import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
13
13
|
|
|
@@ -62,9 +62,14 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
62
62
|
* - The lower bound is `highestFinalizedIndex + 1`. The `highestAgedIndex` mechanism is unnecessary because
|
|
63
63
|
* the nullifier chain prevents out-of-order index usage: no device can fill in a lower index after a higher
|
|
64
64
|
* one is already on-chain.
|
|
65
|
-
* -
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* - Because the sequence is gapless, the scan stops at the first missing index: that gap proves the sequence has
|
|
66
|
+
* ended and no further logs exist. We exploit this with a doubling first-miss probe (see
|
|
67
|
+
* `INITIAL_CONSTRAINED_PROBE_LEN` in ../constants.ts), so a
|
|
68
|
+
* steady-state sync with no new logs costs two tags instead of the whole window. The probe length is in-memory
|
|
69
|
+
* state scoped to one sync call, so every sync restarts at the initial probe.
|
|
70
|
+
* - The upper bound is the same as unconstrained: `highestFinalizedIndex + WINDOW_LEN`. Advancing the probe is
|
|
71
|
+
* decoupled from persisting the finalized index, so unfinalized logs at the top of the run are still fetched
|
|
72
|
+
* while only the finalized prefix is persisted.
|
|
68
73
|
*
|
|
69
74
|
* # Batching across secrets
|
|
70
75
|
*
|
|
@@ -109,19 +114,23 @@ export async function syncTaggedPrivateLogs(
|
|
|
109
114
|
|
|
110
115
|
// Persist new indexes. If the finalized index moved forward, the window advances
|
|
111
116
|
// and we need another round for this secret.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
return pendingSecret.kind === AppTaggingSecretKind.CONSTRAINED
|
|
118
|
+
? await processConstrainedResults(
|
|
119
|
+
pendingSecret,
|
|
120
|
+
logsFoundWithSecret,
|
|
121
|
+
taggingStore,
|
|
122
|
+
currentTimestamp,
|
|
123
|
+
finalizedBlockNumber,
|
|
124
|
+
jobId,
|
|
125
|
+
)
|
|
126
|
+
: await processUnconstrainedResults(
|
|
127
|
+
pendingSecret,
|
|
128
|
+
logsFoundWithSecret,
|
|
129
|
+
taggingStore,
|
|
130
|
+
currentTimestamp,
|
|
131
|
+
finalizedBlockNumber,
|
|
132
|
+
jobId,
|
|
133
|
+
);
|
|
125
134
|
}),
|
|
126
135
|
);
|
|
127
136
|
|
|
@@ -138,18 +147,29 @@ function getIndexRangesForSecrets(
|
|
|
138
147
|
jobId: string,
|
|
139
148
|
): Promise<PendingSecret[]> {
|
|
140
149
|
return Promise.all(
|
|
141
|
-
secrets.map(async secret => {
|
|
150
|
+
secrets.map(async (secret): Promise<PendingSecret> => {
|
|
142
151
|
const currentHighestFinalizedIndex = await taggingStore.getHighestFinalizedIndex(secret, jobId);
|
|
152
|
+
const boundEnd = (currentHighestFinalizedIndex ?? 0) + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1;
|
|
153
|
+
|
|
154
|
+
if (secret.kind === AppTaggingSecretKind.CONSTRAINED) {
|
|
155
|
+
// Constrained streams are gapless and resume at the finalized index, so probe a small initial window and stop
|
|
156
|
+
// at the first missing tag instead of always fetching the full window. The probe grows each round up to the
|
|
157
|
+
// window cap.
|
|
158
|
+
const start = currentHighestFinalizedIndex === undefined ? 0 : currentHighestFinalizedIndex + 1;
|
|
159
|
+
return {
|
|
160
|
+
kind: AppTaggingSecretKind.CONSTRAINED,
|
|
161
|
+
secret,
|
|
162
|
+
start,
|
|
163
|
+
end: Math.min(boundEnd, start + INITIAL_CONSTRAINED_PROBE_LEN),
|
|
164
|
+
boundEnd,
|
|
165
|
+
probeLen: INITIAL_CONSTRAINED_PROBE_LEN,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
143
168
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const start = highestIndexBeforeStart === undefined ? 0 : highestIndexBeforeStart + 1;
|
|
149
|
-
|
|
150
|
-
const end = (currentHighestFinalizedIndex ?? 0) + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1;
|
|
151
|
-
|
|
152
|
-
return { secret, start, end };
|
|
169
|
+
// Unconstrained secrets can have gaps, so they scan the whole window starting past the highest aged index.
|
|
170
|
+
const highestAgedIndex = await taggingStore.getHighestAgedIndex(secret, jobId);
|
|
171
|
+
const start = highestAgedIndex === undefined ? 0 : highestAgedIndex + 1;
|
|
172
|
+
return { kind: AppTaggingSecretKind.UNCONSTRAINED, secret, start, end: boundEnd };
|
|
153
173
|
}),
|
|
154
174
|
);
|
|
155
175
|
}
|
|
@@ -203,41 +223,58 @@ async function fetchLogsForSecrets(
|
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
/**
|
|
206
|
-
* Processes fetched logs for a constrained secret
|
|
207
|
-
*
|
|
226
|
+
* Processes fetched logs for a constrained secret: persists the finalized index and advances the probe. See the
|
|
227
|
+
* "# Constrained secrets" section above for why the scan stops at the first missing index. Only `highestFinalizedIndex`
|
|
208
228
|
* is tracked (no aged index).
|
|
209
229
|
*/
|
|
210
230
|
async function processConstrainedResults(
|
|
211
|
-
pending:
|
|
231
|
+
pending: PendingConstrained,
|
|
212
232
|
logsWithIndexes: LogWithIndex[],
|
|
213
233
|
taggingStore: RecipientTaggingStore,
|
|
214
234
|
currentTimestamp: bigint,
|
|
215
235
|
finalizedBlockNumber: BlockNumber,
|
|
216
236
|
jobId: string,
|
|
217
237
|
): Promise<PendingSecret | undefined> {
|
|
218
|
-
// Find where the contiguous run of indexes ends
|
|
219
|
-
// first gap, so all logs in the batch are within this prefix.
|
|
238
|
+
// Find where the contiguous run of indexes ends; all logs in the batch fall within this prefix.
|
|
220
239
|
const indexesWithLogs = new Set(logsWithIndexes.map(l => l.taggingIndex));
|
|
221
240
|
let firstMissingIndex = pending.start;
|
|
222
241
|
while (firstMissingIndex < pending.end && indexesWithLogs.has(firstMissingIndex)) {
|
|
223
242
|
firstMissingIndex++;
|
|
224
243
|
}
|
|
225
244
|
|
|
226
|
-
//
|
|
227
|
-
// transaction, guaranteeing the log cannot disappear once included, so we persist the
|
|
228
|
-
//
|
|
245
|
+
// Persist the highest finalized index found. The nullifier for a constrained-delivery log is emitted in the same
|
|
246
|
+
// transaction, guaranteeing the log cannot disappear once included, so we persist even when a gap stops the scan.
|
|
247
|
+
// This lets the next sync round skip already-finalized indexes.
|
|
229
248
|
const { highestFinalizedIndex } = findHighestIndexes(logsWithIndexes, currentTimestamp, finalizedBlockNumber);
|
|
230
|
-
|
|
231
249
|
if (highestFinalizedIndex !== undefined) {
|
|
232
250
|
await taggingStore.updateHighestFinalizedIndex(pending.secret, highestFinalizedIndex, jobId);
|
|
251
|
+
}
|
|
233
252
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
253
|
+
// Advancing the probe is decoupled from persisting the finalized index: keep scanning as long as the probe was
|
|
254
|
+
// entirely hits (no gap) and there is room before the protocol bound, even if none of those hits is finalized yet.
|
|
255
|
+
// Gating this on finalization would drop logs in unfinalized blocks, which sit at the top of the contiguous run.
|
|
256
|
+
// The bound re-anchors to any finalized index found this round.
|
|
257
|
+
const probeFullyConsumed = firstMissingIndex >= pending.end;
|
|
258
|
+
const boundEnd =
|
|
259
|
+
highestFinalizedIndex !== undefined
|
|
260
|
+
? Math.max(pending.boundEnd, highestFinalizedIndex + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1)
|
|
261
|
+
: pending.boundEnd;
|
|
262
|
+
|
|
263
|
+
// Double the probe each round, capped at the window (see INITIAL_CONSTRAINED_PROBE_LEN in ../constants.ts).
|
|
264
|
+
// The queried range is already bounded by boundEnd (a probe can never reach
|
|
265
|
+
// more than WINDOW_LEN past the finalized frontier); this cap just keeps the stored length from growing unbounded
|
|
266
|
+
// once the probe saturates the window.
|
|
267
|
+
const nextProbeLen = Math.min(pending.probeLen * 2, UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN);
|
|
268
|
+
|
|
269
|
+
if (probeFullyConsumed && pending.end < boundEnd) {
|
|
270
|
+
return {
|
|
271
|
+
kind: AppTaggingSecretKind.CONSTRAINED,
|
|
272
|
+
secret: pending.secret,
|
|
273
|
+
start: pending.end,
|
|
274
|
+
end: Math.min(boundEnd, pending.end + nextProbeLen),
|
|
275
|
+
boundEnd,
|
|
276
|
+
probeLen: nextProbeLen,
|
|
277
|
+
};
|
|
241
278
|
}
|
|
242
279
|
|
|
243
280
|
return undefined;
|
|
@@ -248,7 +285,7 @@ async function processConstrainedResults(
|
|
|
248
285
|
* if the window needs to advance, or undefined if this secret is done.
|
|
249
286
|
*/
|
|
250
287
|
async function processUnconstrainedResults(
|
|
251
|
-
pending:
|
|
288
|
+
pending: PendingUnconstrained,
|
|
252
289
|
logsWithIndexes: LogWithIndex[],
|
|
253
290
|
taggingStore: RecipientTaggingStore,
|
|
254
291
|
currentTimestamp: bigint,
|
|
@@ -282,19 +319,39 @@ async function processUnconstrainedResults(
|
|
|
282
319
|
|
|
283
320
|
// For the next iteration we want to look only at indexes for which we have not yet fetched logs while
|
|
284
321
|
// ensuring that we do not look further than WINDOW_LEN ahead of the highest finalized index.
|
|
322
|
+
const end = highestFinalizedIndex + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1;
|
|
285
323
|
return {
|
|
324
|
+
kind: AppTaggingSecretKind.UNCONSTRAINED,
|
|
286
325
|
secret: pending.secret,
|
|
287
326
|
start: pending.end,
|
|
288
|
-
end
|
|
327
|
+
end,
|
|
289
328
|
};
|
|
290
329
|
}
|
|
291
330
|
|
|
292
|
-
|
|
331
|
+
/** Working scan state common to both secret kinds: the half-open [start, end) tag-index range to query this round. */
|
|
332
|
+
type PendingSecretBase = {
|
|
293
333
|
secret: AppTaggingSecret;
|
|
294
334
|
start: number;
|
|
295
335
|
end: number;
|
|
296
336
|
};
|
|
297
337
|
|
|
338
|
+
/** Constrained scan state, carrying the doubling-probe bookkeeping that only the gapless constrained scan uses. */
|
|
339
|
+
type PendingConstrained = PendingSecretBase & {
|
|
340
|
+
kind: typeof AppTaggingSecretKind.CONSTRAINED;
|
|
341
|
+
// Exclusive upper bound on the indexes this secret may probe this sync (highest finalized index + WINDOW_LEN + 1, the
|
|
342
|
+
// protocol bound on how far ahead of finalized a log can sit). `end` grows toward it each round.
|
|
343
|
+
boundEnd: number;
|
|
344
|
+
// Intended probe length for the round that produced `end`. The queried span can be smaller when boundEnd caps it.
|
|
345
|
+
probeLen: number;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/** Unconstrained scan state: a plain window with no probe bookkeeping, since the scan always covers the full window. */
|
|
349
|
+
type PendingUnconstrained = PendingSecretBase & {
|
|
350
|
+
kind: typeof AppTaggingSecretKind.UNCONSTRAINED;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
type PendingSecret = PendingConstrained | PendingUnconstrained;
|
|
354
|
+
|
|
298
355
|
type LogWithIndex = {
|
|
299
356
|
log: LogResult;
|
|
300
357
|
taggingIndex: number;
|