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