@aztec/pxe 5.1.0 → 5.2.0-nightly.20260724
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/config/package_info.js +1 -1
- 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/config/package_info.ts +1 -1
- 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
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN: number;
|
|
2
|
-
|
|
2
|
+
export declare const INITIAL_CONSTRAINED_PROBE_LEN = 2;
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGFnZ2luZy9jb25zdGFudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBbUJBLGVBQU8sTUFBTSxzQ0FBc0MsUUFBK0IsQ0FBQztBQVVuRixlQUFPLE1BQU0sNkJBQTZCLElBQUksQ0FBQyJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/tagging/constants.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,sCAAsC,QAA+B,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/tagging/constants.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,sCAAsC,QAA+B,CAAC;AAUnF,eAAO,MAAM,6BAA6B,IAAI,CAAC"}
|
|
@@ -17,3 +17,12 @@ import { MAX_PRIVATE_LOGS_PER_TX } from '@aztec/constants';
|
|
|
17
17
|
// reserved at log emission time, before squashing is decided, and the kernel's reset/squash loop is not bounded by
|
|
18
18
|
// MAX_PRIVATE_LOGS_PER_TX. No fixed window value closes that gap.
|
|
19
19
|
export const UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN = MAX_PRIVATE_LOGS_PER_TX + 20;
|
|
20
|
+
// The number of tags probed per constrained secret in the first round.
|
|
21
|
+
//
|
|
22
|
+
// The probe doubles each round (2, 4, 8, ..., capped at UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN) while every probed
|
|
23
|
+
// index is a hit, stopping at the first missing tag. Constrained delivery is gapless, so a single missing tag proves
|
|
24
|
+
// the stream has ended: at steady state this turns a full WINDOW_LEN probe into two tags. A secret K logs behind
|
|
25
|
+
// catches up in ~log2(K) round-trips while the probe is still doubling (2, 4, 8, 16, 32), but once it saturates the cap
|
|
26
|
+
// and advances WINDOW_LEN tags per round, deeper catch-up is linear at ~K/WINDOW_LEN rounds. Either way it beats both
|
|
27
|
+
// the full window every round and one round per log.
|
|
28
|
+
export const INITIAL_CONSTRAINED_PROBE_LEN = 2;
|
package/dest/tagging/index.d.ts
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
export { syncTaggedPrivateLogs } from './recipient_sync/sync_tagged_private_logs.js';
|
|
12
12
|
export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
|
|
13
13
|
export { persistSenderTaggingIndexRangesForTx } from './persist_sender_tagging_index_ranges.js';
|
|
14
|
-
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
|
|
14
|
+
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, INITIAL_CONSTRAINED_PROBE_LEN } from './constants.js';
|
|
15
15
|
export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
|
|
16
16
|
export { AppTaggingSecret, Tag, SiloedTag } from '@aztec/stdlib/logs';
|
|
17
17
|
export { type PreTag, type TaggingIndexRange } from '@aztec/stdlib/logs';
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90YWdnaW5nL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUVILE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQ3JGLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQ3hGLE9BQU8sRUFBRSxvQ0FBb0MsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQ2hHLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSw2QkFBNkIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3ZHLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxrQ0FBa0MsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBR3hHLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdEUsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFFLEtBQUssaUJBQWlCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQyJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tagging/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,EAAE,oCAAoC,EAAE,MAAM,0CAA0C,CAAC;AAChG,OAAO,EAAE,sCAAsC,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tagging/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,EAAE,oCAAoC,EAAE,MAAM,0CAA0C,CAAC;AAChG,OAAO,EAAE,sCAAsC,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AACvG,OAAO,EAAE,uBAAuB,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAGxG,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dest/tagging/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/ export { syncTaggedPrivateLogs } from './recipient_sync/sync_tagged_private_logs.js';
|
|
11
11
|
export { syncSenderTaggingIndexes } from './sender_sync/sync_sender_tagging_indexes.js';
|
|
12
12
|
export { persistSenderTaggingIndexRangesForTx } from './persist_sender_tagging_index_ranges.js';
|
|
13
|
-
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from './constants.js';
|
|
13
|
+
export { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN, INITIAL_CONSTRAINED_PROBE_LEN } from './constants.js';
|
|
14
14
|
export { getAllPrivateLogsByTags, getAllPublicLogsByTagsFromContract } from './get_all_logs_by_tags.js';
|
|
15
15
|
// Re-export tagging-related types from stdlib
|
|
16
16
|
export { AppTaggingSecret, Tag, SiloedTag } from '@aztec/stdlib/logs';
|
|
@@ -54,9 +54,14 @@ import type { RecipientTaggingStore } from '../../storage/tagging_store/recipien
|
|
|
54
54
|
* - The lower bound is `highestFinalizedIndex + 1`. The `highestAgedIndex` mechanism is unnecessary because
|
|
55
55
|
* the nullifier chain prevents out-of-order index usage: no device can fill in a lower index after a higher
|
|
56
56
|
* one is already on-chain.
|
|
57
|
-
* -
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* - Because the sequence is gapless, the scan stops at the first missing index: that gap proves the sequence has
|
|
58
|
+
* ended and no further logs exist. We exploit this with a doubling first-miss probe (see
|
|
59
|
+
* `INITIAL_CONSTRAINED_PROBE_LEN` in ../constants.ts), so a
|
|
60
|
+
* steady-state sync with no new logs costs two tags instead of the whole window. The probe length is in-memory
|
|
61
|
+
* state scoped to one sync call, so every sync restarts at the initial probe.
|
|
62
|
+
* - The upper bound is the same as unconstrained: `highestFinalizedIndex + WINDOW_LEN`. Advancing the probe is
|
|
63
|
+
* decoupled from persisting the finalized index, so unfinalized logs at the top of the run are still fetched
|
|
64
|
+
* while only the finalized prefix is persisted.
|
|
60
65
|
*
|
|
61
66
|
* # Batching across secrets
|
|
62
67
|
*
|
|
@@ -66,4 +71,4 @@ import type { RecipientTaggingStore } from '../../storage/tagging_store/recipien
|
|
|
66
71
|
* advanced are kept for the next iteration.
|
|
67
72
|
*/
|
|
68
73
|
export declare function syncTaggedPrivateLogs(secrets: AppTaggingSecret[], aztecNode: AztecNode, taggingStore: RecipientTaggingStore, anchorBlockHeader: BlockHeader, finalizedBlockNumber: BlockNumber, jobId: string): Promise<LogResult[]>;
|
|
69
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luY190YWdnZWRfcHJpdmF0ZV9sb2dzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdGFnZ2luZy9yZWNpcGllbnRfc3luYy9zeW5jX3RhZ2dlZF9wcml2YXRlX2xvZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRzlELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXRFLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXBELE9BQU8sS0FBSyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sd0RBQXdELENBQUM7QUFLcEc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtFRztBQUNILHdCQUFzQixxQkFBcUIsQ0FDekMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLEVBQzNCLFNBQVMsRUFBRSxTQUFTLEVBQ3BCLFlBQVksRUFBRSxxQkFBcUIsRUFDbkMsaUJBQWlCLEVBQUUsV0FBVyxFQUM5QixvQkFBb0IsRUFBRSxXQUFXLEVBQ2pDLEtBQUssRUFBRSxNQUFNLEdBQ1osT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBcUR0QiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync_tagged_private_logs.d.ts","sourceRoot":"","sources":["../../../src/tagging/recipient_sync/sync_tagged_private_logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAG9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAKpG
|
|
1
|
+
{"version":3,"file":"sync_tagged_private_logs.d.ts","sourceRoot":"","sources":["../../../src/tagging/recipient_sync/sync_tagged_private_logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAG9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAKpG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,qBAAqB,EACnC,iBAAiB,EAAE,WAAW,EAC9B,oBAAoB,EAAE,WAAW,EACjC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,EAAE,CAAC,CAqDtB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { isDefined } from '@aztec/foundation/types';
|
|
3
3
|
import { AppTaggingSecretKind, SiloedTag } from '@aztec/stdlib/logs';
|
|
4
|
-
import { UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from '../constants.js';
|
|
4
|
+
import { INITIAL_CONSTRAINED_PROBE_LEN, UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN } from '../constants.js';
|
|
5
5
|
import { getAllPrivateLogsByTags } from '../get_all_logs_by_tags.js';
|
|
6
6
|
import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
7
7
|
/**
|
|
@@ -55,9 +55,14 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
55
55
|
* - The lower bound is `highestFinalizedIndex + 1`. The `highestAgedIndex` mechanism is unnecessary because
|
|
56
56
|
* the nullifier chain prevents out-of-order index usage: no device can fill in a lower index after a higher
|
|
57
57
|
* one is already on-chain.
|
|
58
|
-
* -
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* - Because the sequence is gapless, the scan stops at the first missing index: that gap proves the sequence has
|
|
59
|
+
* ended and no further logs exist. We exploit this with a doubling first-miss probe (see
|
|
60
|
+
* `INITIAL_CONSTRAINED_PROBE_LEN` in ../constants.ts), so a
|
|
61
|
+
* steady-state sync with no new logs costs two tags instead of the whole window. The probe length is in-memory
|
|
62
|
+
* state scoped to one sync call, so every sync restarts at the initial probe.
|
|
63
|
+
* - The upper bound is the same as unconstrained: `highestFinalizedIndex + WINDOW_LEN`. Advancing the probe is
|
|
64
|
+
* decoupled from persisting the finalized index, so unfinalized logs at the top of the run are still fetched
|
|
65
|
+
* while only the finalized prefix is persisted.
|
|
61
66
|
*
|
|
62
67
|
* # Batching across secrets
|
|
63
68
|
*
|
|
@@ -87,8 +92,7 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
87
92
|
allLogs.push(...logsFoundWithSecret.map(({ log })=>log));
|
|
88
93
|
// Persist new indexes. If the finalized index moved forward, the window advances
|
|
89
94
|
// and we need another round for this secret.
|
|
90
|
-
|
|
91
|
-
return await processForKind(pendingSecret, logsFoundWithSecret, taggingStore, currentTimestamp, finalizedBlockNumber, jobId);
|
|
95
|
+
return pendingSecret.kind === AppTaggingSecretKind.CONSTRAINED ? await processConstrainedResults(pendingSecret, logsFoundWithSecret, taggingStore, currentTimestamp, finalizedBlockNumber, jobId) : await processUnconstrainedResults(pendingSecret, logsFoundWithSecret, taggingStore, currentTimestamp, finalizedBlockNumber, jobId);
|
|
92
96
|
}));
|
|
93
97
|
pending = nextRound.filter(isDefined);
|
|
94
98
|
}
|
|
@@ -97,13 +101,29 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
97
101
|
/** Reads stored indexes for each secret and computes the initial index range to query. */ function getIndexRangesForSecrets(secrets, taggingStore, jobId) {
|
|
98
102
|
return Promise.all(secrets.map(async (secret)=>{
|
|
99
103
|
const currentHighestFinalizedIndex = await taggingStore.getHighestFinalizedIndex(secret, jobId);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
const boundEnd = (currentHighestFinalizedIndex ?? 0) + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1;
|
|
105
|
+
if (secret.kind === AppTaggingSecretKind.CONSTRAINED) {
|
|
106
|
+
// Constrained streams are gapless and resume at the finalized index, so probe a small initial window and stop
|
|
107
|
+
// at the first missing tag instead of always fetching the full window. The probe grows each round up to the
|
|
108
|
+
// window cap.
|
|
109
|
+
const start = currentHighestFinalizedIndex === undefined ? 0 : currentHighestFinalizedIndex + 1;
|
|
110
|
+
return {
|
|
111
|
+
kind: AppTaggingSecretKind.CONSTRAINED,
|
|
112
|
+
secret,
|
|
113
|
+
start,
|
|
114
|
+
end: Math.min(boundEnd, start + INITIAL_CONSTRAINED_PROBE_LEN),
|
|
115
|
+
boundEnd,
|
|
116
|
+
probeLen: INITIAL_CONSTRAINED_PROBE_LEN
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Unconstrained secrets can have gaps, so they scan the whole window starting past the highest aged index.
|
|
120
|
+
const highestAgedIndex = await taggingStore.getHighestAgedIndex(secret, jobId);
|
|
121
|
+
const start = highestAgedIndex === undefined ? 0 : highestAgedIndex + 1;
|
|
103
122
|
return {
|
|
123
|
+
kind: AppTaggingSecretKind.UNCONSTRAINED,
|
|
104
124
|
secret,
|
|
105
125
|
start,
|
|
106
|
-
end
|
|
126
|
+
end: boundEnd
|
|
107
127
|
};
|
|
108
128
|
}));
|
|
109
129
|
}
|
|
@@ -148,30 +168,43 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
148
168
|
return logsPerSecret;
|
|
149
169
|
}
|
|
150
170
|
/**
|
|
151
|
-
* Processes fetched logs for a constrained secret
|
|
152
|
-
*
|
|
171
|
+
* Processes fetched logs for a constrained secret: persists the finalized index and advances the probe. See the
|
|
172
|
+
* "# Constrained secrets" section above for why the scan stops at the first missing index. Only `highestFinalizedIndex`
|
|
153
173
|
* is tracked (no aged index).
|
|
154
174
|
*/ async function processConstrainedResults(pending, logsWithIndexes, taggingStore, currentTimestamp, finalizedBlockNumber, jobId) {
|
|
155
|
-
// Find where the contiguous run of indexes ends
|
|
156
|
-
// first gap, so all logs in the batch are within this prefix.
|
|
175
|
+
// Find where the contiguous run of indexes ends; all logs in the batch fall within this prefix.
|
|
157
176
|
const indexesWithLogs = new Set(logsWithIndexes.map((l)=>l.taggingIndex));
|
|
158
177
|
let firstMissingIndex = pending.start;
|
|
159
178
|
while(firstMissingIndex < pending.end && indexesWithLogs.has(firstMissingIndex)){
|
|
160
179
|
firstMissingIndex++;
|
|
161
180
|
}
|
|
162
|
-
//
|
|
163
|
-
// transaction, guaranteeing the log cannot disappear once included, so we persist the
|
|
164
|
-
//
|
|
181
|
+
// Persist the highest finalized index found. The nullifier for a constrained-delivery log is emitted in the same
|
|
182
|
+
// transaction, guaranteeing the log cannot disappear once included, so we persist even when a gap stops the scan.
|
|
183
|
+
// This lets the next sync round skip already-finalized indexes.
|
|
165
184
|
const { highestFinalizedIndex } = findHighestIndexes(logsWithIndexes, currentTimestamp, finalizedBlockNumber);
|
|
166
185
|
if (highestFinalizedIndex !== undefined) {
|
|
167
186
|
await taggingStore.updateHighestFinalizedIndex(pending.secret, highestFinalizedIndex, jobId);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
}
|
|
188
|
+
// Advancing the probe is decoupled from persisting the finalized index: keep scanning as long as the probe was
|
|
189
|
+
// entirely hits (no gap) and there is room before the protocol bound, even if none of those hits is finalized yet.
|
|
190
|
+
// Gating this on finalization would drop logs in unfinalized blocks, which sit at the top of the contiguous run.
|
|
191
|
+
// The bound re-anchors to any finalized index found this round.
|
|
192
|
+
const probeFullyConsumed = firstMissingIndex >= pending.end;
|
|
193
|
+
const boundEnd = highestFinalizedIndex !== undefined ? Math.max(pending.boundEnd, highestFinalizedIndex + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1) : pending.boundEnd;
|
|
194
|
+
// Double the probe each round, capped at the window (see INITIAL_CONSTRAINED_PROBE_LEN in ../constants.ts).
|
|
195
|
+
// The queried range is already bounded by boundEnd (a probe can never reach
|
|
196
|
+
// more than WINDOW_LEN past the finalized frontier); this cap just keeps the stored length from growing unbounded
|
|
197
|
+
// once the probe saturates the window.
|
|
198
|
+
const nextProbeLen = Math.min(pending.probeLen * 2, UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN);
|
|
199
|
+
if (probeFullyConsumed && pending.end < boundEnd) {
|
|
200
|
+
return {
|
|
201
|
+
kind: AppTaggingSecretKind.CONSTRAINED,
|
|
202
|
+
secret: pending.secret,
|
|
203
|
+
start: pending.end,
|
|
204
|
+
end: Math.min(boundEnd, pending.end + nextProbeLen),
|
|
205
|
+
boundEnd,
|
|
206
|
+
probeLen: nextProbeLen
|
|
207
|
+
};
|
|
175
208
|
}
|
|
176
209
|
return undefined;
|
|
177
210
|
}
|
|
@@ -195,9 +228,11 @@ import { findHighestIndexes } from './utils/find_highest_indexes.js';
|
|
|
195
228
|
await taggingStore.updateHighestFinalizedIndex(pending.secret, highestFinalizedIndex, jobId);
|
|
196
229
|
// For the next iteration we want to look only at indexes for which we have not yet fetched logs while
|
|
197
230
|
// ensuring that we do not look further than WINDOW_LEN ahead of the highest finalized index.
|
|
231
|
+
const end = highestFinalizedIndex + UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN + 1;
|
|
198
232
|
return {
|
|
233
|
+
kind: AppTaggingSecretKind.UNCONSTRAINED,
|
|
199
234
|
secret: pending.secret,
|
|
200
235
|
start: pending.end,
|
|
201
|
-
end
|
|
236
|
+
end
|
|
202
237
|
};
|
|
203
238
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AppTaggingSecret, type PrivateLogsQuery, SiloedTag } from '@aztec/stdlib/logs';
|
|
2
|
+
/** Computes the siloed tag */
|
|
3
|
+
export declare function computeSiloedTagForIndex(secret: AppTaggingSecret, index: number): Promise<SiloedTag>;
|
|
4
|
+
/** Extracts the bare-tag set from a query */
|
|
5
|
+
export declare function extractTags(query: PrivateLogsQuery): SiloedTag[];
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFnX3F1ZXJ5X3Rlc3RfdXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90YWdnaW5nL3Rlc3RpbmcvdGFnX3F1ZXJ5X3Rlc3RfdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssZ0JBQWdCLEVBQUUsS0FBSyxnQkFBZ0IsRUFBRSxTQUFTLEVBQWlCLE1BQU0sb0JBQW9CLENBQUM7QUFFNUcsOEJBQThCO0FBQzlCLHdCQUFnQix3QkFBd0IsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLEVBQUUsS0FBSyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBRXBHO0FBRUQsNkNBQTZDO0FBQzdDLHdCQUFnQixXQUFXLENBQUMsS0FBSyxFQUFFLGdCQUFnQixHQUFHLFNBQVMsRUFBRSxDQUVoRSJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag_query_test_utils.d.ts","sourceRoot":"","sources":["../../../src/tagging/testing/tag_query_test_utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAiB,MAAM,oBAAoB,CAAC;AAE5G,8BAA8B;AAC9B,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAEpG;AAED,6CAA6C;AAC7C,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EAAE,CAEhE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SiloedTag } from '@aztec/stdlib/logs';
|
|
2
|
+
/** Computes the siloed tag */ export function computeSiloedTagForIndex(secret, index) {
|
|
3
|
+
return SiloedTag.compute({
|
|
4
|
+
extendedSecret: secret,
|
|
5
|
+
index
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
/** Extracts the bare-tag set from a query */ export function extractTags(query) {
|
|
9
|
+
return query.tags.map((entry)=>entry instanceof SiloedTag ? entry : entry.tag);
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/pxe",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0-nightly.20260724",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"typedocOptions": {
|
|
6
6
|
"entryPoints": [
|
|
@@ -71,20 +71,20 @@
|
|
|
71
71
|
]
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@aztec/bb-prover": "5.
|
|
75
|
-
"@aztec/bb.js": "5.
|
|
76
|
-
"@aztec/builder": "5.
|
|
77
|
-
"@aztec/constants": "5.
|
|
78
|
-
"@aztec/ethereum": "5.
|
|
79
|
-
"@aztec/foundation": "5.
|
|
80
|
-
"@aztec/key-store": "5.
|
|
81
|
-
"@aztec/kv-store": "5.
|
|
82
|
-
"@aztec/noir-protocol-circuits-types": "5.
|
|
83
|
-
"@aztec/noir-types": "5.
|
|
84
|
-
"@aztec/protocol-contracts": "5.
|
|
85
|
-
"@aztec/simulator": "5.
|
|
86
|
-
"@aztec/standard-contracts": "5.
|
|
87
|
-
"@aztec/stdlib": "5.
|
|
74
|
+
"@aztec/bb-prover": "5.2.0-nightly.20260724",
|
|
75
|
+
"@aztec/bb.js": "5.2.0-nightly.20260724",
|
|
76
|
+
"@aztec/builder": "5.2.0-nightly.20260724",
|
|
77
|
+
"@aztec/constants": "5.2.0-nightly.20260724",
|
|
78
|
+
"@aztec/ethereum": "5.2.0-nightly.20260724",
|
|
79
|
+
"@aztec/foundation": "5.2.0-nightly.20260724",
|
|
80
|
+
"@aztec/key-store": "5.2.0-nightly.20260724",
|
|
81
|
+
"@aztec/kv-store": "5.2.0-nightly.20260724",
|
|
82
|
+
"@aztec/noir-protocol-circuits-types": "5.2.0-nightly.20260724",
|
|
83
|
+
"@aztec/noir-types": "5.2.0-nightly.20260724",
|
|
84
|
+
"@aztec/protocol-contracts": "5.2.0-nightly.20260724",
|
|
85
|
+
"@aztec/simulator": "5.2.0-nightly.20260724",
|
|
86
|
+
"@aztec/standard-contracts": "5.2.0-nightly.20260724",
|
|
87
|
+
"@aztec/stdlib": "5.2.0-nightly.20260724",
|
|
88
88
|
"koa": "^2.16.1",
|
|
89
89
|
"koa-router": "^13.1.1",
|
|
90
90
|
"lodash.omit": "^4.5.0",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
|
-
"@aztec/noir-test-contracts.js": "5.
|
|
97
|
-
"@aztec/world-state": "5.
|
|
96
|
+
"@aztec/noir-test-contracts.js": "5.2.0-nightly.20260724",
|
|
97
|
+
"@aztec/world-state": "5.2.0-nightly.20260724",
|
|
98
98
|
"@jest/globals": "^30.0.0",
|
|
99
99
|
"@types/jest": "^30.0.0",
|
|
100
100
|
"@types/lodash.omit": "^4.5.7",
|
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;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type AppTaggingSecret, type PrivateLogsQuery, SiloedTag, type TagQuery } from '@aztec/stdlib/logs';
|
|
2
|
+
|
|
3
|
+
/** Computes the siloed tag */
|
|
4
|
+
export function computeSiloedTagForIndex(secret: AppTaggingSecret, index: number): Promise<SiloedTag> {
|
|
5
|
+
return SiloedTag.compute({ extendedSecret: secret, index });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Extracts the bare-tag set from a query */
|
|
9
|
+
export function extractTags(query: PrivateLogsQuery): SiloedTag[] {
|
|
10
|
+
return query.tags.map((entry: TagQuery<SiloedTag>) => (entry instanceof SiloedTag ? entry : entry.tag));
|
|
11
|
+
}
|