@aztec/archiver 0.76.4 → 0.77.0-testnet-ignition.21
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 +1 -1
- package/dest/archiver/archiver.d.ts +22 -10
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +762 -713
- package/dest/archiver/archiver_store.d.ts +20 -7
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store.js +4 -2
- package/dest/archiver/archiver_store_test_suite.d.ts +2 -2
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +398 -227
- package/dest/archiver/config.d.ts +1 -1
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +10 -12
- package/dest/archiver/data_retrieval.d.ts +17 -14
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +90 -88
- package/dest/archiver/errors.js +1 -2
- package/dest/archiver/index.d.ts +1 -1
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/index.js +0 -1
- package/dest/archiver/instrumentation.d.ts +3 -1
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +37 -17
- package/dest/archiver/kv_archiver_store/block_store.d.ts +5 -3
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +125 -130
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +2 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +45 -37
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +10 -2
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +54 -15
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +16 -9
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +143 -160
- package/dest/archiver/kv_archiver_store/log_store.d.ts +5 -3
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +296 -255
- package/dest/archiver/kv_archiver_store/message_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +45 -50
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.js +36 -43
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +2 -2
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +17 -26
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +16 -7
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +287 -247
- package/dest/archiver/structs/data_retrieval.js +5 -2
- package/dest/archiver/structs/published.js +1 -2
- package/dest/factory.d.ts +20 -6
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +54 -30
- package/dest/index.js +0 -1
- package/dest/rpc/index.d.ts +2 -1
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +8 -4
- package/dest/test/index.js +0 -1
- package/dest/test/mock_archiver.d.ts +3 -2
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +8 -13
- package/dest/test/mock_l1_to_l2_message_source.d.ts +2 -2
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +4 -4
- package/dest/test/mock_l2_block_source.d.ts +5 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +71 -68
- package/package.json +15 -16
- package/src/archiver/archiver.ts +149 -89
- package/src/archiver/archiver_store.ts +27 -27
- package/src/archiver/archiver_store_test_suite.ts +22 -15
- package/src/archiver/config.ts +1 -1
- package/src/archiver/data_retrieval.ts +32 -44
- package/src/archiver/index.ts +1 -1
- package/src/archiver/instrumentation.ts +11 -1
- package/src/archiver/kv_archiver_store/block_store.ts +10 -4
- package/src/archiver/kv_archiver_store/contract_class_store.ts +9 -9
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +81 -3
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +44 -29
- package/src/archiver/kv_archiver_store/log_store.ts +56 -32
- package/src/archiver/kv_archiver_store/message_store.ts +4 -3
- package/src/archiver/kv_archiver_store/nullifier_store.ts +3 -2
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +3 -3
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +110 -57
- package/src/factory.ts +44 -25
- package/src/rpc/index.ts +2 -6
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l1_to_l2_message_source.ts +2 -2
- package/src/test/mock_l2_block_source.ts +16 -15
|
@@ -1,26 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { ExtendedPublicLog, ExtendedUnencryptedL2Log, L2BlockHash, LogId, TxReceipt, TxScopedL2Log, wrapInBlock, } from '@aztec/circuit-types';
|
|
4
|
-
import { Fr, INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, PUBLIC_LOG_DATA_SIZE_IN_FIELDS, } from '@aztec/circuits.js';
|
|
5
|
-
import { FunctionSelector } from '@aztec/foundation/abi';
|
|
1
|
+
import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, PUBLIC_LOG_DATA_SIZE_IN_FIELDS } from '@aztec/constants';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
6
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
5
|
+
import { L2BlockHash, wrapInBlock } from '@aztec/stdlib/block';
|
|
6
|
+
import { ExtendedContractClassLog, ExtendedPublicLog, LogId, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
7
|
+
import { TxReceipt } from '@aztec/stdlib/tx';
|
|
7
8
|
import { L1ToL2MessageStore } from './l1_to_l2_message_store.js';
|
|
8
9
|
/**
|
|
9
10
|
* Simple, in-memory implementation of an archiver data store.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
*/ export class MemoryArchiverStore {
|
|
12
|
+
maxLogs;
|
|
13
|
+
/**
|
|
14
|
+
* An array containing all the L2 blocks that have been fetched so far.
|
|
15
|
+
*/ l2Blocks;
|
|
16
|
+
/**
|
|
17
|
+
* An array containing all the tx effects in the L2 blocks that have been fetched so far.
|
|
18
|
+
*/ txEffects;
|
|
19
|
+
taggedLogs;
|
|
20
|
+
logTagsPerBlock;
|
|
21
|
+
privateLogsPerBlock;
|
|
22
|
+
publicLogsPerBlock;
|
|
23
|
+
contractClassLogsPerBlock;
|
|
24
|
+
blockScopedNullifiers;
|
|
25
|
+
/**
|
|
26
|
+
* Contains all L1 to L2 messages.
|
|
27
|
+
*/ l1ToL2Messages;
|
|
28
|
+
contractClasses;
|
|
29
|
+
bytecodeCommitments;
|
|
30
|
+
privateFunctions;
|
|
31
|
+
unconstrainedFunctions;
|
|
32
|
+
contractInstances;
|
|
33
|
+
contractInstanceUpdates;
|
|
34
|
+
lastL1BlockNewBlocks;
|
|
35
|
+
lastL1BlockNewMessages;
|
|
36
|
+
lastProvenL2BlockNumber;
|
|
37
|
+
lastProvenL2EpochNumber;
|
|
38
|
+
functionNames;
|
|
39
|
+
#log;
|
|
40
|
+
constructor(/** The max number of logs that can be obtained in 1 "getPublicLogs" call. */ maxLogs){
|
|
16
41
|
this.maxLogs = maxLogs;
|
|
17
|
-
/**
|
|
18
|
-
* An array containing all the L2 blocks that have been fetched so far.
|
|
19
|
-
*/
|
|
20
42
|
this.l2Blocks = [];
|
|
21
|
-
/**
|
|
22
|
-
* An array containing all the tx effects in the L2 blocks that have been fetched so far.
|
|
23
|
-
*/
|
|
24
43
|
this.txEffects = [];
|
|
25
44
|
this.taggedLogs = new Map();
|
|
26
45
|
this.logTagsPerBlock = new Map();
|
|
@@ -28,35 +47,49 @@ export class MemoryArchiverStore {
|
|
|
28
47
|
this.publicLogsPerBlock = new Map();
|
|
29
48
|
this.contractClassLogsPerBlock = new Map();
|
|
30
49
|
this.blockScopedNullifiers = new Map();
|
|
31
|
-
/**
|
|
32
|
-
* Contains all L1 to L2 messages.
|
|
33
|
-
*/
|
|
34
50
|
this.l1ToL2Messages = new L1ToL2MessageStore();
|
|
35
51
|
this.contractClasses = new Map();
|
|
36
52
|
this.bytecodeCommitments = new Map();
|
|
37
53
|
this.privateFunctions = new Map();
|
|
38
54
|
this.unconstrainedFunctions = new Map();
|
|
39
55
|
this.contractInstances = new Map();
|
|
56
|
+
this.contractInstanceUpdates = new Map();
|
|
40
57
|
this.lastL1BlockNewBlocks = undefined;
|
|
41
58
|
this.lastL1BlockNewMessages = undefined;
|
|
42
59
|
this.lastProvenL2BlockNumber = 0;
|
|
43
60
|
this.lastProvenL2EpochNumber = 0;
|
|
44
61
|
this.functionNames = new Map();
|
|
45
|
-
|
|
62
|
+
this.#log = createLogger('archiver:data-store');
|
|
46
63
|
}
|
|
47
64
|
getContractClass(id) {
|
|
48
65
|
const contractClass = this.contractClasses.get(id.toString());
|
|
49
66
|
return Promise.resolve(contractClass && {
|
|
50
67
|
...contractClass,
|
|
51
68
|
privateFunctions: this.privateFunctions.get(id.toString()) ?? [],
|
|
52
|
-
unconstrainedFunctions: this.unconstrainedFunctions.get(id.toString()) ?? []
|
|
69
|
+
unconstrainedFunctions: this.unconstrainedFunctions.get(id.toString()) ?? []
|
|
53
70
|
});
|
|
54
71
|
}
|
|
55
72
|
getContractClassIds() {
|
|
56
|
-
return Promise.resolve(Array.from(this.contractClasses.keys()).map(key
|
|
73
|
+
return Promise.resolve(Array.from(this.contractClasses.keys()).map((key)=>Fr.fromHexString(key)));
|
|
57
74
|
}
|
|
58
75
|
getContractInstance(address) {
|
|
59
|
-
|
|
76
|
+
const instance = this.contractInstances.get(address.toString());
|
|
77
|
+
if (!instance) {
|
|
78
|
+
return Promise.resolve(undefined);
|
|
79
|
+
}
|
|
80
|
+
const updates = this.contractInstanceUpdates.get(address.toString()) || [];
|
|
81
|
+
if (updates.length > 0) {
|
|
82
|
+
const lastUpdate = updates[0];
|
|
83
|
+
const currentBlockNumber = this.getLastBlockNumber();
|
|
84
|
+
if (currentBlockNumber >= lastUpdate.blockOfChange) {
|
|
85
|
+
instance.currentContractClassId = lastUpdate.newContractClassId;
|
|
86
|
+
} else if (!lastUpdate.prevContractClassId.isZero()) {
|
|
87
|
+
instance.currentContractClassId = lastUpdate.prevContractClassId;
|
|
88
|
+
} else {
|
|
89
|
+
instance.currentContractClassId = instance.originalContractClassId;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return Promise.resolve(instance);
|
|
60
93
|
}
|
|
61
94
|
getBytecodeCommitment(contractClassId) {
|
|
62
95
|
return Promise.resolve(this.bytecodeCommitments.get(contractClassId.toString()));
|
|
@@ -66,23 +99,23 @@ export class MemoryArchiverStore {
|
|
|
66
99
|
const unconstrainedFunctions = this.unconstrainedFunctions.get(contractClassId.toString()) ?? [];
|
|
67
100
|
const updatedPrivateFunctions = [
|
|
68
101
|
...privateFunctions,
|
|
69
|
-
...newPrivateFunctions.filter(newFn
|
|
102
|
+
...newPrivateFunctions.filter((newFn)=>!privateFunctions.find((f)=>f.selector.equals(newFn.selector)))
|
|
70
103
|
];
|
|
71
104
|
const updatedUnconstrainedFunctions = [
|
|
72
105
|
...unconstrainedFunctions,
|
|
73
|
-
...newUnconstrainedFunctions.filter(newFn
|
|
106
|
+
...newUnconstrainedFunctions.filter((newFn)=>!unconstrainedFunctions.find((f)=>f.selector.equals(newFn.selector)))
|
|
74
107
|
];
|
|
75
108
|
this.privateFunctions.set(contractClassId.toString(), updatedPrivateFunctions);
|
|
76
109
|
this.unconstrainedFunctions.set(contractClassId.toString(), updatedUnconstrainedFunctions);
|
|
77
110
|
return Promise.resolve(true);
|
|
78
111
|
}
|
|
79
112
|
addContractClasses(data, bytecodeCommitments, blockNumber) {
|
|
80
|
-
for
|
|
113
|
+
for(let i = 0; i < data.length; i++){
|
|
81
114
|
const contractClass = data[i];
|
|
82
115
|
if (!this.contractClasses.has(contractClass.id.toString())) {
|
|
83
116
|
this.contractClasses.set(contractClass.id.toString(), {
|
|
84
117
|
...contractClass,
|
|
85
|
-
l2BlockNumber: blockNumber
|
|
118
|
+
l2BlockNumber: blockNumber
|
|
86
119
|
});
|
|
87
120
|
}
|
|
88
121
|
if (!this.bytecodeCommitments.has(contractClass.id.toString())) {
|
|
@@ -92,7 +125,7 @@ export class MemoryArchiverStore {
|
|
|
92
125
|
return Promise.resolve(true);
|
|
93
126
|
}
|
|
94
127
|
deleteContractClasses(data, blockNumber) {
|
|
95
|
-
for (const contractClass of data)
|
|
128
|
+
for (const contractClass of data){
|
|
96
129
|
const restored = this.contractClasses.get(contractClass.id.toString());
|
|
97
130
|
if (restored && restored.l2BlockNumber >= blockNumber) {
|
|
98
131
|
this.contractClasses.delete(contractClass.id.toString());
|
|
@@ -102,78 +135,160 @@ export class MemoryArchiverStore {
|
|
|
102
135
|
return Promise.resolve(true);
|
|
103
136
|
}
|
|
104
137
|
addContractInstances(data, _blockNumber) {
|
|
105
|
-
for (const contractInstance of data)
|
|
138
|
+
for (const contractInstance of data){
|
|
106
139
|
this.contractInstances.set(contractInstance.address.toString(), contractInstance);
|
|
107
140
|
}
|
|
108
141
|
return Promise.resolve(true);
|
|
109
142
|
}
|
|
110
143
|
deleteContractInstances(data, _blockNumber) {
|
|
111
|
-
for (const contractInstance of data)
|
|
144
|
+
for (const contractInstance of data){
|
|
112
145
|
this.contractInstances.delete(contractInstance.address.toString());
|
|
113
146
|
}
|
|
114
147
|
return Promise.resolve(true);
|
|
115
148
|
}
|
|
149
|
+
addContractInstanceUpdates(data, blockNumber) {
|
|
150
|
+
for(let logIndex = 0; logIndex < data.length; logIndex++){
|
|
151
|
+
const contractInstanceUpdate = data[logIndex];
|
|
152
|
+
const updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || [];
|
|
153
|
+
updates.unshift({
|
|
154
|
+
...contractInstanceUpdate,
|
|
155
|
+
blockNumber,
|
|
156
|
+
logIndex
|
|
157
|
+
});
|
|
158
|
+
this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates);
|
|
159
|
+
}
|
|
160
|
+
return Promise.resolve(true);
|
|
161
|
+
}
|
|
162
|
+
deleteContractInstanceUpdates(data, blockNumber) {
|
|
163
|
+
for(let logIndex = 0; logIndex < data.length; logIndex++){
|
|
164
|
+
const contractInstanceUpdate = data[logIndex];
|
|
165
|
+
let updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || [];
|
|
166
|
+
updates = updates.filter((update)=>!(update.blockNumber === blockNumber && update.logIndex === logIndex));
|
|
167
|
+
this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates);
|
|
168
|
+
}
|
|
169
|
+
return Promise.resolve(true);
|
|
170
|
+
}
|
|
116
171
|
/**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
async addBlocks(blocks) {
|
|
172
|
+
* Append new blocks to the store's list.
|
|
173
|
+
* @param blocks - The L2 blocks to be added to the store and the last processed L1 block.
|
|
174
|
+
* @returns True if the operation is successful.
|
|
175
|
+
*/ async addBlocks(blocks) {
|
|
122
176
|
if (blocks.length === 0) {
|
|
123
177
|
return Promise.resolve(true);
|
|
124
178
|
}
|
|
125
179
|
this.lastL1BlockNewBlocks = blocks[blocks.length - 1].l1.blockNumber;
|
|
126
180
|
this.l2Blocks.push(...blocks);
|
|
127
|
-
const flatTxEffects = blocks.flatMap(b
|
|
128
|
-
|
|
181
|
+
const flatTxEffects = blocks.flatMap((b)=>b.data.body.txEffects.map((txEffect)=>({
|
|
182
|
+
block: b,
|
|
183
|
+
txEffect
|
|
184
|
+
})));
|
|
185
|
+
const wrappedTxEffects = await Promise.all(flatTxEffects.map((flatTxEffect)=>wrapInBlock(flatTxEffect.txEffect, flatTxEffect.block.data)));
|
|
129
186
|
this.txEffects.push(...wrappedTxEffects);
|
|
130
187
|
return Promise.resolve(true);
|
|
131
188
|
}
|
|
132
189
|
/**
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
async unwindBlocks(from, blocksToUnwind) {
|
|
190
|
+
* Unwinds blocks from the database
|
|
191
|
+
* @param from - The tip of the chain, passed for verification purposes,
|
|
192
|
+
* ensuring that we don't end up deleting something we did not intend
|
|
193
|
+
* @param blocksToUnwind - The number of blocks we are to unwind
|
|
194
|
+
* @returns True if the operation is successful
|
|
195
|
+
*/ async unwindBlocks(from, blocksToUnwind) {
|
|
140
196
|
const last = await this.getSynchedL2BlockNumber();
|
|
141
197
|
if (from != last) {
|
|
142
198
|
throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
|
|
143
199
|
}
|
|
144
200
|
const stopAt = from - blocksToUnwind;
|
|
145
|
-
while
|
|
201
|
+
while(await this.getSynchedL2BlockNumber() > stopAt){
|
|
146
202
|
const block = this.l2Blocks.pop();
|
|
147
203
|
if (block == undefined) {
|
|
148
204
|
break;
|
|
149
205
|
}
|
|
150
|
-
block.data.body.txEffects.forEach(()
|
|
206
|
+
block.data.body.txEffects.forEach(()=>this.txEffects.pop());
|
|
151
207
|
}
|
|
152
208
|
return Promise.resolve(true);
|
|
153
209
|
}
|
|
210
|
+
#storeTaggedLogsFromPrivate(block) {
|
|
211
|
+
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
212
|
+
block.body.txEffects.forEach((txEffect, txIndex)=>{
|
|
213
|
+
const txHash = txEffect.txHash;
|
|
214
|
+
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
215
|
+
txEffect.privateLogs.forEach((log)=>{
|
|
216
|
+
const tag = log.fields[0];
|
|
217
|
+
const currentLogs = this.taggedLogs.get(tag.toString()) || [];
|
|
218
|
+
this.taggedLogs.set(tag.toString(), [
|
|
219
|
+
...currentLogs,
|
|
220
|
+
new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ false, log.toBuffer())
|
|
221
|
+
]);
|
|
222
|
+
const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || [];
|
|
223
|
+
this.logTagsPerBlock.set(block.number, [
|
|
224
|
+
...currentTagsInBlock,
|
|
225
|
+
tag
|
|
226
|
+
]);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
#storeTaggedLogsFromPublic(block) {
|
|
231
|
+
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
232
|
+
block.body.txEffects.forEach((txEffect, txIndex)=>{
|
|
233
|
+
const txHash = txEffect.txHash;
|
|
234
|
+
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
235
|
+
txEffect.publicLogs.forEach((log)=>{
|
|
236
|
+
// Check that each log stores 3 lengths in its first field. If not, it's not a tagged log:
|
|
237
|
+
// See macros/note/mod/ and see how finalization_log[0] is constructed, to understand this monstrosity. (It wasn't me).
|
|
238
|
+
// 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.
|
|
239
|
+
const firstFieldBuf = log.log[0].toBuffer();
|
|
240
|
+
if (!firstFieldBuf.subarray(0, 27).equals(Buffer.alloc(27)) || firstFieldBuf[29] !== 0) {
|
|
241
|
+
// See parseLogFromPublic - the first field of a tagged log is 8 bytes structured:
|
|
242
|
+
// [ publicLen[0], publicLen[1], 0, privateLen[0], privateLen[1]]
|
|
243
|
+
this.#log.warn(`Skipping public log with invalid first field: ${log.log[0]}`);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// Check that the length values line up with the log contents
|
|
247
|
+
const publicValuesLength = firstFieldBuf.subarray(-5).readUint16BE();
|
|
248
|
+
const privateValuesLength = firstFieldBuf.subarray(-5).readUint16BE(3);
|
|
249
|
+
// Add 1 for the first field holding lengths
|
|
250
|
+
const totalLogLength = 1 + publicValuesLength + privateValuesLength;
|
|
251
|
+
// Note that zeroes can be valid log values, so we can only assert that we do not go over the given length
|
|
252
|
+
if (totalLogLength > PUBLIC_LOG_DATA_SIZE_IN_FIELDS || log.log.slice(totalLogLength).find((f)=>!f.isZero())) {
|
|
253
|
+
this.#log.warn(`Skipping invalid tagged public log with first field: ${log.log[0]}`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// The first elt stores lengths => tag is in fields[1]
|
|
257
|
+
const tag = log.log[1];
|
|
258
|
+
this.#log.verbose(`Storing public tagged log with tag ${tag.toString()} in block ${block.number}`);
|
|
259
|
+
const currentLogs = this.taggedLogs.get(tag.toString()) || [];
|
|
260
|
+
this.taggedLogs.set(tag.toString(), [
|
|
261
|
+
...currentLogs,
|
|
262
|
+
new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ true, log.toBuffer())
|
|
263
|
+
]);
|
|
264
|
+
const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || [];
|
|
265
|
+
this.logTagsPerBlock.set(block.number, [
|
|
266
|
+
...currentTagsInBlock,
|
|
267
|
+
tag
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
154
272
|
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
165
|
-
this.contractClassLogsPerBlock.set(block.number, block.body.contractClassLogs);
|
|
273
|
+
* Append new logs to the store's list.
|
|
274
|
+
* @param block - The block for which to add the logs.
|
|
275
|
+
* @returns True if the operation is successful.
|
|
276
|
+
*/ addLogs(blocks) {
|
|
277
|
+
blocks.forEach((block)=>{
|
|
278
|
+
this.#storeTaggedLogsFromPrivate(block);
|
|
279
|
+
this.#storeTaggedLogsFromPublic(block);
|
|
280
|
+
this.privateLogsPerBlock.set(block.number, block.body.txEffects.map((txEffect)=>txEffect.privateLogs).flat());
|
|
281
|
+
this.publicLogsPerBlock.set(block.number, block.body.txEffects.map((txEffect)=>txEffect.publicLogs).flat());
|
|
282
|
+
this.contractClassLogsPerBlock.set(block.number, block.body.txEffects.map((txEffect)=>txEffect.contractClassLogs).flat());
|
|
166
283
|
});
|
|
167
284
|
return Promise.resolve(true);
|
|
168
285
|
}
|
|
169
286
|
deleteLogs(blocks) {
|
|
170
|
-
const tagsToDelete = blocks.flatMap(block
|
|
171
|
-
tagsToDelete
|
|
172
|
-
.filter(tag => tag != undefined)
|
|
173
|
-
.forEach(tag => {
|
|
287
|
+
const tagsToDelete = blocks.flatMap((block)=>this.logTagsPerBlock.get(block.number));
|
|
288
|
+
tagsToDelete.filter((tag)=>tag != undefined).forEach((tag)=>{
|
|
174
289
|
this.taggedLogs.delete(tag.toString());
|
|
175
290
|
});
|
|
176
|
-
blocks.forEach(block
|
|
291
|
+
blocks.forEach((block)=>{
|
|
177
292
|
this.privateLogsPerBlock.delete(block.number);
|
|
178
293
|
this.publicLogsPerBlock.delete(block.number);
|
|
179
294
|
this.logTagsPerBlock.delete(block.number);
|
|
@@ -182,17 +297,16 @@ export class MemoryArchiverStore {
|
|
|
182
297
|
return Promise.resolve(true);
|
|
183
298
|
}
|
|
184
299
|
async addNullifiers(blocks) {
|
|
185
|
-
await Promise.all(blocks.map(async (block)
|
|
186
|
-
const dataStartIndexForBlock = block.header.state.partial.nullifierTree.nextAvailableLeafIndex -
|
|
187
|
-
block.body.txEffects.length * MAX_NULLIFIERS_PER_TX;
|
|
300
|
+
await Promise.all(blocks.map(async (block)=>{
|
|
301
|
+
const dataStartIndexForBlock = block.header.state.partial.nullifierTree.nextAvailableLeafIndex - block.body.txEffects.length * MAX_NULLIFIERS_PER_TX;
|
|
188
302
|
const blockHash = await block.hash();
|
|
189
|
-
block.body.txEffects.forEach((txEffects, txIndex)
|
|
303
|
+
block.body.txEffects.forEach((txEffects, txIndex)=>{
|
|
190
304
|
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX;
|
|
191
|
-
txEffects.nullifiers.forEach((nullifier, nullifierIndex)
|
|
305
|
+
txEffects.nullifiers.forEach((nullifier, nullifierIndex)=>{
|
|
192
306
|
this.blockScopedNullifiers.set(nullifier.toString(), {
|
|
193
307
|
index: BigInt(dataStartIndexForTx + nullifierIndex),
|
|
194
308
|
blockNumber: block.number,
|
|
195
|
-
blockHash: blockHash.toString()
|
|
309
|
+
blockHash: blockHash.toString()
|
|
196
310
|
});
|
|
197
311
|
});
|
|
198
312
|
});
|
|
@@ -200,9 +314,9 @@ export class MemoryArchiverStore {
|
|
|
200
314
|
return Promise.resolve(true);
|
|
201
315
|
}
|
|
202
316
|
deleteNullifiers(blocks) {
|
|
203
|
-
blocks.forEach(block
|
|
204
|
-
block.body.txEffects.forEach(txEffect
|
|
205
|
-
txEffect.nullifiers.forEach(nullifier
|
|
317
|
+
blocks.forEach((block)=>{
|
|
318
|
+
block.body.txEffects.forEach((txEffect)=>{
|
|
319
|
+
txEffect.nullifiers.forEach((nullifier)=>{
|
|
206
320
|
this.blockScopedNullifiers.delete(nullifier.toString());
|
|
207
321
|
});
|
|
208
322
|
});
|
|
@@ -210,13 +324,13 @@ export class MemoryArchiverStore {
|
|
|
210
324
|
return Promise.resolve(true);
|
|
211
325
|
}
|
|
212
326
|
findNullifiersIndexesWithBlock(blockNumber, nullifiers) {
|
|
213
|
-
const blockScopedNullifiers = nullifiers.map(nullifier
|
|
327
|
+
const blockScopedNullifiers = nullifiers.map((nullifier)=>{
|
|
214
328
|
const nullifierData = this.blockScopedNullifiers.get(nullifier.toString());
|
|
215
329
|
if (nullifierData !== undefined && nullifierData.blockNumber <= blockNumber) {
|
|
216
330
|
return {
|
|
217
331
|
data: nullifierData.index,
|
|
218
332
|
l2BlockHash: nullifierData.blockHash,
|
|
219
|
-
l2BlockNumber: nullifierData.blockNumber
|
|
333
|
+
l2BlockNumber: nullifierData.blockNumber
|
|
220
334
|
};
|
|
221
335
|
}
|
|
222
336
|
return undefined;
|
|
@@ -227,37 +341,33 @@ export class MemoryArchiverStore {
|
|
|
227
341
|
return Promise.resolve(this.l1ToL2Messages.getTotalL1ToL2MessageCount());
|
|
228
342
|
}
|
|
229
343
|
/**
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (typeof this.lastL1BlockNewMessages === 'bigint' &&
|
|
236
|
-
messages.lastProcessedL1BlockNumber <= this.lastL1BlockNewMessages) {
|
|
344
|
+
* Append L1 to L2 messages to the store.
|
|
345
|
+
* @param messages - The L1 to L2 messages to be added to the store and the last processed L1 block.
|
|
346
|
+
* @returns True if the operation is successful.
|
|
347
|
+
*/ addL1ToL2Messages(messages) {
|
|
348
|
+
if (typeof this.lastL1BlockNewMessages === 'bigint' && messages.lastProcessedL1BlockNumber <= this.lastL1BlockNewMessages) {
|
|
237
349
|
return Promise.resolve(false);
|
|
238
350
|
}
|
|
239
351
|
this.lastL1BlockNewMessages = messages.lastProcessedL1BlockNumber;
|
|
240
|
-
for (const message of messages.retrievedData)
|
|
352
|
+
for (const message of messages.retrievedData){
|
|
241
353
|
this.l1ToL2Messages.addMessage(message);
|
|
242
354
|
}
|
|
243
355
|
return Promise.resolve(true);
|
|
244
356
|
}
|
|
245
357
|
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
getL1ToL2MessageIndex(l1ToL2Message) {
|
|
358
|
+
* Gets the L1 to L2 message index in the L1 to L2 message tree.
|
|
359
|
+
* @param l1ToL2Message - The L1 to L2 message.
|
|
360
|
+
* @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found).
|
|
361
|
+
*/ getL1ToL2MessageIndex(l1ToL2Message) {
|
|
251
362
|
return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message));
|
|
252
363
|
}
|
|
253
364
|
/**
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
getBlocks(from, limit) {
|
|
365
|
+
* Gets up to `limit` amount of L2 blocks starting from `from`.
|
|
366
|
+
* @param from - Number of the first block to return (inclusive).
|
|
367
|
+
* @param limit - The number of blocks to return.
|
|
368
|
+
* @returns The requested L2 blocks.
|
|
369
|
+
* @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned.
|
|
370
|
+
*/ getBlocks(from, limit) {
|
|
261
371
|
if (limit < 1) {
|
|
262
372
|
return Promise.reject(new Error(`Invalid limit: ${limit}`));
|
|
263
373
|
}
|
|
@@ -273,25 +383,23 @@ export class MemoryArchiverStore {
|
|
|
273
383
|
}
|
|
274
384
|
async getBlockHeaders(from, limit) {
|
|
275
385
|
const blocks = await this.getBlocks(from, limit);
|
|
276
|
-
return blocks.map(block
|
|
386
|
+
return blocks.map((block)=>block.data.header);
|
|
277
387
|
}
|
|
278
388
|
/**
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const txEffect = this.txEffects.find(tx => tx.data.txHash.equals(txHash));
|
|
389
|
+
* Gets a tx effect.
|
|
390
|
+
* @param txHash - The txHash of the tx effect.
|
|
391
|
+
* @returns The requested tx effect.
|
|
392
|
+
*/ getTxEffect(txHash) {
|
|
393
|
+
const txEffect = this.txEffects.find((tx)=>tx.data.txHash.equals(txHash));
|
|
285
394
|
return Promise.resolve(txEffect);
|
|
286
395
|
}
|
|
287
396
|
/**
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for (const txEffect of block.data.body.txEffects) {
|
|
397
|
+
* Gets a receipt of a settled tx.
|
|
398
|
+
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
399
|
+
* @returns The requested tx receipt (or undefined if not found).
|
|
400
|
+
*/ async getSettledTxReceipt(txHash) {
|
|
401
|
+
for (const block of this.l2Blocks){
|
|
402
|
+
for (const txEffect of block.data.body.txEffects){
|
|
295
403
|
if (txEffect.txHash.equals(txHash)) {
|
|
296
404
|
return new TxReceipt(txHash, TxReceipt.statusFromRevertCode(txEffect.revertCode), '', txEffect.transactionFee.toBigInt(), L2BlockHash.fromField(await block.data.hash()), block.data.number);
|
|
297
405
|
}
|
|
@@ -300,20 +408,18 @@ export class MemoryArchiverStore {
|
|
|
300
408
|
return undefined;
|
|
301
409
|
}
|
|
302
410
|
/**
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
getL1ToL2Messages(blockNumber) {
|
|
411
|
+
* Gets L1 to L2 message (to be) included in a given block.
|
|
412
|
+
* @param blockNumber - L2 block number to get messages for.
|
|
413
|
+
* @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
|
|
414
|
+
*/ getL1ToL2Messages(blockNumber) {
|
|
308
415
|
return Promise.resolve(this.l1ToL2Messages.getMessages(blockNumber));
|
|
309
416
|
}
|
|
310
417
|
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
getPrivateLogs(from, limit) {
|
|
418
|
+
* Retrieves all private logs from up to `limit` blocks, starting from the block number `from`.
|
|
419
|
+
* @param from - The block number from which to begin retrieving logs.
|
|
420
|
+
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
421
|
+
* @returns An array of private logs from the specified range of blocks.
|
|
422
|
+
*/ getPrivateLogs(from, limit) {
|
|
317
423
|
if (from < INITIAL_L2_BLOCK_NUM || limit < 1) {
|
|
318
424
|
return Promise.resolve([]);
|
|
319
425
|
}
|
|
@@ -324,7 +430,7 @@ export class MemoryArchiverStore {
|
|
|
324
430
|
const endIndex = startIndex + limit;
|
|
325
431
|
const upper = Math.min(endIndex, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM);
|
|
326
432
|
const logsInBlocks = [];
|
|
327
|
-
for
|
|
433
|
+
for(let i = startIndex; i < upper; i++){
|
|
328
434
|
const logs = this.privateLogsPerBlock.get(i);
|
|
329
435
|
if (logs) {
|
|
330
436
|
logsInBlocks.push(logs);
|
|
@@ -333,22 +439,20 @@ export class MemoryArchiverStore {
|
|
|
333
439
|
return Promise.resolve(logsInBlocks.flat());
|
|
334
440
|
}
|
|
335
441
|
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const noteLogs = tags.map(tag => this.taggedLogs.get(tag.toString()) || []);
|
|
442
|
+
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
443
|
+
* @param tags - The tags to filter the logs by.
|
|
444
|
+
* @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match
|
|
445
|
+
* that tag.
|
|
446
|
+
*/ getLogsByTags(tags) {
|
|
447
|
+
const noteLogs = tags.map((tag)=>this.taggedLogs.get(tag.toString()) || []);
|
|
343
448
|
return Promise.resolve(noteLogs);
|
|
344
449
|
}
|
|
345
450
|
/**
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
getPublicLogs(filter) {
|
|
451
|
+
* Gets public logs based on the provided filter.
|
|
452
|
+
* @param filter - The filter to apply to the logs.
|
|
453
|
+
* @returns The requested logs.
|
|
454
|
+
* @remarks Works by doing an intersection of all params in the filter.
|
|
455
|
+
*/ getPublicLogs(filter) {
|
|
352
456
|
let txHash;
|
|
353
457
|
let fromBlock = 0;
|
|
354
458
|
let toBlock = this.l2Blocks.length + INITIAL_L2_BLOCK_NUM;
|
|
@@ -360,12 +464,10 @@ export class MemoryArchiverStore {
|
|
|
360
464
|
fromBlock = filter.afterLog.blockNumber;
|
|
361
465
|
txIndexInBlock = filter.afterLog.txIndex;
|
|
362
466
|
logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
467
|
+
} else {
|
|
365
468
|
fromBlock = filter.fromBlock;
|
|
366
469
|
}
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
470
|
+
} else {
|
|
369
471
|
txHash = filter.txHash;
|
|
370
472
|
if (filter.fromBlock !== undefined) {
|
|
371
473
|
fromBlock = filter.fromBlock;
|
|
@@ -380,29 +482,26 @@ export class MemoryArchiverStore {
|
|
|
380
482
|
if (fromBlock > this.l2Blocks.length || toBlock < fromBlock || toBlock <= 0) {
|
|
381
483
|
return Promise.resolve({
|
|
382
484
|
logs: [],
|
|
383
|
-
maxLogsHit: false
|
|
485
|
+
maxLogsHit: false
|
|
384
486
|
});
|
|
385
487
|
}
|
|
386
488
|
const contractAddress = filter.contractAddress;
|
|
387
489
|
const logs = [];
|
|
388
|
-
for
|
|
490
|
+
for(; fromBlock < toBlock; fromBlock++){
|
|
389
491
|
const block = this.l2Blocks[fromBlock - INITIAL_L2_BLOCK_NUM];
|
|
390
492
|
const blockLogs = this.publicLogsPerBlock.get(fromBlock);
|
|
391
493
|
if (blockLogs) {
|
|
392
|
-
for
|
|
494
|
+
for(let logIndex = 0; logIndex < blockLogs.length; logIndex++){
|
|
393
495
|
const log = blockLogs[logIndex];
|
|
394
|
-
const thisTxEffect = block.data.body.txEffects.filter(effect
|
|
496
|
+
const thisTxEffect = block.data.body.txEffects.filter((effect)=>effect.publicLogs.includes(log))[0];
|
|
395
497
|
const thisTxIndexInBlock = block.data.body.txEffects.indexOf(thisTxEffect);
|
|
396
498
|
const thisLogIndexInTx = thisTxEffect.publicLogs.indexOf(log);
|
|
397
|
-
if ((!txHash || thisTxEffect.txHash.equals(txHash)) &&
|
|
398
|
-
(!contractAddress || log.contractAddress.equals(contractAddress)) &&
|
|
399
|
-
thisTxIndexInBlock >= txIndexInBlock &&
|
|
400
|
-
thisLogIndexInTx >= logIndexInTx) {
|
|
499
|
+
if ((!txHash || thisTxEffect.txHash.equals(txHash)) && (!contractAddress || log.contractAddress.equals(contractAddress)) && thisTxIndexInBlock >= txIndexInBlock && thisLogIndexInTx >= logIndexInTx) {
|
|
401
500
|
logs.push(new ExtendedPublicLog(new LogId(block.data.number, thisTxIndexInBlock, thisLogIndexInTx), log));
|
|
402
501
|
if (logs.length === this.maxLogs) {
|
|
403
502
|
return Promise.resolve({
|
|
404
503
|
logs,
|
|
405
|
-
maxLogsHit: true
|
|
504
|
+
maxLogsHit: true
|
|
406
505
|
});
|
|
407
506
|
}
|
|
408
507
|
}
|
|
@@ -411,17 +510,16 @@ export class MemoryArchiverStore {
|
|
|
411
510
|
}
|
|
412
511
|
return Promise.resolve({
|
|
413
512
|
logs,
|
|
414
|
-
maxLogsHit: false
|
|
513
|
+
maxLogsHit: false
|
|
415
514
|
});
|
|
416
515
|
}
|
|
417
516
|
/**
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
getContractClassLogs(filter) {
|
|
517
|
+
* Gets contract class logs based on the provided filter.
|
|
518
|
+
* NB: clone of the above fn, but for contract class logs
|
|
519
|
+
* @param filter - The filter to apply to the logs.
|
|
520
|
+
* @returns The requested logs.
|
|
521
|
+
* @remarks Works by doing an intersection of all params in the filter.
|
|
522
|
+
*/ getContractClassLogs(filter) {
|
|
425
523
|
let txHash;
|
|
426
524
|
let fromBlock = 0;
|
|
427
525
|
let toBlock = this.l2Blocks.length + INITIAL_L2_BLOCK_NUM;
|
|
@@ -433,12 +531,10 @@ export class MemoryArchiverStore {
|
|
|
433
531
|
fromBlock = filter.afterLog.blockNumber;
|
|
434
532
|
txIndexInBlock = filter.afterLog.txIndex;
|
|
435
533
|
logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
534
|
+
} else {
|
|
438
535
|
fromBlock = filter.fromBlock;
|
|
439
536
|
}
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
537
|
+
} else {
|
|
442
538
|
txHash = filter.txHash;
|
|
443
539
|
if (filter.fromBlock !== undefined) {
|
|
444
540
|
fromBlock = filter.fromBlock;
|
|
@@ -453,49 +549,48 @@ export class MemoryArchiverStore {
|
|
|
453
549
|
if (fromBlock > this.l2Blocks.length || toBlock < fromBlock || toBlock <= 0) {
|
|
454
550
|
return Promise.resolve({
|
|
455
551
|
logs: [],
|
|
456
|
-
maxLogsHit: false
|
|
552
|
+
maxLogsHit: false
|
|
457
553
|
});
|
|
458
554
|
}
|
|
459
555
|
const contractAddress = filter.contractAddress;
|
|
460
556
|
const logs = [];
|
|
461
|
-
for
|
|
557
|
+
for(; fromBlock < toBlock; fromBlock++){
|
|
462
558
|
const block = this.l2Blocks[fromBlock - INITIAL_L2_BLOCK_NUM];
|
|
463
559
|
const blockLogs = this.contractClassLogsPerBlock.get(fromBlock);
|
|
464
560
|
if (blockLogs) {
|
|
465
|
-
for
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
561
|
+
for(let logIndex = 0; logIndex < blockLogs.length; logIndex++){
|
|
562
|
+
const log = blockLogs[logIndex];
|
|
563
|
+
const thisTxEffect = block.data.body.txEffects.filter((effect)=>effect.contractClassLogs.includes(log))[0];
|
|
564
|
+
const thisTxIndexInBlock = block.data.body.txEffects.indexOf(thisTxEffect);
|
|
565
|
+
const thisLogIndexInTx = thisTxEffect.contractClassLogs.indexOf(log);
|
|
566
|
+
if ((!txHash || thisTxEffect.txHash.equals(txHash)) && (!contractAddress || log.contractAddress.equals(contractAddress)) && thisTxIndexInBlock >= txIndexInBlock && thisLogIndexInTx >= logIndexInTx) {
|
|
567
|
+
logs.push(new ExtendedContractClassLog(new LogId(block.data.number, thisTxIndexInBlock, thisLogIndexInTx), log));
|
|
568
|
+
if (logs.length === this.maxLogs) {
|
|
569
|
+
return Promise.resolve({
|
|
570
|
+
logs,
|
|
571
|
+
maxLogsHit: true
|
|
572
|
+
});
|
|
478
573
|
}
|
|
479
574
|
}
|
|
480
|
-
logIndexInTx = 0;
|
|
481
575
|
}
|
|
482
576
|
}
|
|
483
|
-
txIndexInBlock = 0;
|
|
484
577
|
}
|
|
485
578
|
return Promise.resolve({
|
|
486
579
|
logs,
|
|
487
|
-
maxLogsHit: false
|
|
580
|
+
maxLogsHit: false
|
|
488
581
|
});
|
|
489
582
|
}
|
|
490
|
-
|
|
491
|
-
* Gets the number of the latest L2 block processed.
|
|
492
|
-
* @returns The number of the latest L2 block processed.
|
|
493
|
-
*/
|
|
494
|
-
getSynchedL2BlockNumber() {
|
|
583
|
+
getLastBlockNumber() {
|
|
495
584
|
if (this.l2Blocks.length === 0) {
|
|
496
|
-
return
|
|
585
|
+
return INITIAL_L2_BLOCK_NUM - 1;
|
|
497
586
|
}
|
|
498
|
-
return
|
|
587
|
+
return this.l2Blocks[this.l2Blocks.length - 1].data.number;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Gets the number of the latest L2 block processed.
|
|
591
|
+
* @returns The number of the latest L2 block processed.
|
|
592
|
+
*/ getSynchedL2BlockNumber() {
|
|
593
|
+
return Promise.resolve(this.getLastBlockNumber());
|
|
499
594
|
}
|
|
500
595
|
getProvenL2BlockNumber() {
|
|
501
596
|
return Promise.resolve(this.lastProvenL2BlockNumber);
|
|
@@ -522,82 +617,27 @@ export class MemoryArchiverStore {
|
|
|
522
617
|
getSynchPoint() {
|
|
523
618
|
return Promise.resolve({
|
|
524
619
|
blocksSynchedTo: this.lastL1BlockNewBlocks,
|
|
525
|
-
messagesSynchedTo: this.lastL1BlockNewMessages
|
|
620
|
+
messagesSynchedTo: this.lastL1BlockNewMessages
|
|
526
621
|
});
|
|
527
622
|
}
|
|
528
623
|
getContractFunctionName(_address, selector) {
|
|
529
624
|
return Promise.resolve(this.functionNames.get(selector.toString()));
|
|
530
625
|
}
|
|
531
626
|
async registerContractFunctionSignatures(_address, signatures) {
|
|
532
|
-
for (const sig of signatures)
|
|
627
|
+
for (const sig of signatures){
|
|
533
628
|
try {
|
|
534
629
|
const selector = await FunctionSelector.fromSignature(sig);
|
|
535
630
|
this.functionNames.set(selector.toString(), sig.slice(0, sig.indexOf('(')));
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
__classPrivateFieldGet(this, _MemoryArchiverStore_log, "f").warn(`Failed to parse signature: ${sig}. Ignoring`);
|
|
631
|
+
} catch {
|
|
632
|
+
this.#log.warn(`Failed to parse signature: ${sig}. Ignoring`);
|
|
539
633
|
}
|
|
540
634
|
}
|
|
541
635
|
}
|
|
542
636
|
estimateSize() {
|
|
543
|
-
return Promise.resolve({
|
|
637
|
+
return Promise.resolve({
|
|
638
|
+
mappingSize: 0,
|
|
639
|
+
actualSize: 0,
|
|
640
|
+
numItems: 0
|
|
641
|
+
});
|
|
544
642
|
}
|
|
545
643
|
}
|
|
546
|
-
_MemoryArchiverStore_log = new WeakMap(), _MemoryArchiverStore_instances = new WeakSet(), _MemoryArchiverStore_storeTaggedLogsFromPrivate = function _MemoryArchiverStore_storeTaggedLogsFromPrivate(block) {
|
|
547
|
-
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
548
|
-
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
549
|
-
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
550
|
-
const txHash = txEffect.txHash;
|
|
551
|
-
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
552
|
-
txEffect.privateLogs.forEach(log => {
|
|
553
|
-
const tag = log.fields[0];
|
|
554
|
-
const currentLogs = this.taggedLogs.get(tag.toString()) || [];
|
|
555
|
-
this.taggedLogs.set(tag.toString(), [
|
|
556
|
-
...currentLogs,
|
|
557
|
-
new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ false, log.toBuffer()),
|
|
558
|
-
]);
|
|
559
|
-
const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || [];
|
|
560
|
-
this.logTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]);
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
}, _MemoryArchiverStore_storeTaggedLogsFromPublic = function _MemoryArchiverStore_storeTaggedLogsFromPublic(block) {
|
|
564
|
-
const dataStartIndexForBlock = block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
565
|
-
block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX;
|
|
566
|
-
block.body.txEffects.forEach((txEffect, txIndex) => {
|
|
567
|
-
const txHash = txEffect.txHash;
|
|
568
|
-
const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX;
|
|
569
|
-
txEffect.publicLogs.forEach(log => {
|
|
570
|
-
// Check that each log stores 3 lengths in its first field. If not, it's not a tagged log:
|
|
571
|
-
// See macros/note/mod/ and see how finalization_log[0] is constructed, to understand this monstrosity. (It wasn't me).
|
|
572
|
-
// 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.
|
|
573
|
-
const firstFieldBuf = log.log[0].toBuffer();
|
|
574
|
-
if (!firstFieldBuf.subarray(0, 27).equals(Buffer.alloc(27)) || firstFieldBuf[29] !== 0) {
|
|
575
|
-
// See parseLogFromPublic - the first field of a tagged log is 8 bytes structured:
|
|
576
|
-
// [ publicLen[0], publicLen[1], 0, privateLen[0], privateLen[1]]
|
|
577
|
-
__classPrivateFieldGet(this, _MemoryArchiverStore_log, "f").warn(`Skipping public log with invalid first field: ${log.log[0]}`);
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
// Check that the length values line up with the log contents
|
|
581
|
-
const publicValuesLength = firstFieldBuf.subarray(-5).readUint16BE();
|
|
582
|
-
const privateValuesLength = firstFieldBuf.subarray(-5).readUint16BE(3);
|
|
583
|
-
// Add 1 for the first field holding lengths
|
|
584
|
-
const totalLogLength = 1 + publicValuesLength + privateValuesLength;
|
|
585
|
-
// Note that zeroes can be valid log values, so we can only assert that we do not go over the given length
|
|
586
|
-
if (totalLogLength > PUBLIC_LOG_DATA_SIZE_IN_FIELDS || log.log.slice(totalLogLength).find(f => !f.isZero())) {
|
|
587
|
-
__classPrivateFieldGet(this, _MemoryArchiverStore_log, "f").warn(`Skipping invalid tagged public log with first field: ${log.log[0]}`);
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
// The first elt stores lengths => tag is in fields[1]
|
|
591
|
-
const tag = log.log[1];
|
|
592
|
-
__classPrivateFieldGet(this, _MemoryArchiverStore_log, "f").verbose(`Storing public tagged log with tag ${tag.toString()} in block ${block.number}`);
|
|
593
|
-
const currentLogs = this.taggedLogs.get(tag.toString()) || [];
|
|
594
|
-
this.taggedLogs.set(tag.toString(), [
|
|
595
|
-
...currentLogs,
|
|
596
|
-
new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, /* isFromPublic */ true, log.toBuffer()),
|
|
597
|
-
]);
|
|
598
|
-
const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || [];
|
|
599
|
-
this.logTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]);
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
};
|
|
603
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVtb3J5X2FyY2hpdmVyX3N0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FyY2hpdmVyL21lbW9yeV9hcmNoaXZlcl9zdG9yZS9tZW1vcnlfYXJjaGl2ZXJfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxPQUFPLEVBRUwsaUJBQWlCLEVBQ2pCLHdCQUF3QixFQU14QixXQUFXLEVBRVgsS0FBSyxFQUdMLFNBQVMsRUFDVCxhQUFhLEVBQ2IsV0FBVyxHQUNaLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQU1MLEVBQUUsRUFDRixvQkFBb0IsRUFDcEIsc0JBQXNCLEVBQ3RCLHFCQUFxQixFQUNyQiw4QkFBOEIsR0FJL0IsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUV6RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFLckQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFakU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO0lBZ0Q5QjtJQUNFLDZFQUE2RTtJQUM3RCxPQUFlOztRQUFmLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFqRGpDOztXQUVHO1FBQ0ssYUFBUSxHQUEyQixFQUFFLENBQUM7UUFFOUM7O1dBRUc7UUFDSyxjQUFTLEdBQXdCLEVBQUUsQ0FBQztRQUVwQyxlQUFVLEdBQWlDLElBQUksR0FBRyxFQUFFLENBQUM7UUFFckQsb0JBQWUsR0FBc0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUUvQyx3QkFBbUIsR0FBOEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUUzRCx1QkFBa0IsR0FBNkIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV6RCw4QkFBeUIsR0FBMkMsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUU5RSwwQkFBcUIsR0FBMkUsSUFBSSxHQUFHLEVBQUUs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
|