@aztec/archiver 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2
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/archiver/archiver.js +729 -711
- package/dest/archiver/archiver_store.js +4 -2
- package/dest/archiver/archiver_store_test_suite.js +385 -224
- package/dest/archiver/config.js +9 -11
- package/dest/archiver/data_retrieval.js +78 -68
- package/dest/archiver/errors.js +1 -2
- package/dest/archiver/index.js +0 -1
- package/dest/archiver/instrumentation.js +29 -17
- package/dest/archiver/kv_archiver_store/block_store.js +118 -127
- package/dest/archiver/kv_archiver_store/contract_class_store.js +43 -37
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +7 -12
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +135 -158
- package/dest/archiver/kv_archiver_store/log_store.js +271 -256
- package/dest/archiver/kv_archiver_store/message_store.js +42 -48
- package/dest/archiver/kv_archiver_store/nullifier_store.js +35 -42
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +15 -24
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +227 -231
- package/dest/archiver/structs/data_retrieval.js +5 -2
- package/dest/archiver/structs/published.js +1 -2
- package/dest/factory.js +30 -20
- package/dest/index.js +0 -1
- package/dest/rpc/index.js +5 -2
- package/dest/test/index.js +0 -1
- package/dest/test/mock_archiver.js +8 -13
- package/dest/test/mock_l1_to_l2_message_source.js +4 -4
- package/dest/test/mock_l2_block_source.js +68 -67
- package/package.json +14 -13
- package/src/archiver/data_retrieval.ts +1 -1
- package/src/archiver/index.ts +1 -1
- package/dest/archiver/archiver.d.ts +0 -186
- package/dest/archiver/archiver.d.ts.map +0 -1
- package/dest/archiver/archiver_store.d.ts +0 -217
- package/dest/archiver/archiver_store.d.ts.map +0 -1
- package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
- package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
- package/dest/archiver/config.d.ts +0 -35
- package/dest/archiver/config.d.ts.map +0 -1
- package/dest/archiver/data_retrieval.d.ts +0 -71
- package/dest/archiver/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/errors.d.ts +0 -4
- package/dest/archiver/errors.d.ts.map +0 -1
- package/dest/archiver/index.d.ts +0 -8
- package/dest/archiver/index.d.ts.map +0 -1
- package/dest/archiver/instrumentation.d.ts +0 -27
- package/dest/archiver/instrumentation.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +0 -87
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +0 -17
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +0 -13
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +0 -147
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/log_store.d.ts +0 -47
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/message_store.d.ts +0 -33
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +0 -12
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +0 -168
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +0 -1
- package/dest/archiver/structs/data_retrieval.d.ts +0 -27
- package/dest/archiver/structs/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/structs/published.d.ts +0 -11
- package/dest/archiver/structs/published.d.ts.map +0 -1
- package/dest/factory.d.ts +0 -10
- package/dest/factory.d.ts.map +0 -1
- package/dest/index.d.ts +0 -5
- package/dest/index.d.ts.map +0 -1
- package/dest/rpc/index.d.ts +0 -4
- package/dest/rpc/index.d.ts.map +0 -1
- package/dest/test/index.d.ts +0 -4
- package/dest/test/index.d.ts.map +0 -1
- package/dest/test/mock_archiver.d.ts +0 -22
- package/dest/test/mock_archiver.d.ts.map +0 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +0 -16
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +0 -1
- package/dest/test/mock_l2_block_source.d.ts +0 -80
- package/dest/test/mock_l2_block_source.d.ts.map +0 -1
|
@@ -1,323 +1,338 @@
|
|
|
1
|
-
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
|
-
import { ContractClass2BlockL2Logs, ExtendedPublicLog, ExtendedUnencryptedL2Log, LogId, TxScopedL2Log, UnencryptedL2Log, } from '@aztec/circuit-types';
|
|
1
|
+
import { ContractClass2BlockL2Logs, ExtendedPublicLog, ExtendedUnencryptedL2Log, LogId, TxScopedL2Log, UnencryptedL2Log } from '@aztec/circuit-types';
|
|
4
2
|
import { PrivateLog, PublicLog } from '@aztec/circuits.js';
|
|
5
|
-
import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, PUBLIC_LOG_DATA_SIZE_IN_FIELDS
|
|
3
|
+
import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, PUBLIC_LOG_DATA_SIZE_IN_FIELDS } from '@aztec/circuits.js/constants';
|
|
6
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
5
|
import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
|
|
8
6
|
/**
|
|
9
7
|
* A store for logs
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
*/ export class LogStore {
|
|
9
|
+
db;
|
|
10
|
+
blockStore;
|
|
11
|
+
#logsByTag;
|
|
12
|
+
#logTagsByBlock;
|
|
13
|
+
#privateLogsByBlock;
|
|
14
|
+
#publicLogsByBlock;
|
|
15
|
+
#contractClassLogsByBlock;
|
|
16
|
+
#logsMaxPageSize;
|
|
17
|
+
#log;
|
|
18
|
+
constructor(db, blockStore, logsMaxPageSize = 1000){
|
|
14
19
|
this.db = db;
|
|
15
20
|
this.blockStore = blockStore;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
this.#log = createLogger('archiver:log_store');
|
|
22
|
+
this.#logsByTag = db.openMap('archiver_tagged_logs_by_tag');
|
|
23
|
+
this.#logTagsByBlock = db.openMap('archiver_log_tags_by_block');
|
|
24
|
+
this.#privateLogsByBlock = db.openMap('archiver_private_logs_by_block');
|
|
25
|
+
this.#publicLogsByBlock = db.openMap('archiver_public_logs_by_block');
|
|
26
|
+
this.#contractClassLogsByBlock = db.openMap('archiver_contract_class_logs_by_block');
|
|
27
|
+
this.#logsMaxPageSize = logsMaxPageSize;
|
|
28
|
+
}
|
|
29
|
+
#extractTaggedLogsFromPrivate(block) {
|
|
30
|
+
const taggedLogs = new Map();
|
|
31
|
+
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
32
|
+
block.body.txEffects.forEach((txEffect, txIndex)=>{
|
|
33
|
+
const txHash = txEffect.txHash;
|
|
34
|
+
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
35
|
+
txEffect.privateLogs.forEach((log)=>{
|
|
36
|
+
const tag = log.fields[0];
|
|
37
|
+
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
38
|
+
currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ false, log.toBuffer()).toBuffer());
|
|
39
|
+
taggedLogs.set(tag.toString(), currentLogs);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return taggedLogs;
|
|
43
|
+
}
|
|
44
|
+
#extractTaggedLogsFromPublic(block) {
|
|
45
|
+
const taggedLogs = new Map();
|
|
46
|
+
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
47
|
+
block.body.txEffects.forEach((txEffect, txIndex)=>{
|
|
48
|
+
const txHash = txEffect.txHash;
|
|
49
|
+
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
50
|
+
txEffect.publicLogs.forEach((log)=>{
|
|
51
|
+
// Check that each log stores 2 lengths in its first field. If not, it's not a tagged log:
|
|
52
|
+
const firstFieldBuf = log.log[0].toBuffer();
|
|
53
|
+
// See macros/note/mod/ and see how finalization_log[0] is constructed, to understand this monstrosity. (It wasn't me).
|
|
54
|
+
// Search the codebase for "disgusting encoding" to see other hardcoded instances of this encoding, that you might need to change if you ever find yourself here.
|
|
55
|
+
if (!firstFieldBuf.subarray(0, 27).equals(Buffer.alloc(27)) || firstFieldBuf[29] !== 0) {
|
|
56
|
+
// See parseLogFromPublic - the first field of a tagged log is 5 bytes structured:
|
|
57
|
+
// [ publicLen[0], publicLen[1], 0, privateLen[0], privateLen[1]]
|
|
58
|
+
this.#log.warn(`Skipping public log with invalid first field: ${log.log[0]}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Check that the length values line up with the log contents
|
|
62
|
+
const publicValuesLength = firstFieldBuf.subarray(-5).readUint16BE();
|
|
63
|
+
const privateValuesLength = firstFieldBuf.subarray(-5).readUint16BE(3);
|
|
64
|
+
// Add 1 for the first field holding lengths
|
|
65
|
+
const totalLogLength = 1 + publicValuesLength + privateValuesLength;
|
|
66
|
+
// Note that zeroes can be valid log values, so we can only assert that we do not go over the given length
|
|
67
|
+
if (totalLogLength > PUBLIC_LOG_DATA_SIZE_IN_FIELDS || log.log.slice(totalLogLength).find((f)=>!f.isZero())) {
|
|
68
|
+
this.#log.warn(`Skipping invalid tagged public log with first field: ${log.log[0]}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// The first elt stores lengths as above => tag is in fields[1]
|
|
72
|
+
const tag = log.log[1];
|
|
73
|
+
this.#log.debug(`Found tagged public log with tag ${tag.toString()} in block ${block.number}`);
|
|
74
|
+
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
75
|
+
currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ true, log.toBuffer()).toBuffer());
|
|
76
|
+
taggedLogs.set(tag.toString(), currentLogs);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
return taggedLogs;
|
|
29
80
|
}
|
|
30
81
|
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.reduce((acc, val)
|
|
39
|
-
for (const [tag, logs] of val.entries())
|
|
82
|
+
* Append new logs to the store's list.
|
|
83
|
+
* @param blocks - The blocks for which to add the logs.
|
|
84
|
+
* @returns True if the operation is successful.
|
|
85
|
+
*/ addLogs(blocks) {
|
|
86
|
+
const taggedLogsToAdd = blocks.flatMap((block)=>[
|
|
87
|
+
this.#extractTaggedLogsFromPrivate(block),
|
|
88
|
+
this.#extractTaggedLogsFromPublic(block)
|
|
89
|
+
]).reduce((acc, val)=>{
|
|
90
|
+
for (const [tag, logs] of val.entries()){
|
|
40
91
|
const currentLogs = acc.get(tag) ?? [];
|
|
41
92
|
acc.set(tag, currentLogs.concat(logs));
|
|
42
93
|
}
|
|
43
94
|
return acc;
|
|
44
95
|
});
|
|
45
96
|
const tagsToUpdate = Array.from(taggedLogsToAdd.keys());
|
|
46
|
-
return this.db.transactionAsync(async ()
|
|
47
|
-
const currentTaggedLogs = await Promise.all(tagsToUpdate.map(async (tag)
|
|
48
|
-
|
|
97
|
+
return this.db.transactionAsync(async ()=>{
|
|
98
|
+
const currentTaggedLogs = await Promise.all(tagsToUpdate.map(async (tag)=>({
|
|
99
|
+
tag,
|
|
100
|
+
logBuffers: await this.#logsByTag.getAsync(tag)
|
|
101
|
+
})));
|
|
102
|
+
currentTaggedLogs.forEach((taggedLogBuffer)=>{
|
|
49
103
|
if (taggedLogBuffer.logBuffers && taggedLogBuffer.logBuffers.length > 0) {
|
|
50
104
|
taggedLogsToAdd.set(taggedLogBuffer.tag, taggedLogBuffer.logBuffers.concat(taggedLogsToAdd.get(taggedLogBuffer.tag)));
|
|
51
105
|
}
|
|
52
106
|
});
|
|
53
|
-
for (const block of blocks)
|
|
107
|
+
for (const block of blocks){
|
|
54
108
|
const tagsInBlock = [];
|
|
55
|
-
for (const [tag, logs] of taggedLogsToAdd.entries())
|
|
56
|
-
await
|
|
109
|
+
for (const [tag, logs] of taggedLogsToAdd.entries()){
|
|
110
|
+
await this.#logsByTag.set(tag, logs);
|
|
57
111
|
tagsInBlock.push(tag);
|
|
58
112
|
}
|
|
59
|
-
await
|
|
60
|
-
const privateLogsInBlock = block.body.txEffects
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
txEffect.publicLogs.map(log => log.toBuffer()),
|
|
70
|
-
].flat())
|
|
71
|
-
.flat();
|
|
72
|
-
await __classPrivateFieldGet(this, _LogStore_publicLogsByBlock, "f").set(block.number, Buffer.concat(publicLogsInBlock));
|
|
73
|
-
await __classPrivateFieldGet(this, _LogStore_contractClassLogsByBlock, "f").set(block.number, block.body.contractClassLogs.toBuffer());
|
|
113
|
+
await this.#logTagsByBlock.set(block.number, tagsInBlock);
|
|
114
|
+
const privateLogsInBlock = block.body.txEffects.map((txEffect)=>txEffect.privateLogs).flat().map((log)=>log.toBuffer());
|
|
115
|
+
await this.#privateLogsByBlock.set(block.number, Buffer.concat(privateLogsInBlock));
|
|
116
|
+
const publicLogsInBlock = block.body.txEffects.map((txEffect, txIndex)=>[
|
|
117
|
+
numToUInt32BE(txIndex),
|
|
118
|
+
numToUInt32BE(txEffect.publicLogs.length),
|
|
119
|
+
txEffect.publicLogs.map((log)=>log.toBuffer())
|
|
120
|
+
].flat()).flat();
|
|
121
|
+
await this.#publicLogsByBlock.set(block.number, Buffer.concat(publicLogsInBlock));
|
|
122
|
+
await this.#contractClassLogsByBlock.set(block.number, block.body.contractClassLogs.toBuffer());
|
|
74
123
|
}
|
|
75
124
|
return true;
|
|
76
125
|
});
|
|
77
126
|
}
|
|
78
127
|
deleteLogs(blocks) {
|
|
79
|
-
return this.db.transactionAsync(async ()
|
|
80
|
-
const tagsToDelete = (await Promise.all(blocks.map(async (block)
|
|
81
|
-
const tags = await
|
|
128
|
+
return this.db.transactionAsync(async ()=>{
|
|
129
|
+
const tagsToDelete = (await Promise.all(blocks.map(async (block)=>{
|
|
130
|
+
const tags = await this.#logTagsByBlock.getAsync(block.number);
|
|
82
131
|
return tags ?? [];
|
|
83
132
|
}))).flat();
|
|
84
|
-
await Promise.all(blocks.map(block
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await Promise.all(tagsToDelete.map(tag
|
|
133
|
+
await Promise.all(blocks.map((block)=>Promise.all([
|
|
134
|
+
this.#privateLogsByBlock.delete(block.number),
|
|
135
|
+
this.#publicLogsByBlock.delete(block.number),
|
|
136
|
+
this.#logTagsByBlock.delete(block.number)
|
|
137
|
+
])));
|
|
138
|
+
await Promise.all(tagsToDelete.map((tag)=>this.#logsByTag.delete(tag.toString())));
|
|
90
139
|
return true;
|
|
91
140
|
});
|
|
92
141
|
}
|
|
93
142
|
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
async getPrivateLogs(start, limit) {
|
|
143
|
+
* Retrieves all private logs from up to `limit` blocks, starting from the block number `start`.
|
|
144
|
+
* @param start - The block number from which to begin retrieving logs.
|
|
145
|
+
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
146
|
+
* @returns An array of private logs from the specified range of blocks.
|
|
147
|
+
*/ async getPrivateLogs(start, limit) {
|
|
100
148
|
const logs = [];
|
|
101
|
-
for await (const buffer of
|
|
149
|
+
for await (const buffer of this.#privateLogsByBlock.valuesAsync({
|
|
150
|
+
start,
|
|
151
|
+
limit
|
|
152
|
+
})){
|
|
102
153
|
const reader = new BufferReader(buffer);
|
|
103
|
-
while
|
|
154
|
+
while(reader.remainingBytes() > 0){
|
|
104
155
|
logs.push(reader.readObject(PrivateLog));
|
|
105
156
|
}
|
|
106
157
|
}
|
|
107
158
|
return logs;
|
|
108
159
|
}
|
|
109
160
|
/**
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return logs.map(noteLogBuffers => noteLogBuffers?.map(noteLogBuffer => TxScopedL2Log.fromBuffer(noteLogBuffer)) ?? []);
|
|
161
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
162
|
+
* @param tags - The tags to filter the logs by.
|
|
163
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
164
|
+
* that tag.
|
|
165
|
+
*/ async getLogsByTags(tags) {
|
|
166
|
+
const logs = await Promise.all(tags.map((tag)=>this.#logsByTag.getAsync(tag.toString())));
|
|
167
|
+
return logs.map((noteLogBuffers)=>noteLogBuffers?.map((noteLogBuffer)=>TxScopedL2Log.fromBuffer(noteLogBuffer)) ?? []);
|
|
118
168
|
}
|
|
119
169
|
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
getPublicLogs(filter) {
|
|
170
|
+
* Gets public logs based on the provided filter.
|
|
171
|
+
* @param filter - The filter to apply to the logs.
|
|
172
|
+
* @returns The requested logs.
|
|
173
|
+
*/ getPublicLogs(filter) {
|
|
125
174
|
if (filter.afterLog) {
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
else {
|
|
132
|
-
return __classPrivateFieldGet(this, _LogStore_instances, "m", _LogStore_filterPublicLogsBetweenBlocks).call(this, filter);
|
|
175
|
+
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
176
|
+
} else if (filter.txHash) {
|
|
177
|
+
return this.#filterPublicLogsOfTx(filter);
|
|
178
|
+
} else {
|
|
179
|
+
return this.#filterPublicLogsBetweenBlocks(filter);
|
|
133
180
|
}
|
|
134
181
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* @returns The requested logs.
|
|
139
|
-
*/
|
|
140
|
-
getContractClassLogs(filter) {
|
|
141
|
-
if (filter.afterLog) {
|
|
142
|
-
return __classPrivateFieldGet(this, _LogStore_instances, "m", _LogStore_filterContractClassLogsBetweenBlocks).call(this, filter);
|
|
143
|
-
}
|
|
144
|
-
else if (filter.txHash) {
|
|
145
|
-
return __classPrivateFieldGet(this, _LogStore_instances, "m", _LogStore_filterContractClassLogsOfTx).call(this, filter);
|
|
182
|
+
async #filterPublicLogsOfTx(filter) {
|
|
183
|
+
if (!filter.txHash) {
|
|
184
|
+
throw new Error('Missing txHash');
|
|
146
185
|
}
|
|
147
|
-
|
|
148
|
-
|
|
186
|
+
const [blockNumber, txIndex] = await this.blockStore.getTxLocation(filter.txHash) ?? [];
|
|
187
|
+
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
188
|
+
return {
|
|
189
|
+
logs: [],
|
|
190
|
+
maxLogsHit: false
|
|
191
|
+
};
|
|
149
192
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
157
|
-
const txHash = txEffect.txHash;
|
|
158
|
-
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
159
|
-
txEffect.privateLogs.forEach(log => {
|
|
160
|
-
const tag = log.fields[0];
|
|
161
|
-
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
162
|
-
currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number,
|
|
163
|
-
/* isFromPublic */ false, log.toBuffer()).toBuffer());
|
|
164
|
-
taggedLogs.set(tag.toString(), currentLogs);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
return taggedLogs;
|
|
168
|
-
}, _LogStore_extractTaggedLogsFromPublic = function _LogStore_extractTaggedLogsFromPublic(block) {
|
|
169
|
-
const taggedLogs = new Map();
|
|
170
|
-
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
171
|
-
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
172
|
-
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
173
|
-
const txHash = txEffect.txHash;
|
|
174
|
-
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
175
|
-
txEffect.publicLogs.forEach(log => {
|
|
176
|
-
// Check that each log stores 2 lengths in its first field. If not, it's not a tagged log:
|
|
177
|
-
const firstFieldBuf = log.log[0].toBuffer();
|
|
178
|
-
// See macros/note/mod/ and see how finalization_log[0] is constructed, to understand this monstrosity. (It wasn't me).
|
|
179
|
-
// Search the codebase for "disgusting encoding" to see other hardcoded instances of this encoding, that you might need to change if you ever find yourself here.
|
|
180
|
-
if (!firstFieldBuf.subarray(0, 27).equals(Buffer.alloc(27)) || firstFieldBuf[29] !== 0) {
|
|
181
|
-
// See parseLogFromPublic - the first field of a tagged log is 5 bytes structured:
|
|
182
|
-
// [ publicLen[0], publicLen[1], 0, privateLen[0], privateLen[1]]
|
|
183
|
-
__classPrivateFieldGet(this, _LogStore_log, "f").warn(`Skipping public log with invalid first field: ${log.log[0]}`);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
// Check that the length values line up with the log contents
|
|
187
|
-
const publicValuesLength = firstFieldBuf.subarray(-5).readUint16BE();
|
|
188
|
-
const privateValuesLength = firstFieldBuf.subarray(-5).readUint16BE(3);
|
|
189
|
-
// Add 1 for the first field holding lengths
|
|
190
|
-
const totalLogLength = 1 + publicValuesLength + privateValuesLength;
|
|
191
|
-
// Note that zeroes can be valid log values, so we can only assert that we do not go over the given length
|
|
192
|
-
if (totalLogLength > PUBLIC_LOG_DATA_SIZE_IN_FIELDS || log.log.slice(totalLogLength).find(f => !f.isZero())) {
|
|
193
|
-
__classPrivateFieldGet(this, _LogStore_log, "f").warn(`Skipping invalid tagged public log with first field: ${log.log[0]}`);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
// The first elt stores lengths as above => tag is in fields[1]
|
|
197
|
-
const tag = log.log[1];
|
|
198
|
-
__classPrivateFieldGet(this, _LogStore_log, "f").debug(`Found tagged public log with tag ${tag.toString()} in block ${block.number}`);
|
|
199
|
-
const currentLogs = taggedLogs.get(tag.toString()) ?? [];
|
|
200
|
-
currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number,
|
|
201
|
-
/* isFromPublic */ true, log.toBuffer()).toBuffer());
|
|
202
|
-
taggedLogs.set(tag.toString(), currentLogs);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
return taggedLogs;
|
|
206
|
-
}, _LogStore_filterPublicLogsOfTx = async function _LogStore_filterPublicLogsOfTx(filter) {
|
|
207
|
-
if (!filter.txHash) {
|
|
208
|
-
throw new Error('Missing txHash');
|
|
209
|
-
}
|
|
210
|
-
const [blockNumber, txIndex] = (await this.blockStore.getTxLocation(filter.txHash)) ?? [];
|
|
211
|
-
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
212
|
-
return { logs: [], maxLogsHit: false };
|
|
213
|
-
}
|
|
214
|
-
const buffer = (await __classPrivateFieldGet(this, _LogStore_publicLogsByBlock, "f").getAsync(blockNumber)) ?? Buffer.alloc(0);
|
|
215
|
-
const publicLogsInBlock = [[]];
|
|
216
|
-
const reader = new BufferReader(buffer);
|
|
217
|
-
while (reader.remainingBytes() > 0) {
|
|
218
|
-
const indexOfTx = reader.readNumber();
|
|
219
|
-
const numLogsInTx = reader.readNumber();
|
|
220
|
-
publicLogsInBlock[indexOfTx] = [];
|
|
221
|
-
for (let i = 0; i < numLogsInTx; i++) {
|
|
222
|
-
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
const txLogs = publicLogsInBlock[txIndex];
|
|
226
|
-
const logs = [];
|
|
227
|
-
const maxLogsHit = __classPrivateFieldGet(this, _LogStore_instances, "m", _LogStore_accumulateLogs).call(this, logs, blockNumber, txIndex, txLogs, filter);
|
|
228
|
-
return { logs, maxLogsHit };
|
|
229
|
-
}, _LogStore_filterPublicLogsBetweenBlocks = async function _LogStore_filterPublicLogsBetweenBlocks(filter) {
|
|
230
|
-
const start = filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
231
|
-
const end = filter.toBlock;
|
|
232
|
-
if (typeof end === 'number' && end < start) {
|
|
233
|
-
return {
|
|
234
|
-
logs: [],
|
|
235
|
-
maxLogsHit: true,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const logs = [];
|
|
239
|
-
let maxLogsHit = false;
|
|
240
|
-
loopOverBlocks: for await (const [blockNumber, logBuffer] of __classPrivateFieldGet(this, _LogStore_publicLogsByBlock, "f").entriesAsync({ start, end })) {
|
|
241
|
-
const publicLogsInBlock = [[]];
|
|
242
|
-
const reader = new BufferReader(logBuffer);
|
|
243
|
-
while (reader.remainingBytes() > 0) {
|
|
193
|
+
const buffer = await this.#publicLogsByBlock.getAsync(blockNumber) ?? Buffer.alloc(0);
|
|
194
|
+
const publicLogsInBlock = [
|
|
195
|
+
[]
|
|
196
|
+
];
|
|
197
|
+
const reader = new BufferReader(buffer);
|
|
198
|
+
while(reader.remainingBytes() > 0){
|
|
244
199
|
const indexOfTx = reader.readNumber();
|
|
245
200
|
const numLogsInTx = reader.readNumber();
|
|
246
201
|
publicLogsInBlock[indexOfTx] = [];
|
|
247
|
-
for
|
|
202
|
+
for(let i = 0; i < numLogsInTx; i++){
|
|
248
203
|
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
249
204
|
}
|
|
250
205
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
206
|
+
const txLogs = publicLogsInBlock[txIndex];
|
|
207
|
+
const logs = [];
|
|
208
|
+
const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
209
|
+
return {
|
|
210
|
+
logs,
|
|
211
|
+
maxLogsHit
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
async #filterPublicLogsBetweenBlocks(filter) {
|
|
215
|
+
const start = filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
216
|
+
const end = filter.toBlock;
|
|
217
|
+
if (typeof end === 'number' && end < start) {
|
|
218
|
+
return {
|
|
219
|
+
logs: [],
|
|
220
|
+
maxLogsHit: true
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const logs = [];
|
|
224
|
+
let maxLogsHit = false;
|
|
225
|
+
loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#publicLogsByBlock.entriesAsync({
|
|
226
|
+
start,
|
|
227
|
+
end
|
|
228
|
+
})){
|
|
229
|
+
const publicLogsInBlock = [
|
|
230
|
+
[]
|
|
231
|
+
];
|
|
232
|
+
const reader = new BufferReader(logBuffer);
|
|
233
|
+
while(reader.remainingBytes() > 0){
|
|
234
|
+
const indexOfTx = reader.readNumber();
|
|
235
|
+
const numLogsInTx = reader.readNumber();
|
|
236
|
+
publicLogsInBlock[indexOfTx] = [];
|
|
237
|
+
for(let i = 0; i < numLogsInTx; i++){
|
|
238
|
+
publicLogsInBlock[indexOfTx].push(reader.readObject(PublicLog));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
for(let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < publicLogsInBlock.length; txIndex++){
|
|
242
|
+
const txLogs = publicLogsInBlock[txIndex];
|
|
243
|
+
maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
244
|
+
if (maxLogsHit) {
|
|
245
|
+
this.#log.debug(`Max logs hit at block ${blockNumber}`);
|
|
246
|
+
break loopOverBlocks;
|
|
247
|
+
}
|
|
257
248
|
}
|
|
258
249
|
}
|
|
250
|
+
return {
|
|
251
|
+
logs,
|
|
252
|
+
maxLogsHit
|
|
253
|
+
};
|
|
259
254
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Gets contract class logs based on the provided filter.
|
|
257
|
+
* @param filter - The filter to apply to the logs.
|
|
258
|
+
* @returns The requested logs.
|
|
259
|
+
*/ getContractClassLogs(filter) {
|
|
260
|
+
if (filter.afterLog) {
|
|
261
|
+
return this.#filterContractClassLogsBetweenBlocks(filter);
|
|
262
|
+
} else if (filter.txHash) {
|
|
263
|
+
return this.#filterContractClassLogsOfTx(filter);
|
|
264
|
+
} else {
|
|
265
|
+
return this.#filterContractClassLogsBetweenBlocks(filter);
|
|
266
|
+
}
|
|
268
267
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
async #filterContractClassLogsOfTx(filter) {
|
|
269
|
+
if (!filter.txHash) {
|
|
270
|
+
throw new Error('Missing txHash');
|
|
271
|
+
}
|
|
272
|
+
const [blockNumber, txIndex] = await this.blockStore.getTxLocation(filter.txHash) ?? [];
|
|
273
|
+
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
274
|
+
return {
|
|
275
|
+
logs: [],
|
|
276
|
+
maxLogsHit: false
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const contractClassLogsBuffer = await this.#contractClassLogsByBlock.getAsync(blockNumber);
|
|
280
|
+
const contractClassLogsInBlock = contractClassLogsBuffer ? ContractClass2BlockL2Logs.fromBuffer(contractClassLogsBuffer) : new ContractClass2BlockL2Logs([]);
|
|
281
|
+
const txLogs = contractClassLogsInBlock.txLogs[txIndex].unrollLogs();
|
|
282
|
+
const logs = [];
|
|
283
|
+
const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
281
284
|
return {
|
|
282
|
-
logs
|
|
283
|
-
maxLogsHit
|
|
285
|
+
logs,
|
|
286
|
+
maxLogsHit
|
|
284
287
|
};
|
|
285
288
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
start
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
289
|
+
async #filterContractClassLogsBetweenBlocks(filter) {
|
|
290
|
+
const start = filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
291
|
+
const end = filter.toBlock;
|
|
292
|
+
if (typeof end === 'number' && end < start) {
|
|
293
|
+
return {
|
|
294
|
+
logs: [],
|
|
295
|
+
maxLogsHit: true
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const logs = [];
|
|
299
|
+
let maxLogsHit = false;
|
|
300
|
+
loopOverBlocks: for await (const [blockNumber, logBuffer] of this.#contractClassLogsByBlock.entriesAsync({
|
|
301
|
+
start,
|
|
302
|
+
end
|
|
303
|
+
})){
|
|
304
|
+
const contractClassLogsInBlock = ContractClass2BlockL2Logs.fromBuffer(logBuffer);
|
|
305
|
+
for(let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < contractClassLogsInBlock.txLogs.length; txIndex++){
|
|
306
|
+
const txLogs = contractClassLogsInBlock.txLogs[txIndex].unrollLogs();
|
|
307
|
+
maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
308
|
+
if (maxLogsHit) {
|
|
309
|
+
this.#log.debug(`Max logs hit at block ${blockNumber}`);
|
|
310
|
+
break loopOverBlocks;
|
|
311
|
+
}
|
|
299
312
|
}
|
|
300
313
|
}
|
|
314
|
+
return {
|
|
315
|
+
logs,
|
|
316
|
+
maxLogsHit
|
|
317
|
+
};
|
|
301
318
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
results.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
break;
|
|
319
|
+
#accumulateLogs(results, blockNumber, txIndex, txLogs, filter) {
|
|
320
|
+
let maxLogsHit = false;
|
|
321
|
+
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
322
|
+
for(; logIndex < txLogs.length; logIndex++){
|
|
323
|
+
const log = txLogs[logIndex];
|
|
324
|
+
if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) {
|
|
325
|
+
if (log instanceof UnencryptedL2Log) {
|
|
326
|
+
results.push(new ExtendedUnencryptedL2Log(new LogId(blockNumber, txIndex, logIndex), log));
|
|
327
|
+
} else {
|
|
328
|
+
results.push(new ExtendedPublicLog(new LogId(blockNumber, txIndex, logIndex), log));
|
|
329
|
+
}
|
|
330
|
+
if (results.length >= this.#logsMaxPageSize) {
|
|
331
|
+
maxLogsHit = true;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
318
334
|
}
|
|
319
335
|
}
|
|
336
|
+
return maxLogsHit;
|
|
320
337
|
}
|
|
321
|
-
|
|
322
|
-
};
|
|
323
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nX3N0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FyY2hpdmVyL2t2X2FyY2hpdmVyX3N0b3JlL2xvZ19zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsaUJBQWlCLEVBQ2pCLHdCQUF3QixFQUt4QixLQUFLLEVBQ0wsYUFBYSxFQUNiLGdCQUFnQixHQUNqQixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFBVyxVQUFVLEVBQUUsU0FBUyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDcEUsT0FBTyxFQUNMLG9CQUFvQixFQUNwQixzQkFBc0IsRUFDdEIsOEJBQThCLEdBQy9CLE1BQU0sOEJBQThCLENBQUM7QUFDdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFLMUU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sUUFBUTtJQVNuQixZQUFvQixFQUFxQixFQUFVLFVBQXNCLEVBQUUsa0JBQTBCLElBQUk7O1FBQXJGLE9BQUUsR0FBRixFQUFFLENBQW1CO1FBQVUsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQVJ6RSxzQ0FBNEM7UUFDNUMsMkNBQWlEO1FBQ2pELCtDQUFtRDtRQUNuRCw4Q0FBa0Q7UUFDbEQscURBQXlEO1FBQ3pELDRDQUF5QjtRQUN6Qix3QkFBTyxZQUFZLENBQUMsb0JBQW9CLENBQUMsRUFBQztRQUd4Qyx1QkFBQSxJQUFJLHVCQUFjLEVBQUUsQ0FBQyxPQUFPLENBQUMsNkJBQTZCLENBQUMsTUFBQSxDQUFDO1FBQzVELHVCQUFBLElBQUksNEJBQW1CLEVBQUUsQ0FBQyxPQUFPLENBQUMsNEJBQTRCLENBQUMsTUFBQSxDQUFDO1FBQ2hFLHVCQUFBLElBQUksZ0NBQXVCLEVBQUUsQ0FBQyxPQUFPLENBQUMsZ0NBQWdDLENB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
|
|
338
|
+
}
|