@aztec/archiver 0.0.1-commit.d431d1c → 0.0.1-commit.d58ff9d0
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/README.md +28 -11
- package/dest/archiver.d.ts +80 -21
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +347 -167
- package/dest/config.d.ts +7 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +38 -14
- package/dest/errors.d.ts +60 -9
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +88 -13
- package/dest/factory.d.ts +13 -6
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +59 -39
- package/dest/index.d.ts +12 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +11 -2
- package/dest/l1/bin/retrieve-calldata.js +35 -32
- package/dest/l1/calldata_retriever.d.ts +81 -50
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +202 -260
- package/dest/l1/data_retrieval.d.ts +32 -18
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +45 -50
- package/dest/l1/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -1
- package/dest/l1/spire_proposer.js +9 -17
- package/dest/l1/trace_tx.d.ts +12 -66
- package/dest/l1/trace_tx.d.ts.map +1 -1
- package/dest/l1/validate_historical_logs.d.ts +23 -0
- package/dest/l1/validate_historical_logs.d.ts.map +1 -0
- package/dest/l1/validate_historical_logs.js +108 -0
- package/dest/l1/validate_trace.d.ts +6 -3
- package/dest/l1/validate_trace.d.ts.map +1 -1
- package/dest/l1/validate_trace.js +13 -9
- package/dest/modules/contract_data_source_adapter.d.ts +25 -0
- package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
- package/dest/modules/contract_data_source_adapter.js +32 -0
- package/dest/modules/data_source_base.d.ts +75 -48
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +251 -220
- package/dest/modules/data_store_updater.d.ts +65 -29
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +242 -153
- package/dest/modules/instrumentation.d.ts +30 -4
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +83 -20
- package/dest/modules/l1_synchronizer.d.ts +16 -14
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +493 -225
- package/dest/modules/outbox_trees_resolver.d.ts +62 -0
- package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
- package/dest/modules/outbox_trees_resolver.js +162 -0
- package/dest/modules/validation.d.ts +30 -7
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +35 -17
- package/dest/store/block_store.d.ts +200 -75
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +802 -276
- package/dest/store/contract_class_store.d.ts +17 -4
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +51 -73
- package/dest/store/contract_instance_store.d.ts +28 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +54 -2
- package/dest/store/data_stores.d.ts +68 -0
- package/dest/store/data_stores.d.ts.map +1 -0
- package/dest/store/data_stores.js +54 -0
- package/dest/store/function_names_cache.d.ts +17 -0
- package/dest/store/function_names_cache.d.ts.map +1 -0
- package/dest/store/function_names_cache.js +30 -0
- package/dest/store/l2_tips_cache.d.ts +25 -0
- package/dest/store/l2_tips_cache.d.ts.map +1 -0
- package/dest/store/l2_tips_cache.js +26 -0
- package/dest/store/log_store.d.ts +42 -37
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +262 -388
- package/dest/store/log_store_codec.d.ts +78 -0
- package/dest/store/log_store_codec.d.ts.map +1 -0
- package/dest/store/log_store_codec.js +110 -0
- package/dest/store/message_store.d.ts +11 -1
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +51 -9
- package/dest/test/fake_l1_state.d.ts +28 -4
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +166 -32
- package/dest/test/index.js +3 -1
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +3 -2
- package/dest/test/mock_l2_block_source.d.ts +65 -51
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +309 -215
- package/dest/test/mock_structs.d.ts +6 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +26 -12
- package/dest/test/noop_l1_archiver.d.ts +34 -0
- package/dest/test/noop_l1_archiver.d.ts.map +1 -0
- package/dest/test/noop_l1_archiver.js +88 -0
- package/package.json +15 -14
- package/src/archiver.ts +458 -198
- package/src/config.ts +43 -13
- package/src/errors.ts +134 -21
- package/src/factory.ts +87 -36
- package/src/index.ts +19 -2
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +45 -33
- package/src/l1/calldata_retriever.ts +267 -379
- package/src/l1/data_retrieval.ts +70 -74
- package/src/l1/spire_proposer.ts +7 -15
- package/src/l1/validate_historical_logs.ts +140 -0
- package/src/l1/validate_trace.ts +24 -6
- package/src/modules/contract_data_source_adapter.ts +46 -0
- package/src/modules/data_source_base.ts +336 -288
- package/src/modules/data_store_updater.ts +287 -187
- package/src/modules/instrumentation.ts +86 -21
- package/src/modules/l1_synchronizer.ts +667 -278
- package/src/modules/outbox_trees_resolver.ts +199 -0
- package/src/modules/validation.ts +80 -23
- package/src/store/block_store.ts +984 -352
- package/src/store/contract_class_store.ts +57 -107
- package/src/store/contract_instance_store.ts +68 -5
- package/src/store/data_stores.ts +104 -0
- package/src/store/function_names_cache.ts +37 -0
- package/src/store/l2_tips_cache.ts +35 -0
- package/src/store/log_store.ts +305 -500
- package/src/store/log_store_codec.ts +143 -0
- package/src/store/message_store.ts +60 -10
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +215 -44
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +381 -246
- package/src/test/mock_structs.ts +50 -17
- package/src/test/noop_l1_archiver.ts +158 -0
- package/dest/store/kv_archiver_store.d.ts +0 -336
- package/dest/store/kv_archiver_store.d.ts.map +0 -1
- package/dest/store/kv_archiver_store.js +0 -443
- package/src/store/kv_archiver_store.ts +0 -631
package/src/store/log_store.ts
CHANGED
|
@@ -1,575 +1,380 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { filterAsync } from '@aztec/foundation/collection';
|
|
4
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
|
-
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
7
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
8
|
-
import type {
|
|
9
|
-
import { L2BlockHash, L2BlockNew } from '@aztec/stdlib/block';
|
|
5
|
+
import type { BlockHash, L2Block } from '@aztec/stdlib/block';
|
|
10
6
|
import { MAX_LOGS_PER_TAG } from '@aztec/stdlib/interfaces/api-limit';
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
LogId,
|
|
18
|
-
PublicLog,
|
|
19
|
-
type SiloedTag,
|
|
7
|
+
import type {
|
|
8
|
+
LogCursor,
|
|
9
|
+
LogResult,
|
|
10
|
+
PrivateLogsQuery,
|
|
11
|
+
PublicLogsQuery,
|
|
12
|
+
SiloedTag,
|
|
20
13
|
Tag,
|
|
21
|
-
|
|
14
|
+
TagQuery,
|
|
22
15
|
} from '@aztec/stdlib/logs';
|
|
16
|
+
import { TxHash } from '@aztec/stdlib/tx';
|
|
23
17
|
|
|
24
18
|
import type { BlockStore } from './block_store.js';
|
|
19
|
+
import {
|
|
20
|
+
decodeKeyTail,
|
|
21
|
+
decodeValue,
|
|
22
|
+
encodeKey,
|
|
23
|
+
encodePublicPrefix,
|
|
24
|
+
encodeValue,
|
|
25
|
+
endOfTagRange,
|
|
26
|
+
endOfTxRange,
|
|
27
|
+
fieldHex,
|
|
28
|
+
incKey,
|
|
29
|
+
tagHexForLog,
|
|
30
|
+
} from './log_store_codec.js';
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
|
-
*
|
|
33
|
+
* Indexes every emitted private and public log under a composite hex-string key
|
|
34
|
+
* `[contractAddress (public only)]-tag-blockNumber-txIndexWithinBlock-logIndexWithinTx`,
|
|
35
|
+
* where each numeric segment is zero-padded to 8 lowercase hex digits (4 bytes BE) and
|
|
36
|
+
* `contractAddress` / `tag` are the bare 64-hex-char field representations (no `0x` prefix). The
|
|
37
|
+
* fixed-width zero-padded hex segments sort lexicographically in the same order as the canonical
|
|
38
|
+
* `(contract, tag, blockNumber, txIndexWithinBlock, logIndexWithinTx)` tuple, so a single ordered
|
|
39
|
+
* range scan answers every {@link PrivateLogsQuery} / {@link PublicLogsQuery}.
|
|
40
|
+
*
|
|
41
|
+
* Per-block secondary indices (`#privateKeysByBlock`, `#publicKeysByBlock`) record the exact primary
|
|
42
|
+
* keys written for each block so {@link deleteLogs} can drop them on reorg without having to range
|
|
43
|
+
* scan by block (block isn't the leading key segment).
|
|
44
|
+
*
|
|
45
|
+
* Contract-class logs are no longer stored or served by the log store.
|
|
28
46
|
*/
|
|
29
47
|
export class LogStore {
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
|
|
48
|
+
/** Primary map: composite private key (tag + tail = 96 hex chars + separators) -> serialized {@link StoredLogValue}. */
|
|
49
|
+
#privateLogs: AztecAsyncMap<string, Buffer>;
|
|
50
|
+
/** Primary map: composite public key (contract + tag + tail) -> serialized {@link StoredLogValue}. */
|
|
51
|
+
#publicLogs: AztecAsyncMap<string, Buffer>;
|
|
52
|
+
|
|
53
|
+
/** Secondary deletion index: blockNumber -> the exact primary keys written for that block. */
|
|
54
|
+
#privateKeysByBlock: AztecAsyncMap<number, string[]>;
|
|
55
|
+
#publicKeysByBlock: AztecAsyncMap<number, string[]>;
|
|
56
|
+
|
|
39
57
|
#log = createLogger('archiver:log_store');
|
|
40
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @param genesisBlockHash - Hash of the synthetic genesis block. During early sync the PXE anchors to
|
|
61
|
+
* genesis and passes its hash as a query `referenceBlock`; since the archiver never indexes the
|
|
62
|
+
* genesis block, the store recognizes this hash directly and resolves it to the genesis block number
|
|
63
|
+
* rather than mistaking it for a reorg.
|
|
64
|
+
*/
|
|
41
65
|
constructor(
|
|
42
66
|
private db: AztecAsyncKVStore,
|
|
43
67
|
private blockStore: BlockStore,
|
|
44
|
-
|
|
68
|
+
private readonly genesisBlockHash: BlockHash,
|
|
45
69
|
) {
|
|
46
|
-
this.#
|
|
47
|
-
this.#
|
|
48
|
-
this.#
|
|
49
|
-
this.#
|
|
50
|
-
this.#publicLogsByBlock = db.openMap('archiver_public_logs_by_block');
|
|
51
|
-
this.#contractClassLogsByBlock = db.openMap('archiver_contract_class_logs_by_block');
|
|
52
|
-
|
|
53
|
-
this.#logsMaxPageSize = logsMaxPageSize;
|
|
70
|
+
this.#privateLogs = db.openMap('archiver_private_logs');
|
|
71
|
+
this.#publicLogs = db.openMap('archiver_public_logs');
|
|
72
|
+
this.#privateKeysByBlock = db.openMap('archiver_private_log_keys_by_block');
|
|
73
|
+
this.#publicKeysByBlock = db.openMap('archiver_public_log_keys_by_block');
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
/**
|
|
57
|
-
*
|
|
77
|
+
* Indexes every emitted private and public log from the given blocks. Wraps the write in a single
|
|
78
|
+
* `db.transactionAsync` so the primary entries and the per-block secondary indices stay consistent.
|
|
58
79
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*/
|
|
62
|
-
#extractTaggedLogsFromBlock(block: L2BlockNew) {
|
|
63
|
-
// SiloedTag (as string) -> array of log buffers.
|
|
64
|
-
const privateTaggedLogs = new Map<string, Buffer[]>();
|
|
65
|
-
// "{contractAddress}_{tag}" (as string) -> array of log buffers.
|
|
66
|
-
const publicTaggedLogs = new Map<string, Buffer[]>();
|
|
67
|
-
|
|
68
|
-
block.body.txEffects.forEach(txEffect => {
|
|
69
|
-
const txHash = txEffect.txHash;
|
|
70
|
-
|
|
71
|
-
txEffect.privateLogs.forEach(log => {
|
|
72
|
-
// Private logs use SiloedTag (already siloed by kernel)
|
|
73
|
-
const tag = log.fields[0];
|
|
74
|
-
this.#log.debug(`Found private log with tag ${tag.toString()} in block ${block.number}`);
|
|
75
|
-
|
|
76
|
-
const currentLogs = privateTaggedLogs.get(tag.toString()) ?? [];
|
|
77
|
-
currentLogs.push(
|
|
78
|
-
new TxScopedL2Log(
|
|
79
|
-
txHash,
|
|
80
|
-
block.number,
|
|
81
|
-
block.timestamp,
|
|
82
|
-
log.getEmittedFields(),
|
|
83
|
-
txEffect.noteHashes,
|
|
84
|
-
txEffect.nullifiers[0],
|
|
85
|
-
).toBuffer(),
|
|
86
|
-
);
|
|
87
|
-
privateTaggedLogs.set(tag.toString(), currentLogs);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
txEffect.publicLogs.forEach(log => {
|
|
91
|
-
// Public logs use Tag directly (not siloed) and are stored with contract address
|
|
92
|
-
const tag = log.fields[0];
|
|
93
|
-
const contractAddress = log.contractAddress;
|
|
94
|
-
const key = `${contractAddress.toString()}_${tag.toString()}`;
|
|
95
|
-
this.#log.debug(
|
|
96
|
-
`Found public log with tag ${tag.toString()} from contract ${contractAddress.toString()} in block ${block.number}`,
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const currentLogs = publicTaggedLogs.get(key) ?? [];
|
|
100
|
-
currentLogs.push(
|
|
101
|
-
new TxScopedL2Log(
|
|
102
|
-
txHash,
|
|
103
|
-
block.number,
|
|
104
|
-
block.timestamp,
|
|
105
|
-
log.getEmittedFields(),
|
|
106
|
-
txEffect.noteHashes,
|
|
107
|
-
txEffect.nullifiers[0],
|
|
108
|
-
).toBuffer(),
|
|
109
|
-
);
|
|
110
|
-
publicTaggedLogs.set(key, currentLogs);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
return { privateTaggedLogs, publicTaggedLogs };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Extracts and aggregates tagged logs from a list of blocks.
|
|
119
|
-
* @param blocks - The blocks to extract logs from.
|
|
120
|
-
* @returns A map from tag (as string) to an array of serialized private logs belonging to that tag, and a map from
|
|
121
|
-
* "{contractAddress}_{tag}" (as string) to an array of serialized public logs belonging to that key.
|
|
80
|
+
* A block is only ever added once; on reorg the archiver calls {@link deleteLogs} first, so we write
|
|
81
|
+
* the secondary index entries with a plain `set` (overwrite) rather than read-modify-append.
|
|
122
82
|
*/
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
await this.#privateLogsByTag.set(tag, logs);
|
|
178
|
-
privateTagsInBlock.push(tag);
|
|
179
|
-
}
|
|
180
|
-
await this.#privateLogKeysByBlock.set(block.number, privateTagsInBlock);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async #addPublicLogs(blocks: L2BlockNew[]): Promise<void> {
|
|
185
|
-
const newBlocks = await filterAsync(
|
|
186
|
-
blocks,
|
|
187
|
-
async block => !(await this.#publicLogKeysByBlock.hasAsync(block.number)),
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
const { publicTaggedLogs } = this.#extractTaggedLogs(newBlocks);
|
|
191
|
-
const keysOfPublicLogsToUpdate = Array.from(publicTaggedLogs.keys());
|
|
83
|
+
addLogs(blocks: L2Block[]): Promise<boolean> {
|
|
84
|
+
return this.db.transactionAsync(async () => {
|
|
85
|
+
for (const block of blocks) {
|
|
86
|
+
const blockHash = await block.hash();
|
|
87
|
+
const blockNumber = block.number;
|
|
88
|
+
const blockTimestamp = block.timestamp;
|
|
89
|
+
|
|
90
|
+
const privateKeys: string[] = [];
|
|
91
|
+
const privateValues: Buffer[] = [];
|
|
92
|
+
const publicKeys: string[] = [];
|
|
93
|
+
const publicValues: Buffer[] = [];
|
|
94
|
+
|
|
95
|
+
for (let txIndexWithinBlock = 0; txIndexWithinBlock < block.body.txEffects.length; txIndexWithinBlock++) {
|
|
96
|
+
const txEffect = block.body.txEffects[txIndexWithinBlock];
|
|
97
|
+
const txHash = txEffect.txHash;
|
|
98
|
+
|
|
99
|
+
// Private and public log indices are counted independently per tx, each starting at 0.
|
|
100
|
+
let privateLogIndexWithinTx = 0;
|
|
101
|
+
let publicLogIndexWithinTx = 0;
|
|
102
|
+
|
|
103
|
+
for (const log of txEffect.privateLogs) {
|
|
104
|
+
const tagHex = tagHexForLog(log.fields);
|
|
105
|
+
const key = encodeKey(tagHex, blockNumber, txIndexWithinBlock, privateLogIndexWithinTx);
|
|
106
|
+
const value = encodeValue({
|
|
107
|
+
txHash,
|
|
108
|
+
blockHash,
|
|
109
|
+
blockTimestamp,
|
|
110
|
+
logData: log.getEmittedFields(),
|
|
111
|
+
});
|
|
112
|
+
privateKeys.push(key);
|
|
113
|
+
privateValues.push(value);
|
|
114
|
+
privateLogIndexWithinTx++;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const log of txEffect.publicLogs) {
|
|
118
|
+
const contractHex = fieldHex(log.contractAddress);
|
|
119
|
+
const tagHex = tagHexForLog(log.fields);
|
|
120
|
+
const key = encodeKey(
|
|
121
|
+
encodePublicPrefix(contractHex, tagHex),
|
|
122
|
+
blockNumber,
|
|
123
|
+
txIndexWithinBlock,
|
|
124
|
+
publicLogIndexWithinTx,
|
|
125
|
+
);
|
|
126
|
+
const value = encodeValue({
|
|
127
|
+
txHash,
|
|
128
|
+
blockHash,
|
|
129
|
+
blockTimestamp,
|
|
130
|
+
logData: log.getEmittedFields(),
|
|
131
|
+
});
|
|
132
|
+
publicKeys.push(key);
|
|
133
|
+
publicValues.push(value);
|
|
134
|
+
publicLogIndexWithinTx++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
192
137
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
138
|
+
for (let i = 0; i < privateKeys.length; i++) {
|
|
139
|
+
await this.#privateLogs.set(privateKeys[i], privateValues[i]);
|
|
140
|
+
}
|
|
141
|
+
for (let i = 0; i < publicKeys.length; i++) {
|
|
142
|
+
await this.#publicLogs.set(publicKeys[i], publicValues[i]);
|
|
143
|
+
}
|
|
199
144
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
publicTaggedLogs.set(
|
|
203
|
-
taggedLogBuffer.tag,
|
|
204
|
-
taggedLogBuffer.logBuffers!.concat(publicTaggedLogs.get(taggedLogBuffer.tag)!),
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
145
|
+
await this.#privateKeysByBlock.set(blockNumber, privateKeys);
|
|
146
|
+
await this.#publicKeysByBlock.set(blockNumber, publicKeys);
|
|
208
147
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
publicTagsInBlock.push(tag);
|
|
148
|
+
this.#log.debug(`Indexed logs for block ${blockNumber}`, {
|
|
149
|
+
blockNumber,
|
|
150
|
+
privateCount: privateKeys.length,
|
|
151
|
+
publicCount: publicKeys.length,
|
|
152
|
+
});
|
|
215
153
|
}
|
|
216
|
-
await this.#publicLogKeysByBlock.set(block.number, publicTagsInBlock);
|
|
217
|
-
|
|
218
|
-
const publicLogsInBlock = block.body.txEffects
|
|
219
|
-
.map((txEffect, txIndex) =>
|
|
220
|
-
[
|
|
221
|
-
numToUInt32BE(txIndex),
|
|
222
|
-
numToUInt32BE(txEffect.publicLogs.length),
|
|
223
|
-
txEffect.publicLogs.map(log => log.toBuffer()),
|
|
224
|
-
].flat(),
|
|
225
|
-
)
|
|
226
|
-
.flat();
|
|
227
|
-
|
|
228
|
-
await this.#publicLogsByBlock.set(block.number, this.#packWithBlockHash(blockHash, publicLogsInBlock));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async #addContractClassLogs(blocks: L2BlockNew[]): Promise<void> {
|
|
233
|
-
const newBlocks = await filterAsync(
|
|
234
|
-
blocks,
|
|
235
|
-
async block => !(await this.#contractClassLogsByBlock.hasAsync(block.number)),
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
for (const block of newBlocks) {
|
|
239
|
-
const blockHash = await block.hash();
|
|
240
|
-
|
|
241
|
-
const contractClassLogsInBlock = block.body.txEffects
|
|
242
|
-
.map((txEffect, txIndex) =>
|
|
243
|
-
[
|
|
244
|
-
numToUInt32BE(txIndex),
|
|
245
|
-
numToUInt32BE(txEffect.contractClassLogs.length),
|
|
246
|
-
txEffect.contractClassLogs.map(log => log.toBuffer()),
|
|
247
|
-
].flat(),
|
|
248
|
-
)
|
|
249
|
-
.flat();
|
|
250
|
-
|
|
251
|
-
await this.#contractClassLogsByBlock.set(
|
|
252
|
-
block.number,
|
|
253
|
-
this.#packWithBlockHash(blockHash, contractClassLogsInBlock),
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Append new logs to the store's list.
|
|
260
|
-
* @param blocks - The blocks for which to add the logs.
|
|
261
|
-
* @returns True if the operation is successful.
|
|
262
|
-
*/
|
|
263
|
-
addLogs(blocks: L2BlockNew[]): Promise<boolean> {
|
|
264
|
-
return this.db.transactionAsync(async () => {
|
|
265
|
-
await Promise.all([
|
|
266
|
-
this.#addPrivateLogs(blocks),
|
|
267
|
-
this.#addPublicLogs(blocks),
|
|
268
|
-
this.#addContractClassLogs(blocks),
|
|
269
|
-
]);
|
|
270
154
|
return true;
|
|
271
155
|
});
|
|
272
156
|
}
|
|
273
157
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const blockHash = reader.remainingBytes() > 0 ? reader.readObject(Fr) : undefined;
|
|
280
|
-
|
|
281
|
-
if (!blockHash) {
|
|
282
|
-
throw new Error('Failed to read block hash from log entry buffer');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return L2BlockHash.fromField(blockHash);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
deleteLogs(blocks: L2BlockNew[]): Promise<boolean> {
|
|
158
|
+
/**
|
|
159
|
+
* Deletes every log indexed under any of the given blocks. Secondary-index driven, so it doesn't
|
|
160
|
+
* have to range-scan the primary maps.
|
|
161
|
+
*/
|
|
162
|
+
deleteLogs(blocks: L2Block[]): Promise<boolean> {
|
|
289
163
|
return this.db.transactionAsync(async () => {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this.#
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
);
|
|
312
|
-
|
|
164
|
+
for (const block of blocks) {
|
|
165
|
+
const blockNumber = block.number;
|
|
166
|
+
|
|
167
|
+
const [privateKeys, publicKeys] = await Promise.all([
|
|
168
|
+
this.#privateKeysByBlock.getAsync(blockNumber),
|
|
169
|
+
this.#publicKeysByBlock.getAsync(blockNumber),
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
if (privateKeys) {
|
|
173
|
+
for (const key of privateKeys) {
|
|
174
|
+
await this.#privateLogs.delete(key);
|
|
175
|
+
}
|
|
176
|
+
await this.#privateKeysByBlock.delete(blockNumber);
|
|
177
|
+
}
|
|
178
|
+
if (publicKeys) {
|
|
179
|
+
for (const key of publicKeys) {
|
|
180
|
+
await this.#publicLogs.delete(key);
|
|
181
|
+
}
|
|
182
|
+
await this.#publicKeysByBlock.delete(blockNumber);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
313
185
|
return true;
|
|
314
186
|
});
|
|
315
187
|
}
|
|
316
188
|
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
* @param page - The page number (0-indexed) for pagination.
|
|
322
|
-
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
323
|
-
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
324
|
-
*/
|
|
325
|
-
async getPrivateLogsByTags(tags: SiloedTag[], page: number = 0): Promise<TxScopedL2Log[][]> {
|
|
326
|
-
const logs = await Promise.all(tags.map(tag => this.#privateLogsByTag.getAsync(tag.toString())));
|
|
327
|
-
const start = page * MAX_LOGS_PER_TAG;
|
|
328
|
-
const end = start + MAX_LOGS_PER_TAG;
|
|
329
|
-
|
|
330
|
-
return logs.map(
|
|
331
|
-
logBuffers => logBuffers?.slice(start, end).map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? [],
|
|
332
|
-
);
|
|
189
|
+
/** Returns one inner array per element of `query.tags`, in input order. */
|
|
190
|
+
getPrivateLogsByTags(query: PrivateLogsQuery): Promise<LogResult[][]> {
|
|
191
|
+
LogStore.#validateQuery(query);
|
|
192
|
+
return this.db.transactionAsync(() => this.#runQuery(query, /* contractHex */ undefined));
|
|
333
193
|
}
|
|
334
194
|
|
|
335
|
-
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
* @param tags - The tags to search for.
|
|
340
|
-
* @param page - The page number (0-indexed) for pagination.
|
|
341
|
-
* @returns An array of log arrays, one per tag. Returns at most MAX_LOGS_PER_TAG logs per tag per page. If
|
|
342
|
-
* MAX_LOGS_PER_TAG logs are returned for a tag, the caller should fetch the next page to check for more logs.
|
|
343
|
-
*/
|
|
344
|
-
async getPublicLogsByTagsFromContract(
|
|
345
|
-
contractAddress: AztecAddress,
|
|
346
|
-
tags: Tag[],
|
|
347
|
-
page: number = 0,
|
|
348
|
-
): Promise<TxScopedL2Log[][]> {
|
|
349
|
-
const logs = await Promise.all(
|
|
350
|
-
tags.map(tag => {
|
|
351
|
-
const key = `${contractAddress.toString()}_${tag.value.toString()}`;
|
|
352
|
-
return this.#publicLogsByContractAndTag.getAsync(key);
|
|
353
|
-
}),
|
|
354
|
-
);
|
|
355
|
-
const start = page * MAX_LOGS_PER_TAG;
|
|
356
|
-
const end = start + MAX_LOGS_PER_TAG;
|
|
357
|
-
|
|
358
|
-
return logs.map(
|
|
359
|
-
logBuffers => logBuffers?.slice(start, end).map(logBuffer => TxScopedL2Log.fromBuffer(logBuffer)) ?? [],
|
|
360
|
-
);
|
|
195
|
+
/** Returns one inner array per element of `query.tags`, in input order. */
|
|
196
|
+
getPublicLogsByTags(query: PublicLogsQuery): Promise<LogResult[][]> {
|
|
197
|
+
LogStore.#validateQuery(query);
|
|
198
|
+
return this.db.transactionAsync(() => this.#runQuery(query, fieldHex(query.contractAddress)));
|
|
361
199
|
}
|
|
362
200
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
* @returns The requested logs.
|
|
367
|
-
*/
|
|
368
|
-
getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
369
|
-
if (filter.afterLog) {
|
|
370
|
-
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
371
|
-
} else if (filter.txHash) {
|
|
372
|
-
return this.#filterPublicLogsOfTx(filter);
|
|
373
|
-
} else {
|
|
374
|
-
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
201
|
+
static #validateQuery(query: { txHash?: TxHash; fromBlock?: unknown; toBlock?: unknown }): void {
|
|
202
|
+
if (query.txHash !== undefined && (query.fromBlock !== undefined || query.toBlock !== undefined)) {
|
|
203
|
+
throw new Error('`txHash` is mutually exclusive with `fromBlock`/`toBlock`');
|
|
375
204
|
}
|
|
376
205
|
}
|
|
377
206
|
|
|
378
|
-
async #
|
|
379
|
-
|
|
380
|
-
|
|
207
|
+
async #runQuery(query: PrivateLogsQuery | PublicLogsQuery, contractHex: string | undefined): Promise<LogResult[][]> {
|
|
208
|
+
const isPublic = contractHex !== undefined;
|
|
209
|
+
const tags = (query.tags as ReadonlyArray<TagQuery<Tag | SiloedTag>>) ?? [];
|
|
210
|
+
const primaryMap = isPublic ? this.#publicLogs : this.#privateLogs;
|
|
211
|
+
|
|
212
|
+
// referenceBlock reorg check, in-transaction, against the same db the log primary maps live on. The
|
|
213
|
+
// genesis block is a valid anchor during early sync but is synthetic and never indexed in the block
|
|
214
|
+
// store, so resolve it directly to the genesis block number rather than mistaking it for a reorg.
|
|
215
|
+
let referenceBlockNumber: number | undefined;
|
|
216
|
+
if (query.referenceBlock) {
|
|
217
|
+
if (query.referenceBlock.equals(this.genesisBlockHash)) {
|
|
218
|
+
referenceBlockNumber = INITIAL_L2_BLOCK_NUM - 1;
|
|
219
|
+
} else {
|
|
220
|
+
const refBlk = await this.blockStore.getBlockData({ hash: query.referenceBlock });
|
|
221
|
+
if (!refBlk) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Reference block ${query.referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
referenceBlockNumber = refBlk.header.globalVariables.blockNumber;
|
|
227
|
+
}
|
|
381
228
|
}
|
|
382
229
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
230
|
+
// Compute the exclusive upper-block bound across `toBlock` and `referenceBlock`.
|
|
231
|
+
// `toBlock` is already exclusive; `referenceBlock` caps inclusively, so its exclusive form is +1.
|
|
232
|
+
let upperExclusive: number | undefined;
|
|
233
|
+
if (query.toBlock !== undefined) {
|
|
234
|
+
upperExclusive = query.toBlock;
|
|
235
|
+
}
|
|
236
|
+
if (referenceBlockNumber !== undefined) {
|
|
237
|
+
const refExclusive = referenceBlockNumber + 1;
|
|
238
|
+
upperExclusive = upperExclusive === undefined ? refExclusive : Math.min(upperExclusive, refExclusive);
|
|
386
239
|
}
|
|
387
240
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
for (let i = 0; i < numLogsInTx; i++) {
|
|
399
|
-
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
241
|
+
// Resolve txHash -> (blockNumber, txIndexInBlock) once for the whole query.
|
|
242
|
+
let txLocation: [number, number] | undefined;
|
|
243
|
+
if (query.txHash) {
|
|
244
|
+
const loc = await this.blockStore.getTxLocation(query.txHash);
|
|
245
|
+
if (!loc) {
|
|
246
|
+
return tags.map(() => []);
|
|
247
|
+
}
|
|
248
|
+
txLocation = loc;
|
|
249
|
+
if (upperExclusive !== undefined && txLocation[0] >= upperExclusive) {
|
|
250
|
+
return tags.map(() => []);
|
|
400
251
|
}
|
|
401
252
|
}
|
|
402
253
|
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
254
|
+
const fromBlock = query.fromBlock ?? INITIAL_L2_BLOCK_NUM;
|
|
255
|
+
const includeEffects = query.includeEffects === true;
|
|
256
|
+
|
|
257
|
+
const perTagResults: LogResult[][] = [];
|
|
258
|
+
for (const tagEntry of tags) {
|
|
259
|
+
const { tagHex, afterLog } = normalizeTagEntry(tagEntry);
|
|
260
|
+
const prefix = contractHex !== undefined ? encodePublicPrefix(contractHex, tagHex) : tagHex;
|
|
261
|
+
|
|
262
|
+
const end = txLocation
|
|
263
|
+
? endOfTxRange(prefix, txLocation[0], txLocation[1])
|
|
264
|
+
: endOfTagRange(prefix, upperExclusive);
|
|
265
|
+
|
|
266
|
+
let start: string;
|
|
267
|
+
if (afterLog) {
|
|
268
|
+
// Cursor wins as the start; `fromBlock` is ignored (fine if the cursor sits below it). The cursor
|
|
269
|
+
// carries `(blockNumber, txIndexWithinBlock, logIndexWithinTx)`, which slot directly into the
|
|
270
|
+
// composite key — no tx-hash lookup needed.
|
|
271
|
+
start = incKey(encodeKey(prefix, afterLog.blockNumber, afterLog.txIndexWithinBlock, afterLog.logIndexWithinTx));
|
|
272
|
+
} else if (txLocation) {
|
|
273
|
+
start = encodeKey(prefix, txLocation[0], txLocation[1], 0);
|
|
274
|
+
} else {
|
|
275
|
+
start = encodeKey(prefix, fromBlock, 0, 0);
|
|
276
|
+
}
|
|
415
277
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
278
|
+
const limit = query.limitPerTag ?? MAX_LOGS_PER_TAG;
|
|
279
|
+
const out: LogResult[] = [];
|
|
280
|
+
for await (const [rawKey, rawVal] of primaryMap.entriesAsync({ start, end, limit })) {
|
|
281
|
+
const tail = decodeKeyTail(rawKey);
|
|
282
|
+
const value = decodeValue(rawVal);
|
|
283
|
+
out.push({
|
|
284
|
+
logData: value.logData,
|
|
285
|
+
blockNumber: tail.blockNumber,
|
|
286
|
+
blockHash: value.blockHash,
|
|
287
|
+
blockTimestamp: value.blockTimestamp,
|
|
288
|
+
txHash: value.txHash,
|
|
289
|
+
txIndexWithinBlock: tail.txIndexWithinBlock,
|
|
290
|
+
logIndexWithinTx: tail.logIndexWithinTx,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
perTagResults.push(out);
|
|
421
294
|
}
|
|
422
295
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const blockHash = this.#unpackBlockHash(reader);
|
|
431
|
-
|
|
432
|
-
while (reader.remainingBytes() > 0) {
|
|
433
|
-
const indexOfTx = reader.readNumber();
|
|
434
|
-
const numLogsInTx = reader.readNumber();
|
|
435
|
-
publicLogsInBlock[indexOfTx] = [];
|
|
436
|
-
for (let i = 0; i < numLogsInTx; i++) {
|
|
437
|
-
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
296
|
+
if (includeEffects) {
|
|
297
|
+
// Dedupe by txHash across the entire page so a tx with many tagged logs costs one fetch.
|
|
298
|
+
const txHashByKey = new Map<string, TxHash>();
|
|
299
|
+
for (const arr of perTagResults) {
|
|
300
|
+
for (const log of arr) {
|
|
301
|
+
txHashByKey.set(log.txHash.toString(), log.txHash);
|
|
438
302
|
}
|
|
439
303
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
304
|
+
const uniqueTxs = Array.from(txHashByKey.values());
|
|
305
|
+
if (uniqueTxs.length > 0) {
|
|
306
|
+
const effects = await this.blockStore.getNoteHashesAndNullifiers(uniqueTxs);
|
|
307
|
+
const byTxHash = new Map<string, [Fr[], Fr[]]>();
|
|
308
|
+
uniqueTxs.forEach((tx, i) => byTxHash.set(tx.toString(), effects[i]));
|
|
309
|
+
for (let i = 0; i < perTagResults.length; i++) {
|
|
310
|
+
perTagResults[i] = perTagResults[i].map(log => {
|
|
311
|
+
const [noteHashes, nullifiers] = byTxHash.get(log.txHash.toString()) ?? [[], []];
|
|
312
|
+
return { ...log, noteHashes, nullifiers };
|
|
313
|
+
});
|
|
446
314
|
}
|
|
447
315
|
}
|
|
448
316
|
}
|
|
449
317
|
|
|
450
|
-
return
|
|
318
|
+
return perTagResults;
|
|
451
319
|
}
|
|
452
320
|
|
|
453
321
|
/**
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
322
|
+
* Reads back every private log indexed for the given block via the per-block secondary index. Order
|
|
323
|
+
* matches the canonical composite-key order (`tag`, `blockNumber`, `txIndexWithinBlock`,
|
|
324
|
+
* `logIndexWithinTx`). Used by the data-store-updater test suite to verify the indexed-vs-block-body
|
|
325
|
+
* counts without depending on the removed `getPublicLogs(LogFilter)` API.
|
|
457
326
|
*/
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return this.#filterContractClassLogsOfTx(filter);
|
|
463
|
-
} else {
|
|
464
|
-
return this.#filterContractClassLogsBetweenBlocks(filter);
|
|
465
|
-
}
|
|
327
|
+
getPrivateLogsForBlock(blockNumber: number): Promise<LogResult[]> {
|
|
328
|
+
return this.db.transactionAsync(() =>
|
|
329
|
+
this.#readBlockLogs(this.#privateKeysByBlock, this.#privateLogs, blockNumber),
|
|
330
|
+
);
|
|
466
331
|
}
|
|
467
332
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const [blockNumber, txIndex] = (await this.blockStore.getTxLocation(filter.txHash)) ?? [];
|
|
474
|
-
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
475
|
-
return { logs: [], maxLogsHit: false };
|
|
476
|
-
}
|
|
477
|
-
const contractClassLogsBuffer = (await this.#contractClassLogsByBlock.getAsync(blockNumber)) ?? Buffer.alloc(0);
|
|
478
|
-
const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
|
|
479
|
-
|
|
480
|
-
const reader = new BufferReader(contractClassLogsBuffer);
|
|
481
|
-
const blockHash = this.#unpackBlockHash(reader);
|
|
482
|
-
|
|
483
|
-
while (reader.remainingBytes() > 0) {
|
|
484
|
-
const indexOfTx = reader.readNumber();
|
|
485
|
-
const numLogsInTx = reader.readNumber();
|
|
486
|
-
contractClassLogsInBlock[indexOfTx] = [];
|
|
487
|
-
for (let i = 0; i < numLogsInTx; i++) {
|
|
488
|
-
contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const txLogs = contractClassLogsInBlock[txIndex];
|
|
493
|
-
|
|
494
|
-
const logs: ExtendedContractClassLog[] = [];
|
|
495
|
-
const maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
|
|
496
|
-
|
|
497
|
-
return { logs, maxLogsHit };
|
|
333
|
+
/** {@inheritDoc LogStore.getPrivateLogsForBlock} */
|
|
334
|
+
getPublicLogsForBlock(blockNumber: number): Promise<LogResult[]> {
|
|
335
|
+
return this.db.transactionAsync(() => this.#readBlockLogs(this.#publicKeysByBlock, this.#publicLogs, blockNumber));
|
|
498
336
|
}
|
|
499
337
|
|
|
500
|
-
async #
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
maxLogsHit: true,
|
|
509
|
-
};
|
|
338
|
+
async #readBlockLogs(
|
|
339
|
+
keysByBlock: AztecAsyncMap<number, string[]>,
|
|
340
|
+
primaryMap: AztecAsyncMap<string, Buffer>,
|
|
341
|
+
blockNumber: number,
|
|
342
|
+
): Promise<LogResult[]> {
|
|
343
|
+
const keys = await keysByBlock.getAsync(blockNumber);
|
|
344
|
+
if (!keys || keys.length === 0) {
|
|
345
|
+
return [];
|
|
510
346
|
}
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
start,
|
|
517
|
-
end,
|
|
518
|
-
})) {
|
|
519
|
-
const contractClassLogsInBlock: [ContractClassLog[]] = [[]];
|
|
520
|
-
const reader = new BufferReader(logBuffer);
|
|
521
|
-
const blockHash = this.#unpackBlockHash(reader);
|
|
522
|
-
while (reader.remainingBytes() > 0) {
|
|
523
|
-
const indexOfTx = reader.readNumber();
|
|
524
|
-
const numLogsInTx = reader.readNumber();
|
|
525
|
-
contractClassLogsInBlock[indexOfTx] = [];
|
|
526
|
-
for (let i = 0; i < numLogsInTx; i++) {
|
|
527
|
-
contractClassLogsInBlock[indexOfTx].push(reader.readObject(ContractClassLog));
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < contractClassLogsInBlock.length; txIndex++) {
|
|
531
|
-
const txLogs = contractClassLogsInBlock[txIndex];
|
|
532
|
-
maxLogsHit = this.#accumulateLogs(logs, blockNumber, blockHash, txIndex, txLogs, filter);
|
|
533
|
-
if (maxLogsHit) {
|
|
534
|
-
this.#log.debug(`Max logs hit at block ${blockNumber}`);
|
|
535
|
-
break loopOverBlocks;
|
|
536
|
-
}
|
|
347
|
+
const results: LogResult[] = [];
|
|
348
|
+
for (const key of keys) {
|
|
349
|
+
const raw = await primaryMap.getAsync(key);
|
|
350
|
+
if (!raw) {
|
|
351
|
+
continue;
|
|
537
352
|
}
|
|
353
|
+
const tail = decodeKeyTail(key);
|
|
354
|
+
const value = decodeValue(raw);
|
|
355
|
+
results.push({
|
|
356
|
+
logData: value.logData,
|
|
357
|
+
blockNumber: tail.blockNumber,
|
|
358
|
+
blockHash: value.blockHash,
|
|
359
|
+
blockTimestamp: value.blockTimestamp,
|
|
360
|
+
txHash: value.txHash,
|
|
361
|
+
txIndexWithinBlock: tail.txIndexWithinBlock,
|
|
362
|
+
logIndexWithinTx: tail.logIndexWithinTx,
|
|
363
|
+
});
|
|
538
364
|
}
|
|
539
|
-
|
|
540
|
-
return { logs, maxLogsHit };
|
|
365
|
+
return results;
|
|
541
366
|
}
|
|
367
|
+
}
|
|
542
368
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
)
|
|
551
|
-
|
|
552
|
-
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
553
|
-
for (; logIndex < txLogs.length; logIndex++) {
|
|
554
|
-
const log = txLogs[logIndex];
|
|
555
|
-
if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
|
|
556
|
-
if (log instanceof ContractClassLog) {
|
|
557
|
-
results.push(
|
|
558
|
-
new ExtendedContractClassLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log),
|
|
559
|
-
);
|
|
560
|
-
} else if (log instanceof PublicLog) {
|
|
561
|
-
results.push(new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txIndex, logIndex), log));
|
|
562
|
-
} else {
|
|
563
|
-
throw new Error('Unknown log type');
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
if (results.length >= this.#logsMaxPageSize) {
|
|
567
|
-
maxLogsHit = true;
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return maxLogsHit;
|
|
369
|
+
/** Pulls `{ tagHex, afterLog }` out of a {@link TagQuery}, normalizing the bare-tag form. */
|
|
370
|
+
function normalizeTagEntry<T extends Tag | SiloedTag>(
|
|
371
|
+
entry: TagQuery<T>,
|
|
372
|
+
): {
|
|
373
|
+
tagHex: string;
|
|
374
|
+
afterLog: LogCursor | undefined;
|
|
375
|
+
} {
|
|
376
|
+
if (typeof entry === 'object' && entry !== null && 'tag' in entry) {
|
|
377
|
+
return { tagHex: fieldHex(entry.tag.value), afterLog: entry.afterLog };
|
|
574
378
|
}
|
|
379
|
+
return { tagHex: fieldHex((entry as T).value), afterLog: undefined };
|
|
575
380
|
}
|