@aztec/archiver 0.22.0 → 0.24.0
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.d.ts +14 -1
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +55 -11
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +2 -2
- package/package.json +9 -8
- package/src/archiver/archiver.ts +622 -0
- package/src/archiver/archiver_store.ts +199 -0
- package/src/archiver/archiver_store_test_suite.ts +661 -0
- package/src/archiver/config.ts +89 -0
- package/src/archiver/data_retrieval.ts +187 -0
- package/src/archiver/eth_log_handlers.ts +268 -0
- package/src/archiver/index.ts +5 -0
- package/src/archiver/kv_archiver_store/block_store.ts +164 -0
- package/src/archiver/kv_archiver_store/contract_class_store.ts +64 -0
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +26 -0
- package/src/archiver/kv_archiver_store/contract_store.ts +93 -0
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +264 -0
- package/src/archiver/kv_archiver_store/log_store.ts +174 -0
- package/src/archiver/kv_archiver_store/message_store.ts +170 -0
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +91 -0
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +444 -0
- package/src/index.ts +54 -0
- package/src/rpc/archiver_client.ts +33 -0
- package/src/rpc/archiver_server.ts +37 -0
- package/src/rpc/index.ts +2 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExtendedUnencryptedL2Log,
|
|
3
|
+
GetUnencryptedLogsResponse,
|
|
4
|
+
INITIAL_L2_BLOCK_NUM,
|
|
5
|
+
L2BlockL2Logs,
|
|
6
|
+
LogFilter,
|
|
7
|
+
LogId,
|
|
8
|
+
LogType,
|
|
9
|
+
UnencryptedL2Log,
|
|
10
|
+
} from '@aztec/circuit-types';
|
|
11
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
12
|
+
import { AztecKVStore, AztecMap } from '@aztec/kv-store';
|
|
13
|
+
|
|
14
|
+
import { BlockStore } from './block_store.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A store for logs
|
|
18
|
+
*/
|
|
19
|
+
export class LogStore {
|
|
20
|
+
#encryptedLogs: AztecMap<number, Buffer>;
|
|
21
|
+
#unencryptedLogs: AztecMap<number, Buffer>;
|
|
22
|
+
#logsMaxPageSize: number;
|
|
23
|
+
#log = createDebugLogger('aztec:archiver:log_store');
|
|
24
|
+
|
|
25
|
+
constructor(private db: AztecKVStore, private blockStore: BlockStore, logsMaxPageSize: number = 1000) {
|
|
26
|
+
this.#encryptedLogs = db.openMap('archiver_encrypted_logs');
|
|
27
|
+
this.#unencryptedLogs = db.openMap('archiver_unencrypted_logs');
|
|
28
|
+
|
|
29
|
+
this.#logsMaxPageSize = logsMaxPageSize;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Append new logs to the store's list.
|
|
34
|
+
* @param encryptedLogs - The logs to be added to the store.
|
|
35
|
+
* @param unencryptedLogs - The type of the logs to be added to the store.
|
|
36
|
+
* @param blockNumber - The block for which to add the logs.
|
|
37
|
+
* @returns True if the operation is successful.
|
|
38
|
+
*/
|
|
39
|
+
addLogs(
|
|
40
|
+
encryptedLogs: L2BlockL2Logs | undefined,
|
|
41
|
+
unencryptedLogs: L2BlockL2Logs | undefined,
|
|
42
|
+
blockNumber: number,
|
|
43
|
+
): Promise<boolean> {
|
|
44
|
+
return this.db.transaction(() => {
|
|
45
|
+
if (encryptedLogs) {
|
|
46
|
+
void this.#encryptedLogs.set(blockNumber, encryptedLogs.toBuffer());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (unencryptedLogs) {
|
|
50
|
+
void this.#unencryptedLogs.set(blockNumber, unencryptedLogs.toBuffer());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets up to `limit` amount of logs starting from `from`.
|
|
59
|
+
* @param start - Number of the L2 block to which corresponds the first logs to be returned.
|
|
60
|
+
* @param limit - The number of logs to return.
|
|
61
|
+
* @param logType - Specifies whether to return encrypted or unencrypted logs.
|
|
62
|
+
* @returns The requested logs.
|
|
63
|
+
*/
|
|
64
|
+
*getLogs(start: number, limit: number, logType: LogType): IterableIterator<L2BlockL2Logs> {
|
|
65
|
+
const logMap = logType === LogType.ENCRYPTED ? this.#encryptedLogs : this.#unencryptedLogs;
|
|
66
|
+
for (const buffer of logMap.values({ start, limit })) {
|
|
67
|
+
yield L2BlockL2Logs.fromBuffer(buffer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets unencrypted logs based on the provided filter.
|
|
73
|
+
* @param filter - The filter to apply to the logs.
|
|
74
|
+
* @returns The requested logs.
|
|
75
|
+
*/
|
|
76
|
+
getUnencryptedLogs(filter: LogFilter): GetUnencryptedLogsResponse {
|
|
77
|
+
if (filter.afterLog) {
|
|
78
|
+
return this.#filterUnencryptedLogsBetweenBlocks(filter);
|
|
79
|
+
} else if (filter.txHash) {
|
|
80
|
+
return this.#filterUnencryptedLogsOfTx(filter);
|
|
81
|
+
} else {
|
|
82
|
+
return this.#filterUnencryptedLogsBetweenBlocks(filter);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#filterUnencryptedLogsOfTx(filter: LogFilter): GetUnencryptedLogsResponse {
|
|
87
|
+
if (!filter.txHash) {
|
|
88
|
+
throw new Error('Missing txHash');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const [blockNumber, txIndex] = this.blockStore.getL2TxLocation(filter.txHash) ?? [];
|
|
92
|
+
if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
|
|
93
|
+
return { logs: [], maxLogsHit: false };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const unencryptedLogsInBlock = this.#getBlockLogs(blockNumber, LogType.UNENCRYPTED);
|
|
97
|
+
const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log));
|
|
98
|
+
|
|
99
|
+
const logs: ExtendedUnencryptedL2Log[] = [];
|
|
100
|
+
const maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
101
|
+
|
|
102
|
+
return { logs, maxLogsHit };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#filterUnencryptedLogsBetweenBlocks(filter: LogFilter): GetUnencryptedLogsResponse {
|
|
106
|
+
const start =
|
|
107
|
+
filter.afterLog?.blockNumber ?? Math.max(filter.fromBlock ?? INITIAL_L2_BLOCK_NUM, INITIAL_L2_BLOCK_NUM);
|
|
108
|
+
const end = filter.toBlock;
|
|
109
|
+
|
|
110
|
+
if (typeof end === 'number' && end < start) {
|
|
111
|
+
return {
|
|
112
|
+
logs: [],
|
|
113
|
+
maxLogsHit: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const logs: ExtendedUnencryptedL2Log[] = [];
|
|
118
|
+
|
|
119
|
+
let maxLogsHit = false;
|
|
120
|
+
loopOverBlocks: for (const [blockNumber, logBuffer] of this.#unencryptedLogs.entries({ start, end })) {
|
|
121
|
+
const unencryptedLogsInBlock = L2BlockL2Logs.fromBuffer(logBuffer);
|
|
122
|
+
for (let txIndex = filter.afterLog?.txIndex ?? 0; txIndex < unencryptedLogsInBlock.txLogs.length; txIndex++) {
|
|
123
|
+
const txLogs = unencryptedLogsInBlock.txLogs[txIndex].unrollLogs().map(log => UnencryptedL2Log.fromBuffer(log));
|
|
124
|
+
maxLogsHit = this.#accumulateLogs(logs, blockNumber, txIndex, txLogs, filter);
|
|
125
|
+
if (maxLogsHit) {
|
|
126
|
+
this.#log(`Max logs hit at block ${blockNumber}`);
|
|
127
|
+
break loopOverBlocks;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { logs, maxLogsHit };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#accumulateLogs(
|
|
136
|
+
results: ExtendedUnencryptedL2Log[],
|
|
137
|
+
blockNumber: number,
|
|
138
|
+
txIndex: number,
|
|
139
|
+
txLogs: UnencryptedL2Log[],
|
|
140
|
+
filter: LogFilter,
|
|
141
|
+
): boolean {
|
|
142
|
+
let maxLogsHit = false;
|
|
143
|
+
let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0;
|
|
144
|
+
for (; logIndex < txLogs.length; logIndex++) {
|
|
145
|
+
const log = txLogs[logIndex];
|
|
146
|
+
if (filter.contractAddress && !log.contractAddress.equals(filter.contractAddress)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (filter.selector && !log.selector.equals(filter.selector)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
results.push(new ExtendedUnencryptedL2Log(new LogId(blockNumber, txIndex, logIndex), log));
|
|
155
|
+
if (results.length >= this.#logsMaxPageSize) {
|
|
156
|
+
maxLogsHit = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return maxLogsHit;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#getBlockLogs(blockNumber: number, logType: LogType): L2BlockL2Logs {
|
|
165
|
+
const logMap = logType === LogType.ENCRYPTED ? this.#encryptedLogs : this.#unencryptedLogs;
|
|
166
|
+
const buffer = logMap.get(blockNumber);
|
|
167
|
+
|
|
168
|
+
if (!buffer) {
|
|
169
|
+
return new L2BlockL2Logs([]);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return L2BlockL2Logs.fromBuffer(buffer);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { L1ToL2Message } from '@aztec/circuit-types';
|
|
2
|
+
import { Fr } from '@aztec/circuits.js';
|
|
3
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { AztecCounter, AztecKVStore, AztecMap, AztecSingleton } from '@aztec/kv-store';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A message stored in the database
|
|
8
|
+
*/
|
|
9
|
+
type Message = {
|
|
10
|
+
/** The L1ToL2Message */
|
|
11
|
+
message: Buffer;
|
|
12
|
+
/** The message's fee */
|
|
13
|
+
fee: number;
|
|
14
|
+
/** Has it _ever_ been confirmed? */
|
|
15
|
+
confirmed: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* LMDB implementation of the ArchiverDataStore interface.
|
|
20
|
+
*/
|
|
21
|
+
export class MessageStore {
|
|
22
|
+
#messages: AztecMap<string, Message>;
|
|
23
|
+
#pendingMessagesByFee: AztecCounter<[number, string]>;
|
|
24
|
+
#lastL1BlockAddingMessages: AztecSingleton<bigint>;
|
|
25
|
+
#lastL1BlockCancellingMessages: AztecSingleton<bigint>;
|
|
26
|
+
|
|
27
|
+
#log = createDebugLogger('aztec:archiver:message_store');
|
|
28
|
+
|
|
29
|
+
constructor(private db: AztecKVStore) {
|
|
30
|
+
this.#messages = db.openMap('archiver_l1_to_l2_messages');
|
|
31
|
+
this.#pendingMessagesByFee = db.openCounter('archiver_messages_by_fee');
|
|
32
|
+
this.#lastL1BlockAddingMessages = db.openSingleton('archiver_last_l1_block_adding_messages');
|
|
33
|
+
this.#lastL1BlockCancellingMessages = db.openSingleton('archiver_last_l1_block_cancelling_messages');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets the last L1 block number that emitted new messages and the block that cancelled messages.
|
|
38
|
+
* @returns The last L1 block number processed
|
|
39
|
+
*/
|
|
40
|
+
getL1BlockNumber() {
|
|
41
|
+
return {
|
|
42
|
+
addedMessages: this.#lastL1BlockAddingMessages.get() ?? 0n,
|
|
43
|
+
cancelledMessages: this.#lastL1BlockCancellingMessages.get() ?? 0n,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Append new pending L1 to L2 messages to the store.
|
|
49
|
+
* @param messages - The L1 to L2 messages to be added to the store.
|
|
50
|
+
* @param l1BlockNumber - The L1 block number for which to add the messages.
|
|
51
|
+
* @returns True if the operation is successful.
|
|
52
|
+
*/
|
|
53
|
+
addPendingMessages(messages: L1ToL2Message[], l1BlockNumber: bigint): Promise<boolean> {
|
|
54
|
+
return this.db.transaction(() => {
|
|
55
|
+
const lastL1BlockNumber = this.#lastL1BlockAddingMessages.get() ?? 0n;
|
|
56
|
+
if (lastL1BlockNumber >= l1BlockNumber) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void this.#lastL1BlockAddingMessages.set(l1BlockNumber);
|
|
61
|
+
|
|
62
|
+
for (const message of messages) {
|
|
63
|
+
const messageKey = message.entryKey?.toString();
|
|
64
|
+
if (!messageKey) {
|
|
65
|
+
throw new Error('Message does not have an entry key');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void this.#messages.setIfNotExists(messageKey, {
|
|
69
|
+
message: message.toBuffer(),
|
|
70
|
+
fee: message.fee,
|
|
71
|
+
confirmed: false,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
void this.#pendingMessagesByFee.update([message.fee, messageKey], 1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Remove pending L1 to L2 messages from the store (if they were cancelled).
|
|
83
|
+
* @param messageKeys - The message keys to be removed from the store.
|
|
84
|
+
* @param l1BlockNumber - The L1 block number for which to remove the messages.
|
|
85
|
+
* @returns True if the operation is successful.
|
|
86
|
+
*/
|
|
87
|
+
cancelPendingMessages(messageKeys: Fr[], l1BlockNumber: bigint): Promise<boolean> {
|
|
88
|
+
return this.db.transaction(() => {
|
|
89
|
+
const lastL1BlockNumber = this.#lastL1BlockCancellingMessages.get() ?? 0n;
|
|
90
|
+
if (lastL1BlockNumber >= l1BlockNumber) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void this.#lastL1BlockCancellingMessages.set(l1BlockNumber);
|
|
95
|
+
|
|
96
|
+
for (const messageKey of messageKeys) {
|
|
97
|
+
const messageCtx = this.#messages.get(messageKey.toString());
|
|
98
|
+
if (!messageCtx) {
|
|
99
|
+
throw new Error(`Message ${messageKey.toString()} not found`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void this.#pendingMessagesByFee.update([messageCtx.fee, messageKey.toString()], -1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Messages that have been published in an L2 block are confirmed.
|
|
111
|
+
* Add them to the confirmed store, also remove them from the pending store.
|
|
112
|
+
* @param messageKeys - The message keys to be removed from the store.
|
|
113
|
+
* @returns True if the operation is successful.
|
|
114
|
+
*/
|
|
115
|
+
confirmPendingMessages(messageKeys: Fr[]): Promise<boolean> {
|
|
116
|
+
return this.db.transaction(() => {
|
|
117
|
+
for (const messageKey of messageKeys) {
|
|
118
|
+
const messageCtx = this.#messages.get(messageKey.toString());
|
|
119
|
+
if (!messageCtx) {
|
|
120
|
+
throw new Error(`Message ${messageKey.toString()} not found`);
|
|
121
|
+
}
|
|
122
|
+
messageCtx.confirmed = true;
|
|
123
|
+
|
|
124
|
+
void this.#messages.set(messageKey.toString(), messageCtx);
|
|
125
|
+
void this.#pendingMessagesByFee.update([messageCtx.fee, messageKey.toString()], -1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return true;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Gets the confirmed L1 to L2 message corresponding to the given message key.
|
|
134
|
+
* @param messageKey - The message key to look up.
|
|
135
|
+
* @returns The requested L1 to L2 message or throws if not found.
|
|
136
|
+
*/
|
|
137
|
+
getConfirmedMessage(messageKey: Fr): L1ToL2Message {
|
|
138
|
+
const messageCtx = this.#messages.get(messageKey.toString());
|
|
139
|
+
if (!messageCtx) {
|
|
140
|
+
throw new Error(`Message ${messageKey.toString()} not found`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!messageCtx.confirmed) {
|
|
144
|
+
throw new Error(`Message ${messageKey.toString()} not confirmed`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return L1ToL2Message.fromBuffer(messageCtx.message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee
|
|
152
|
+
* @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).
|
|
153
|
+
* @returns The requested L1 to L2 message keys.
|
|
154
|
+
*/
|
|
155
|
+
getPendingMessageKeysByFee(limit: number): Fr[] {
|
|
156
|
+
const messageKeys: Fr[] = [];
|
|
157
|
+
|
|
158
|
+
for (const [[_, messageKey], count] of this.#pendingMessagesByFee.entries({
|
|
159
|
+
reverse: true,
|
|
160
|
+
})) {
|
|
161
|
+
// put `count` copies of this message in the result list
|
|
162
|
+
messageKeys.push(...Array(count).fill(Fr.fromString(messageKey)));
|
|
163
|
+
if (messageKeys.length >= limit) {
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return messageKeys;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { L1ToL2Message } from '@aztec/circuit-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A simple in-memory implementation of an L1 to L2 message store
|
|
6
|
+
* that handles message duplication.
|
|
7
|
+
*/
|
|
8
|
+
export class L1ToL2MessageStore {
|
|
9
|
+
/**
|
|
10
|
+
* A map containing the message key to the corresponding L1 to L2
|
|
11
|
+
* messages (and the number of times the message has been seen).
|
|
12
|
+
*/
|
|
13
|
+
protected store: Map<bigint, L1ToL2MessageAndCount> = new Map();
|
|
14
|
+
|
|
15
|
+
constructor() {}
|
|
16
|
+
|
|
17
|
+
addMessage(messageKey: Fr, message: L1ToL2Message) {
|
|
18
|
+
const messageKeyBigInt = messageKey.toBigInt();
|
|
19
|
+
const msgAndCount = this.store.get(messageKeyBigInt);
|
|
20
|
+
if (msgAndCount) {
|
|
21
|
+
msgAndCount.count++;
|
|
22
|
+
} else {
|
|
23
|
+
this.store.set(messageKeyBigInt, { message, count: 1 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getMessage(messageKey: Fr): L1ToL2Message | undefined {
|
|
28
|
+
return this.store.get(messageKey.value)?.message;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getMessageAndCount(messageKey: Fr): L1ToL2MessageAndCount | undefined {
|
|
32
|
+
return this.store.get(messageKey.value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Specifically for the store that will hold pending messages
|
|
38
|
+
* for removing messages or fetching multiple messages.
|
|
39
|
+
*/
|
|
40
|
+
export class PendingL1ToL2MessageStore extends L1ToL2MessageStore {
|
|
41
|
+
getMessageKeys(limit: number): Fr[] {
|
|
42
|
+
if (limit < 1) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
// fetch `limit` number of messages from the store with the highest fee.
|
|
46
|
+
// Note the store has multiple of the same message. So if a message has count 2, include both of them in the result:
|
|
47
|
+
const messages: Fr[] = [];
|
|
48
|
+
const sortedMessages = Array.from(this.store.values()).sort((a, b) => b.message.fee - a.message.fee);
|
|
49
|
+
for (const messageAndCount of sortedMessages) {
|
|
50
|
+
for (let i = 0; i < messageAndCount.count; i++) {
|
|
51
|
+
messages.push(messageAndCount.message.entryKey!);
|
|
52
|
+
if (messages.length === limit) {
|
|
53
|
+
return messages;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return messages;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
removeMessage(messageKey: Fr) {
|
|
61
|
+
// ignore 0 - messageKey is a hash, so a 0 can probabilistically never occur. It is best to skip it.
|
|
62
|
+
if (messageKey.equals(Fr.ZERO)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const messageKeyBigInt = messageKey.value;
|
|
67
|
+
const msgAndCount = this.store.get(messageKeyBigInt);
|
|
68
|
+
if (!msgAndCount) {
|
|
69
|
+
throw new Error(`Unable to remove message: L1 to L2 Message with key ${messageKeyBigInt} not found in store`);
|
|
70
|
+
}
|
|
71
|
+
if (msgAndCount.count > 1) {
|
|
72
|
+
msgAndCount.count--;
|
|
73
|
+
} else {
|
|
74
|
+
this.store.delete(messageKeyBigInt);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Useful to keep track of the number of times a message has been seen.
|
|
81
|
+
*/
|
|
82
|
+
type L1ToL2MessageAndCount = {
|
|
83
|
+
/**
|
|
84
|
+
* The message.
|
|
85
|
+
*/
|
|
86
|
+
message: L1ToL2Message;
|
|
87
|
+
/**
|
|
88
|
+
* The number of times the message has been seen.
|
|
89
|
+
*/
|
|
90
|
+
count: number;
|
|
91
|
+
};
|