@aztec/archiver 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a
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 +7 -5
- package/dest/archiver.d.ts +49 -14
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +216 -68
- package/dest/config.d.ts +4 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -16
- package/dest/errors.d.ts +19 -13
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +32 -16
- package/dest/factory.d.ts +12 -6
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +31 -21
- package/dest/index.d.ts +9 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +8 -1
- package/dest/l1/calldata_retriever.d.ts +2 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +13 -4
- package/dest/l1/trace_tx.d.ts +12 -66
- package/dest/l1/trace_tx.d.ts.map +1 -1
- package/dest/modules/contract_data_source_adapter.d.ts +25 -0
- package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
- package/dest/modules/contract_data_source_adapter.js +40 -0
- package/dest/modules/data_source_base.d.ts +71 -49
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +269 -143
- package/dest/modules/data_store_updater.d.ts +18 -6
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +108 -59
- package/dest/modules/instrumentation.js +3 -1
- package/dest/modules/l1_synchronizer.d.ts +6 -5
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +203 -100
- package/dest/modules/outbox_trees_resolver.d.ts +62 -0
- package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
- package/dest/modules/outbox_trees_resolver.js +162 -0
- package/dest/modules/validation.d.ts +4 -3
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +4 -4
- package/dest/store/block_store.d.ts +155 -98
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +473 -265
- package/dest/store/contract_class_store.d.ts +17 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +17 -1
- package/dest/store/contract_instance_store.d.ts +28 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +31 -0
- package/dest/store/data_stores.d.ts +68 -0
- package/dest/store/data_stores.d.ts.map +1 -0
- package/dest/store/data_stores.js +54 -0
- package/dest/store/function_names_cache.d.ts +17 -0
- package/dest/store/function_names_cache.d.ts.map +1 -0
- package/dest/store/function_names_cache.js +30 -0
- package/dest/store/l2_tips_cache.d.ts +13 -8
- package/dest/store/l2_tips_cache.d.ts.map +1 -1
- package/dest/store/l2_tips_cache.js +13 -96
- package/dest/store/log_store.d.ts +42 -40
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +257 -478
- package/dest/store/log_store_codec.d.ts +70 -0
- package/dest/store/log_store_codec.d.ts.map +1 -0
- package/dest/store/log_store_codec.js +101 -0
- package/dest/store/message_store.d.ts +9 -3
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +31 -1
- package/dest/test/fake_l1_state.d.ts +2 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +25 -8
- package/dest/test/mock_l2_block_source.d.ts +47 -51
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +223 -185
- package/dest/test/noop_l1_archiver.d.ts +10 -6
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +19 -8
- package/package.json +14 -14
- package/src/archiver.ts +265 -72
- package/src/config.ts +12 -19
- package/src/errors.ts +46 -23
- package/src/factory.ts +38 -20
- package/src/index.ts +16 -1
- package/src/l1/calldata_retriever.ts +14 -4
- package/src/modules/contract_data_source_adapter.ts +55 -0
- package/src/modules/data_source_base.ts +335 -187
- package/src/modules/data_store_updater.ts +118 -61
- package/src/modules/instrumentation.ts +1 -1
- package/src/modules/l1_synchronizer.ts +278 -120
- package/src/modules/outbox_trees_resolver.ts +199 -0
- package/src/modules/validation.ts +8 -7
- package/src/store/block_store.ts +566 -323
- package/src/store/contract_class_store.ts +28 -2
- package/src/store/contract_instance_store.ts +43 -0
- package/src/store/data_stores.ts +104 -0
- package/src/store/function_names_cache.ts +37 -0
- package/src/store/l2_tips_cache.ts +16 -115
- package/src/store/log_store.ts +296 -650
- package/src/store/log_store_codec.ts +132 -0
- package/src/store/message_store.ts +35 -2
- package/src/test/fake_l1_state.ts +24 -14
- package/src/test/mock_l2_block_source.ts +271 -239
- package/src/test/noop_l1_archiver.ts +39 -8
- package/dest/store/kv_archiver_store.d.ts +0 -383
- package/dest/store/kv_archiver_store.d.ts.map +0 -1
- package/dest/store/kv_archiver_store.js +0 -501
- package/src/store/kv_archiver_store.ts +0 -728
|
@@ -1,222 +1,348 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import {
|
|
1
|
+
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { Body, L2Block } from '@aztec/stdlib/block';
|
|
4
4
|
import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
5
|
-
import { getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
5
|
+
import { getEpochAtSlot, getEpochNumberAtTimestamp, getLastL1SlotTimestampForL2Slot, getProofSubmissionDeadlineEpoch, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
+
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
6
7
|
/**
|
|
7
|
-
* Abstract base class implementing ArchiverDataSource using a
|
|
8
|
-
* Provides implementations for all
|
|
9
|
-
*
|
|
8
|
+
* Abstract base class implementing ArchiverDataSource using a bundle of archiver substores.
|
|
9
|
+
* Provides implementations for all read-side methods and declares abstract methods for
|
|
10
|
+
* L1-dependent functionality that subclasses must implement.
|
|
10
11
|
*/ export class ArchiverDataSourceBase {
|
|
11
|
-
|
|
12
|
+
stores;
|
|
12
13
|
l1Constants;
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
/** The injected genesis block header. */ initialHeader;
|
|
15
|
+
/** Precomputed hash of the initial header, exposed via {@link getGenesisBlockHash}. */ initialBlockHash;
|
|
16
|
+
/** Archive root after block 0 was appended — read from L1 (`Rollup.getGenesisArchiveTreeRoot`). */ genesisArchiveRoot;
|
|
17
|
+
/** Memoized synthetic genesis block — callers rely on referential identity for caching. */ genesisBlock;
|
|
18
|
+
/** Memoized synthetic genesis block data — kept consistent with {@link genesisBlock}. */ genesisBlockData;
|
|
19
|
+
constructor(stores, l1Constants, initialHeader, initialBlockHash, genesisArchiveRoot){
|
|
20
|
+
this.stores = stores;
|
|
15
21
|
this.l1Constants = l1Constants;
|
|
22
|
+
this.initialHeader = initialHeader;
|
|
23
|
+
this.initialBlockHash = initialBlockHash;
|
|
24
|
+
this.genesisArchiveRoot = genesisArchiveRoot;
|
|
25
|
+
const genesisArchive = new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1);
|
|
26
|
+
this.genesisBlock = new L2Block(genesisArchive, initialHeader, Body.empty(), CheckpointNumber.ZERO, IndexWithinCheckpoint(0));
|
|
27
|
+
this.genesisBlockData = {
|
|
28
|
+
header: initialHeader,
|
|
29
|
+
archive: genesisArchive,
|
|
30
|
+
blockHash: initialBlockHash,
|
|
31
|
+
checkpointNumber: CheckpointNumber.ZERO,
|
|
32
|
+
indexWithinCheckpoint: IndexWithinCheckpoint(0)
|
|
33
|
+
};
|
|
16
34
|
}
|
|
17
|
-
|
|
18
|
-
return this.
|
|
19
|
-
}
|
|
20
|
-
getSynchedCheckpointNumber() {
|
|
21
|
-
return this.store.getSynchedCheckpointNumber();
|
|
35
|
+
/** Returns the precomputed hash of the genesis block header. */ getGenesisBlockHash() {
|
|
36
|
+
return this.initialBlockHash;
|
|
22
37
|
}
|
|
23
|
-
|
|
24
|
-
return this.
|
|
38
|
+
/** Returns the synthetic genesis L2Block (memoized — same instance across calls). */ getGenesisBlock() {
|
|
39
|
+
return this.genesisBlock;
|
|
25
40
|
}
|
|
26
|
-
|
|
27
|
-
return this.
|
|
41
|
+
/** Returns genesis block data (memoized — same instance across calls). */ getGenesisBlockData() {
|
|
42
|
+
return this.genesisBlockData;
|
|
28
43
|
}
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Type guard distinguishing the genesis sentinel from a {@link ResolvedBlockQuery}.
|
|
46
|
+
* `resolveBlockQuery` already rewrites every genesis-matching shape to the sentinel,
|
|
47
|
+
* so callers only need this single sync check.
|
|
48
|
+
*/ isGenesisBlockQuery(query) {
|
|
49
|
+
return 'genesis' in query;
|
|
31
50
|
}
|
|
32
|
-
async
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return undefined;
|
|
51
|
+
async isPruneDueAtSlot(slot) {
|
|
52
|
+
if (!this.l1Constants) {
|
|
53
|
+
throw new Error('isPruneDueAtSlot requires l1Constants');
|
|
36
54
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
const tips = await this.getL2Tips();
|
|
56
|
+
const proven = tips.proven.checkpoint.number;
|
|
57
|
+
const pending = tips.checkpointed.checkpoint.number;
|
|
58
|
+
if (pending === proven) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const oldestUnproven = await this.getCheckpointData({
|
|
62
|
+
number: CheckpointNumber(Number(proven) + 1)
|
|
63
|
+
});
|
|
64
|
+
if (!oldestUnproven) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const slotTs = getLastL1SlotTimestampForL2Slot(slot, this.l1Constants);
|
|
68
|
+
const slotEpoch = getEpochNumberAtTimestamp(slotTs, this.l1Constants);
|
|
69
|
+
const oldestUnprovenEpoch = getEpochAtSlot(oldestUnproven.header.slotNumber, this.l1Constants);
|
|
70
|
+
const deadlineEpoch = getProofSubmissionDeadlineEpoch(oldestUnprovenEpoch, this.l1Constants);
|
|
71
|
+
return slotEpoch >= deadlineEpoch;
|
|
42
72
|
}
|
|
43
|
-
|
|
44
|
-
return this.
|
|
73
|
+
getCheckpointNumber() {
|
|
74
|
+
return this.stores.blocks.getLatestCheckpointNumber();
|
|
45
75
|
}
|
|
46
|
-
|
|
47
|
-
return this.
|
|
76
|
+
getProvenCheckpointNumber() {
|
|
77
|
+
return this.stores.blocks.getProvenCheckpointNumber();
|
|
48
78
|
}
|
|
49
|
-
async
|
|
50
|
-
if (
|
|
51
|
-
|
|
79
|
+
async getBlockNumber(query) {
|
|
80
|
+
if (!query) {
|
|
81
|
+
return this.stores.blocks.getLatestL2BlockNumber();
|
|
52
82
|
}
|
|
53
|
-
|
|
83
|
+
const resolved = await this.resolveBlockQuery(query);
|
|
84
|
+
if (resolved === undefined) {
|
|
54
85
|
return undefined;
|
|
55
86
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
87
|
+
if (this.isGenesisBlockQuery(resolved)) {
|
|
88
|
+
return BlockNumber.ZERO;
|
|
89
|
+
}
|
|
90
|
+
return this.stores.blocks.getBlockNumber(resolved);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolves a {@link CheckpointQuery} to a concrete `CheckpointNumber`, or undefined when the
|
|
94
|
+
* query refers to a position that has no checkpoint yet (e.g. `{ slot }` not found).
|
|
95
|
+
*/ resolveCheckpointQuery(query) {
|
|
96
|
+
if ('number' in query) {
|
|
97
|
+
return Promise.resolve(query.number);
|
|
98
|
+
}
|
|
99
|
+
if ('slot' in query) {
|
|
100
|
+
return this.stores.blocks.getCheckpointNumberBySlot(query.slot);
|
|
101
|
+
}
|
|
102
|
+
// tag variant
|
|
103
|
+
switch(query.tag){
|
|
104
|
+
case 'checkpointed':
|
|
105
|
+
return this.stores.blocks.getLatestCheckpointNumber();
|
|
106
|
+
case 'proven':
|
|
107
|
+
return this.stores.blocks.getProvenCheckpointNumber();
|
|
108
|
+
case 'finalized':
|
|
109
|
+
return this.stores.blocks.getFinalizedCheckpointNumber();
|
|
59
110
|
}
|
|
60
|
-
return checkpoint.header;
|
|
61
111
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Resolves a {@link CheckpointsQuery} to a concrete `{from, limit}` pair used by BlockStore,
|
|
114
|
+
* or undefined when the epoch has no checkpoints.
|
|
115
|
+
*/ async resolveCheckpointsQuery(query) {
|
|
116
|
+
if ('from' in query) {
|
|
117
|
+
return query;
|
|
118
|
+
}
|
|
119
|
+
const numbers = await this.getCheckpointNumbersForEpoch(query.epoch);
|
|
120
|
+
if (numbers.length === 0) {
|
|
65
121
|
return undefined;
|
|
66
122
|
}
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
return {
|
|
124
|
+
from: numbers[0],
|
|
125
|
+
limit: numbers.length
|
|
126
|
+
};
|
|
71
127
|
}
|
|
72
|
-
|
|
73
|
-
|
|
128
|
+
async getCheckpoint(query) {
|
|
129
|
+
const number = await this.resolveCheckpointQuery(query);
|
|
130
|
+
if (number === undefined || number === 0) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const data = await this.stores.blocks.getCheckpointData(number);
|
|
134
|
+
if (!data) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return this.getPublishedCheckpointFromCheckpointData(data);
|
|
74
138
|
}
|
|
75
|
-
|
|
76
|
-
|
|
139
|
+
async getCheckpoints(query) {
|
|
140
|
+
const resolved = await this.resolveCheckpointsQuery(query);
|
|
141
|
+
if (!resolved) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const checkpoints = await this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
|
|
145
|
+
return Promise.all(checkpoints.map((ch)=>this.getPublishedCheckpointFromCheckpointData(ch)));
|
|
77
146
|
}
|
|
78
|
-
|
|
79
|
-
|
|
147
|
+
async getCheckpointData(query) {
|
|
148
|
+
const number = await this.resolveCheckpointQuery(query);
|
|
149
|
+
if (number === undefined || number === 0) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
return this.stores.blocks.getCheckpointData(number);
|
|
80
153
|
}
|
|
81
|
-
|
|
82
|
-
|
|
154
|
+
async getCheckpointsData(query) {
|
|
155
|
+
const resolved = await this.resolveCheckpointsQuery(query);
|
|
156
|
+
if (!resolved) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
return this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
|
|
83
160
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
number = await this.store.getLatestBlockNumber();
|
|
161
|
+
getProposedCheckpointData(query) {
|
|
162
|
+
if (!query || 'tag' in query) {
|
|
163
|
+
return this.stores.blocks.getLastProposedCheckpoint();
|
|
88
164
|
}
|
|
89
|
-
if (number
|
|
90
|
-
return
|
|
165
|
+
if ('number' in query) {
|
|
166
|
+
return this.stores.blocks.getProposedCheckpointByNumber(query.number);
|
|
91
167
|
}
|
|
92
|
-
|
|
93
|
-
return publishedBlock;
|
|
168
|
+
return this.stores.blocks.getProposedCheckpointBySlot(query.slot);
|
|
94
169
|
}
|
|
95
170
|
getTxEffect(txHash) {
|
|
96
|
-
return this.
|
|
97
|
-
}
|
|
98
|
-
getSettledTxReceipt(txHash) {
|
|
99
|
-
return this.store.getSettledTxReceipt(txHash, this.l1Constants);
|
|
100
|
-
}
|
|
101
|
-
getProposedCheckpoint() {
|
|
102
|
-
return this.store.getProposedCheckpoint();
|
|
103
|
-
}
|
|
104
|
-
getProposedCheckpointOnly() {
|
|
105
|
-
return this.store.getProposedCheckpointOnly();
|
|
171
|
+
return this.stores.blocks.getTxEffect(txHash);
|
|
106
172
|
}
|
|
107
173
|
isPendingChainInvalid() {
|
|
108
174
|
return this.getPendingChainValidationStatus().then((status)=>!status.valid);
|
|
109
175
|
}
|
|
110
176
|
async getPendingChainValidationStatus() {
|
|
111
|
-
return await this.
|
|
177
|
+
return await this.stores.blocks.getPendingChainValidationStatus() ?? {
|
|
112
178
|
valid: true
|
|
113
179
|
};
|
|
114
180
|
}
|
|
115
|
-
getPrivateLogsByTags(
|
|
116
|
-
return this.
|
|
117
|
-
}
|
|
118
|
-
getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber) {
|
|
119
|
-
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
120
|
-
}
|
|
121
|
-
getPublicLogs(filter) {
|
|
122
|
-
return this.store.getPublicLogs(filter);
|
|
181
|
+
getPrivateLogsByTags(query) {
|
|
182
|
+
return this.stores.logs.getPrivateLogsByTags(query);
|
|
123
183
|
}
|
|
124
|
-
|
|
125
|
-
return this.
|
|
184
|
+
getPublicLogsByTags(query) {
|
|
185
|
+
return this.stores.logs.getPublicLogsByTags(query);
|
|
126
186
|
}
|
|
127
187
|
getContractClass(id) {
|
|
128
|
-
return this.
|
|
188
|
+
return this.stores.contractClasses.getContractClass(id);
|
|
129
189
|
}
|
|
130
190
|
getBytecodeCommitment(id) {
|
|
131
|
-
return this.
|
|
191
|
+
return this.stores.contractClasses.getBytecodeCommitment(id);
|
|
132
192
|
}
|
|
133
193
|
async getContract(address, maybeTimestamp) {
|
|
134
194
|
let timestamp;
|
|
135
195
|
if (maybeTimestamp === undefined) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
196
|
+
const latestBlockData = await this.getBlockData({
|
|
197
|
+
tag: 'proposed'
|
|
198
|
+
});
|
|
199
|
+
timestamp = latestBlockData ? latestBlockData.header.globalVariables.timestamp : 0n;
|
|
139
200
|
} else {
|
|
140
201
|
timestamp = maybeTimestamp;
|
|
141
202
|
}
|
|
142
|
-
return this.
|
|
203
|
+
return this.stores.contractInstances.getContractInstance(address, timestamp);
|
|
143
204
|
}
|
|
144
205
|
getContractClassIds() {
|
|
145
|
-
return this.
|
|
206
|
+
return this.stores.contractClasses.getContractClassIds();
|
|
146
207
|
}
|
|
147
|
-
getDebugFunctionName(
|
|
148
|
-
return this.
|
|
208
|
+
/** Looks up a public function name given a selector. */ getDebugFunctionName(_address, selector) {
|
|
209
|
+
return Promise.resolve(this.stores.functionNames.get(selector));
|
|
149
210
|
}
|
|
150
|
-
registerContractFunctionSignatures(signatures) {
|
|
151
|
-
return this.
|
|
211
|
+
/** Register public function signatures so they can be looked up by selector. */ registerContractFunctionSignatures(signatures) {
|
|
212
|
+
return this.stores.functionNames.register(signatures);
|
|
152
213
|
}
|
|
153
214
|
getL1ToL2Messages(checkpointNumber) {
|
|
154
|
-
return this.
|
|
215
|
+
return this.stores.messages.getL1ToL2Messages(checkpointNumber);
|
|
155
216
|
}
|
|
156
217
|
getL1ToL2MessageIndex(l1ToL2Message) {
|
|
157
|
-
return this.
|
|
158
|
-
}
|
|
159
|
-
async getCheckpoints(checkpointNumber, limit) {
|
|
160
|
-
const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
|
|
161
|
-
return Promise.all(checkpoints.map((ch)=>this.getPublishedCheckpointFromCheckpointData(ch)));
|
|
218
|
+
return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message);
|
|
162
219
|
}
|
|
163
220
|
async getPublishedCheckpointFromCheckpointData(checkpoint) {
|
|
164
|
-
const blocksForCheckpoint = await this.
|
|
221
|
+
const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber);
|
|
165
222
|
if (!blocksForCheckpoint) {
|
|
166
223
|
throw new Error(`Blocks for checkpoint ${checkpoint.checkpointNumber} not found`);
|
|
167
224
|
}
|
|
168
|
-
const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber);
|
|
225
|
+
const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber, checkpoint.feeAssetPriceModifier);
|
|
169
226
|
return new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations);
|
|
170
227
|
}
|
|
171
228
|
getBlocksForSlot(slotNumber) {
|
|
172
|
-
return this.
|
|
173
|
-
}
|
|
174
|
-
async getCheckpointedBlocksForEpoch(epochNumber) {
|
|
175
|
-
const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
|
|
176
|
-
const blocks = await Promise.all(checkpointsData.flatMap((checkpoint)=>range(checkpoint.blockCount, checkpoint.startBlock).map((blockNumber)=>this.getCheckpointedBlock(BlockNumber(blockNumber)))));
|
|
177
|
-
return blocks.filter(isDefined);
|
|
178
|
-
}
|
|
179
|
-
async getCheckpointedBlockHeadersForEpoch(epochNumber) {
|
|
180
|
-
const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
|
|
181
|
-
const blocks = await Promise.all(checkpointsData.flatMap((checkpoint)=>range(checkpoint.blockCount, checkpoint.startBlock).map((blockNumber)=>this.getBlockHeader(BlockNumber(blockNumber)))));
|
|
182
|
-
return blocks.filter(isDefined);
|
|
229
|
+
return this.stores.blocks.getBlocksForSlot(slotNumber);
|
|
183
230
|
}
|
|
184
|
-
|
|
185
|
-
const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
|
|
186
|
-
return Promise.all(checkpointsData.map((data)=>this.getPublishedCheckpointFromCheckpointData(data).then((p)=>p.checkpoint)));
|
|
187
|
-
}
|
|
188
|
-
/** Returns checkpoint data for all checkpoints whose slot falls within the given epoch. */ getCheckpointsDataForEpoch(epochNumber) {
|
|
231
|
+
/** Returns just the checkpoint numbers for all checkpoints whose slot falls within the given epoch. */ getCheckpointNumbersForEpoch(epochNumber) {
|
|
189
232
|
if (!this.l1Constants) {
|
|
190
233
|
throw new Error('L1 constants not set');
|
|
191
234
|
}
|
|
192
235
|
const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
|
|
193
|
-
return this.
|
|
236
|
+
return this.stores.blocks.getCheckpointNumbersForSlotRange(start, end);
|
|
194
237
|
}
|
|
195
|
-
async getBlock(
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
|
|
238
|
+
async getBlock(query) {
|
|
239
|
+
const resolved = await this.resolveBlockQuery(query);
|
|
240
|
+
if (resolved === undefined) {
|
|
241
|
+
return undefined;
|
|
199
242
|
}
|
|
200
|
-
if (
|
|
243
|
+
if (this.isGenesisBlockQuery(resolved)) {
|
|
244
|
+
return this.getGenesisBlock();
|
|
245
|
+
}
|
|
246
|
+
return this.stores.blocks.getBlock(resolved);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Range queries iterate physical blocks only; the genesis block is NOT prepended.
|
|
250
|
+
* `L2BlockStream` consumers (`world-state.handleL2Blocks`, etc.) emit `blocks-added` events for
|
|
251
|
+
* real blocks and would be surprised by a synthetic block 0. Use {@link getBlock} or
|
|
252
|
+
* {@link getBlockData} for genesis-aware single-block lookups.
|
|
253
|
+
*/ async getBlocks(query) {
|
|
254
|
+
const resolved = await this.resolveBlocksQuery(query);
|
|
255
|
+
return resolved ? this.stores.blocks.getBlocks(resolved) : [];
|
|
256
|
+
}
|
|
257
|
+
async getBlockData(query) {
|
|
258
|
+
const resolved = await this.resolveBlockQuery(query);
|
|
259
|
+
if (resolved === undefined) {
|
|
201
260
|
return undefined;
|
|
202
261
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return this.
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
262
|
+
if (this.isGenesisBlockQuery(resolved)) {
|
|
263
|
+
return this.getGenesisBlockData();
|
|
264
|
+
}
|
|
265
|
+
return this.stores.blocks.getBlockData(resolved);
|
|
266
|
+
}
|
|
267
|
+
/** See {@link getBlocks} — range queries do not prepend the genesis block. */ async getBlocksData(query) {
|
|
268
|
+
const resolved = await this.resolveBlocksQuery(query);
|
|
269
|
+
return resolved ? this.stores.blocks.getBlocksData(resolved) : [];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Resolves a {@link BlockQuery} to either the genesis sentinel or a {@link ResolvedBlockQuery}
|
|
273
|
+
* understood by BlockStore. Detects every shape that points at block 0 — `{number:0}`,
|
|
274
|
+
* `{hash}` matching the initial header, `{archive}` matching the post-genesis archive root,
|
|
275
|
+
* and `{tag}` resolving to 0 — and rewrites them to the sentinel so callers branch once.
|
|
276
|
+
*/ async resolveBlockQuery(query) {
|
|
277
|
+
if ('number' in query) {
|
|
278
|
+
return query.number === BlockNumber.ZERO ? {
|
|
279
|
+
genesis: true
|
|
280
|
+
} : query;
|
|
281
|
+
}
|
|
282
|
+
if ('hash' in query) {
|
|
283
|
+
return query.hash.equals(this.initialBlockHash) ? {
|
|
284
|
+
genesis: true
|
|
285
|
+
} : query;
|
|
286
|
+
}
|
|
287
|
+
if ('archive' in query) {
|
|
288
|
+
return query.archive.equals(this.genesisArchiveRoot) ? {
|
|
289
|
+
genesis: true
|
|
290
|
+
} : query;
|
|
291
|
+
}
|
|
292
|
+
const number = await this.resolveBlockTag(query.tag);
|
|
293
|
+
if (number === BlockNumber.ZERO) {
|
|
294
|
+
return {
|
|
295
|
+
genesis: true
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
number
|
|
300
|
+
};
|
|
213
301
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
302
|
+
/** Maps a {@link BlockTag} to the matching block number for the current chain state. */ resolveBlockTag(tag) {
|
|
303
|
+
switch(tag){
|
|
304
|
+
case 'latest':
|
|
305
|
+
case 'proposed':
|
|
306
|
+
return this.stores.blocks.getLatestL2BlockNumber();
|
|
307
|
+
case 'checkpointed':
|
|
308
|
+
return this.stores.blocks.getCheckpointedL2BlockNumber();
|
|
309
|
+
case 'proven':
|
|
310
|
+
return this.stores.blocks.getProvenBlockNumber();
|
|
311
|
+
case 'finalized':
|
|
312
|
+
return this.stores.blocks.getFinalizedL2BlockNumber();
|
|
313
|
+
}
|
|
217
314
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
315
|
+
/**
|
|
316
|
+
* Converts an epoch-based BlocksQuery to a from/limit query using l1Constants.
|
|
317
|
+
* Returns undefined when the epoch has no checkpoints, so callers can return [] without
|
|
318
|
+
* entering BlockStore. Reads only the two endpoint checkpoints rather than the whole epoch.
|
|
319
|
+
*/ async resolveBlocksQuery(query) {
|
|
320
|
+
if (!('epoch' in query)) {
|
|
321
|
+
if (query.from < INITIAL_L2_BLOCK_NUM) {
|
|
322
|
+
throw new Error(`getBlocks/getBlocksData: 'from' must be >= ${INITIAL_L2_BLOCK_NUM}, got ${query.from}. ` + `Use getBlock({number:0})/getBlockData({number:0}) for genesis-aware single-block lookups.`);
|
|
323
|
+
}
|
|
324
|
+
return query;
|
|
325
|
+
}
|
|
326
|
+
const checkpointNumbers = await this.getCheckpointNumbersForEpoch(query.epoch);
|
|
327
|
+
if (checkpointNumbers.length === 0) {
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
const firstNumber = checkpointNumbers[0];
|
|
331
|
+
const lastNumber = checkpointNumbers[checkpointNumbers.length - 1];
|
|
332
|
+
const first = await this.stores.blocks.getCheckpointData(firstNumber);
|
|
333
|
+
if (!first) {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
const last = firstNumber === lastNumber ? first : await this.stores.blocks.getCheckpointData(lastNumber);
|
|
337
|
+
if (!last) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
const from = BlockNumber(first.startBlock);
|
|
341
|
+
const limit = last.startBlock + last.blockCount - first.startBlock;
|
|
342
|
+
return {
|
|
343
|
+
from,
|
|
344
|
+
limit,
|
|
345
|
+
onlyCheckpointed: true
|
|
346
|
+
};
|
|
221
347
|
}
|
|
222
348
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { CommitteeAttestation, L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
3
3
|
import { type L1PublishedData, type ProposedCheckpointInput, type PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ArchiverDataStores } from '../store/data_stores.js';
|
|
5
5
|
import type { L2TipsCache } from '../store/l2_tips_cache.js';
|
|
6
6
|
/** Result of adding checkpoints with information about any pruned blocks. */
|
|
7
7
|
type ReconcileCheckpointsResult = {
|
|
@@ -12,11 +12,11 @@ type ReconcileCheckpointsResult = {
|
|
|
12
12
|
};
|
|
13
13
|
/** Archiver helper module to handle updates to the data store. */
|
|
14
14
|
export declare class ArchiverDataStoreUpdater {
|
|
15
|
-
private
|
|
15
|
+
private stores;
|
|
16
16
|
private l2TipsCache?;
|
|
17
17
|
private opts;
|
|
18
18
|
private readonly log;
|
|
19
|
-
constructor(
|
|
19
|
+
constructor(stores: ArchiverDataStores, l2TipsCache?: L2TipsCache | undefined, opts?: {
|
|
20
20
|
rollupManaLimit?: number;
|
|
21
21
|
});
|
|
22
22
|
/**
|
|
@@ -46,8 +46,8 @@ export declare class ArchiverDataStoreUpdater {
|
|
|
46
46
|
l1: L1PublishedData;
|
|
47
47
|
attestations: CommitteeAttestation[];
|
|
48
48
|
checkpoint: PublishedCheckpoint;
|
|
49
|
-
}): Promise<ReconcileCheckpointsResult>;
|
|
50
|
-
|
|
49
|
+
}, evictProposedFrom?: CheckpointNumber): Promise<ReconcileCheckpointsResult>;
|
|
50
|
+
addProposedCheckpoint(proposedCheckpoint: ProposedCheckpointInput): Promise<void>;
|
|
51
51
|
private pruneMismatchingLocalBlocks;
|
|
52
52
|
/**
|
|
53
53
|
* Removes all uncheckpointed blocks strictly after the specified block number and cleans up associated contract data.
|
|
@@ -59,6 +59,18 @@ export declare class ArchiverDataStoreUpdater {
|
|
|
59
59
|
* @throws Error if any block to be removed is checkpointed.
|
|
60
60
|
*/
|
|
61
61
|
removeUncheckpointedBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Removes all blocks without a proposed checkpoint strictly after the specified block number and cleans up associated contract data.
|
|
64
|
+
* This handles removal of provisionally added blocks along with their contract classes/instances.
|
|
65
|
+
* Verifies that each block being removed is not part of a stored checkpoint (proposed or not).
|
|
66
|
+
* This differs from `removeUncheckpointedBlocksAfter` in that it also checks proposed checkpoints.
|
|
67
|
+
*
|
|
68
|
+
* @param blockNumber - Remove all blocks with number greater than this.
|
|
69
|
+
* @returns The removed blocks.
|
|
70
|
+
* @throws Error if any block to be removed is checkpointed.
|
|
71
|
+
*/
|
|
72
|
+
removeBlocksWithoutProposedCheckpointAfter(blockNumber: BlockNumber): Promise<L2Block[]>;
|
|
73
|
+
private evictProposedCheckpointsForPrunedBlocks;
|
|
62
74
|
private removeBlocksAfter;
|
|
63
75
|
/**
|
|
64
76
|
* Removes all checkpoints after the given checkpoint number.
|
|
@@ -90,4 +102,4 @@ export declare class ArchiverDataStoreUpdater {
|
|
|
90
102
|
private updateUpdatedContractInstances;
|
|
91
103
|
}
|
|
92
104
|
export {};
|
|
93
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
105
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YV9zdG9yZV91cGRhdGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kdWxlcy9kYXRhX3N0b3JlX3VwZGF0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBUWhGLE9BQU8sS0FBSyxFQUFFLG9CQUFvQixFQUFFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ25HLE9BQU8sRUFDTCxLQUFLLGVBQWUsRUFDcEIsS0FBSyx1QkFBdUIsRUFDNUIsS0FBSyxtQkFBbUIsRUFFekIsTUFBTSwwQkFBMEIsQ0FBQztBQVNsQyxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2xFLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBUTdELDZFQUE2RTtBQUM3RSxLQUFLLDBCQUEwQixHQUFHO0lBQ2hDLG1FQUFtRTtJQUNuRSxZQUFZLEVBQUUsT0FBTyxFQUFFLEdBQUcsU0FBUyxDQUFDO0lBQ3BDLGlGQUFpRjtJQUNqRiw4QkFBOEIsRUFBRSxXQUFXLEdBQUcsU0FBUyxDQUFDO0NBQ3pELENBQUM7QUFFRixrRUFBa0U7QUFDbEUscUJBQWEsd0JBQXdCO0lBSWpDLE9BQU8sQ0FBQyxNQUFNO0lBQ2QsT0FBTyxDQUFDLFdBQVcsQ0FBQztJQUNwQixPQUFPLENBQUMsSUFBSTtJQUxkLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUEwQztJQUU5RCxZQUNVLE1BQU0sRUFBRSxrQkFBa0IsRUFDMUIsV0FBVyxDQUFDLHlCQUFhLEVBQ3pCLElBQUksR0FBRTtRQUFFLGVBQWUsQ0FBQyxFQUFFLE1BQU0sQ0FBQTtLQUFPLEVBQzdDO0lBRUo7Ozs7Ozs7O09BUUc7SUFDVSxnQkFBZ0IsQ0FDM0IsS0FBSyxFQUFFLE9BQU8sRUFDZCw0QkFBNEIsQ0FBQyxFQUFFLHdCQUF3QixHQUN0RCxPQUFPLENBQUMsT0FBTyxDQUFDLENBa0JsQjtJQUVEOzs7Ozs7Ozs7Ozs7T0FZRztJQUNVLGNBQWMsQ0FDekIsV0FBVyxFQUFFLG1CQUFtQixFQUFFLEVBQ2xDLDRCQUE0QixDQUFDLEVBQUUsd0JBQXdCLEVBQ3ZELGVBQWUsQ0FBQyxFQUFFO1FBQ2hCLEVBQUUsRUFBRSxlQUFlLENBQUM7UUFDcEIsWUFBWSxFQUFFLG9CQUFvQixFQUFFLENBQUM7UUFDckMsVUFBVSxFQUFFLG1CQUFtQixDQUFDO0tBQ2pDLEVBQ0QsaUJBQWlCLENBQUMsRUFBRSxnQkFBZ0IsR0FDbkMsT0FBTyxDQUFDLDBCQUEwQixDQUFDLENBOENyQztJQUVZLHFCQUFxQixDQUFDLGtCQUFrQixFQUFFLHVCQUF1QixpQkFNN0U7WUFnQmEsMkJBQTJCO0lBdUV6Qzs7Ozs7Ozs7T0FRRztJQUNVLCtCQUErQixDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBaUJ6RjtJQUVEOzs7Ozs7Ozs7T0FTRztJQUNVLDBDQUEwQyxDQUFDLFdBQVcsRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBY3BHO1lBT2EsdUNBQXVDO1lBWXZDLGlCQUFpQjtJQWEvQjs7Ozs7Ozs7T0FRRztJQUNVLHNCQUFzQixDQUFDLGdCQUFnQixFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FnQnhGO0lBRUQ7OztPQUdHO0lBQ1UseUJBQXlCLENBQUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUt4RjtJQUVEOzs7T0FHRztJQUNVLDRCQUE0QixDQUFDLGdCQUFnQixFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FLM0Y7SUFFRCw2REFBNkQ7SUFDN0QsT0FBTyxDQUFDLG1CQUFtQjtJQUkzQixxREFBcUQ7SUFDckQsT0FBTyxDQUFDLHdCQUF3QjtZQUtsQixzQkFBc0I7WUFpQnRCLDhCQUE4QjtZQStDOUIsK0JBQStCO1lBMEMvQiw4QkFBOEI7Q0FzQjdDIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data_store_updater.d.ts","sourceRoot":"","sources":["../../src/modules/data_store_updater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAQhF,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACnG,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAEzB,MAAM,0BAA0B,CAAC;AASlC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"data_store_updater.d.ts","sourceRoot":"","sources":["../../src/modules/data_store_updater.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAQhF,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACnG,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAEzB,MAAM,0BAA0B,CAAC;AASlC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAQ7D,6EAA6E;AAC7E,KAAK,0BAA0B,GAAG;IAChC,mEAAmE;IACnE,YAAY,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IACpC,iFAAiF;IACjF,8BAA8B,EAAE,WAAW,GAAG,SAAS,CAAC;CACzD,CAAC;AAEF,kEAAkE;AAClE,qBAAa,wBAAwB;IAIjC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,IAAI;IALd,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA0C;IAE9D,YACU,MAAM,EAAE,kBAAkB,EAC1B,WAAW,CAAC,yBAAa,EACzB,IAAI,GAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAO,EAC7C;IAEJ;;;;;;;;OAQG;IACU,gBAAgB,CAC3B,KAAK,EAAE,OAAO,EACd,4BAA4B,CAAC,EAAE,wBAAwB,GACtD,OAAO,CAAC,OAAO,CAAC,CAkBlB;IAED;;;;;;;;;;;;OAYG;IACU,cAAc,CACzB,WAAW,EAAE,mBAAmB,EAAE,EAClC,4BAA4B,CAAC,EAAE,wBAAwB,EACvD,eAAe,CAAC,EAAE;QAChB,EAAE,EAAE,eAAe,CAAC;QACpB,YAAY,EAAE,oBAAoB,EAAE,CAAC;QACrC,UAAU,EAAE,mBAAmB,CAAC;KACjC,EACD,iBAAiB,CAAC,EAAE,gBAAgB,GACnC,OAAO,CAAC,0BAA0B,CAAC,CA8CrC;IAEY,qBAAqB,CAAC,kBAAkB,EAAE,uBAAuB,iBAM7E;YAgBa,2BAA2B;IAuEzC;;;;;;;;OAQG;IACU,+BAA+B,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAiBzF;IAED;;;;;;;;;OASG;IACU,0CAA0C,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAcpG;YAOa,uCAAuC;YAYvC,iBAAiB;IAa/B;;;;;;;;OAQG;IACU,sBAAsB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBxF;IAED;;;OAGG;IACU,yBAAyB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxF;IAED;;;OAGG;IACU,4BAA4B,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3F;IAED,6DAA6D;IAC7D,OAAO,CAAC,mBAAmB;IAI3B,qDAAqD;IACrD,OAAO,CAAC,wBAAwB;YAKlB,sBAAsB;YAiBtB,8BAA8B;YA+C9B,+BAA+B;YA0C/B,8BAA8B;CAsB7C"}
|