envio 3.3.0-alpha.1 → 3.3.0-alpha.2
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/package.json +6 -6
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +0 -11
- package/src/BatchProcessing.res +2 -12
- package/src/BatchProcessing.res.mjs +2 -7
- package/src/ChainFetching.res +17 -6
- package/src/ChainFetching.res.mjs +11 -11
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +150 -1
- package/src/ChainState.res.mjs +138 -9
- package/src/ChainState.resi +51 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +9 -26
- package/src/CrossChainState.res.mjs +7 -16
- package/src/Ecosystem.res +6 -1
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +19 -5
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/EventProcessing.res +26 -0
- package/src/EventProcessing.res.mjs +20 -0
- package/src/Internal.res +43 -4
- package/src/Internal.res.mjs +18 -0
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +2 -23
- package/src/Rollback.res +1 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SimulateItems.res +3 -0
- package/src/SimulateItems.res.mjs +2 -1
- package/src/sources/Evm.res +52 -38
- package/src/sources/Evm.res.mjs +48 -36
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -0
- package/src/sources/HyperFuelSource.res +5 -0
- package/src/sources/HyperFuelSource.res.mjs +2 -0
- package/src/sources/HyperSync.res +4 -1
- package/src/sources/HyperSync.res.mjs +5 -3
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +9 -78
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +3 -4
- package/src/sources/HyperSyncSource.res.mjs +2 -1
- package/src/sources/RpcSource.res +7 -2
- package/src/sources/RpcSource.res.mjs +3 -1
- package/src/sources/SimulateSource.res +3 -1
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +4 -0
- package/src/sources/Svm.res +25 -2
- package/src/sources/Svm.res.mjs +27 -2
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +60 -84
- package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
- package/src/sources/TransactionStore.res +111 -0
- package/src/sources/TransactionStore.res.mjs +77 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.3.0-alpha.
|
|
73
|
-
"envio-linux-x64-musl": "3.3.0-alpha.
|
|
74
|
-
"envio-linux-arm64": "3.3.0-alpha.
|
|
75
|
-
"envio-darwin-x64": "3.3.0-alpha.
|
|
76
|
-
"envio-darwin-arm64": "3.3.0-alpha.
|
|
72
|
+
"envio-linux-x64": "3.3.0-alpha.2",
|
|
73
|
+
"envio-linux-x64-musl": "3.3.0-alpha.2",
|
|
74
|
+
"envio-linux-arm64": "3.3.0-alpha.2",
|
|
75
|
+
"envio-darwin-x64": "3.3.0-alpha.2",
|
|
76
|
+
"envio-darwin-arm64": "3.3.0-alpha.2"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/Batch.res
CHANGED
|
@@ -34,12 +34,6 @@ type t = {
|
|
|
34
34
|
checkpointEventsProcessed: array<int>,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
let hasReadyItem = (fetchStates: array<FetchState.t>) => {
|
|
38
|
-
fetchStates->Array.some(fetchState => {
|
|
39
|
-
fetchState->FetchState.isActivelyIndexing && fetchState->FetchState.hasReadyItem
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
37
|
let getProgressedChainsById = {
|
|
44
38
|
let getChainAfterBatchIfProgressed = (
|
|
45
39
|
~chainBeforeBatch: chainBeforeBatch,
|
package/src/Batch.res.mjs
CHANGED
|
@@ -4,16 +4,6 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as FetchState from "./FetchState.res.mjs";
|
|
5
5
|
import * as ReorgDetection from "./ReorgDetection.res.mjs";
|
|
6
6
|
|
|
7
|
-
function hasReadyItem(fetchStates) {
|
|
8
|
-
return fetchStates.some(fetchState => {
|
|
9
|
-
if (FetchState.isActivelyIndexing(fetchState)) {
|
|
10
|
-
return FetchState.hasReadyItem(fetchState);
|
|
11
|
-
} else {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
7
|
function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, fetchStateAfterBatch, batchSize) {
|
|
18
8
|
if (chainBeforeBatch.progressBlockNumber < progressBlockNumberAfterBatch) {
|
|
19
9
|
return {
|
|
@@ -208,7 +198,6 @@ function findLastEventItem(batch, chainId) {
|
|
|
208
198
|
}
|
|
209
199
|
|
|
210
200
|
export {
|
|
211
|
-
hasReadyItem,
|
|
212
201
|
getProgressedChainsById,
|
|
213
202
|
addReorgCheckpoints,
|
|
214
203
|
prepareBatch,
|
package/src/BatchProcessing.res
CHANGED
|
@@ -77,15 +77,7 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
77
77
|
state
|
|
78
78
|
->IndexerState.chainStates
|
|
79
79
|
->Dict.valuesToArray
|
|
80
|
-
->Array.every(cs =>
|
|
81
|
-
let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
82
|
-
(cs->ChainState.fetchState).chainId,
|
|
83
|
-
) {
|
|
84
|
-
| Some(chainAfterBatch) => chainAfterBatch.fetchState
|
|
85
|
-
| None => cs->ChainState.fetchState
|
|
86
|
-
}
|
|
87
|
-
fetchState->FetchState.isReadyToEnterReorgThreshold
|
|
88
|
-
})
|
|
80
|
+
->Array.every(cs => cs->ChainState.isReadyToEnterReorgThresholdAfterBatch(~batch))
|
|
89
81
|
|
|
90
82
|
if shouldEnterReorgThreshold {
|
|
91
83
|
IndexerState.enterReorgThreshold(state)
|
|
@@ -171,9 +163,7 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
171
163
|
state
|
|
172
164
|
->IndexerState.chainStates
|
|
173
165
|
->Dict.valuesToArray
|
|
174
|
-
->Array.every(
|
|
175
|
-
cs->ChainState.isProgressAtHead && (cs->ChainState.fetchState).endBlock->Option.isNone
|
|
176
|
-
)
|
|
166
|
+
->Array.every(ChainState.isAtHeadWithoutEndBlock)
|
|
177
167
|
) {
|
|
178
168
|
IndexerState.errorExit(
|
|
179
169
|
state,
|
|
@@ -4,12 +4,10 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as Config from "./Config.res.mjs";
|
|
5
5
|
import * as Logging from "./Logging.res.mjs";
|
|
6
6
|
import * as ChainState from "./ChainState.res.mjs";
|
|
7
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
8
7
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
9
8
|
import * as ChainMetadata from "./ChainMetadata.res.mjs";
|
|
10
9
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
11
10
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
12
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
13
11
|
import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
|
|
14
12
|
import * as EventProcessing from "./EventProcessing.res.mjs";
|
|
15
13
|
import * as PruneStaleHistory from "./PruneStaleHistory.res.mjs";
|
|
@@ -30,10 +28,7 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
30
28
|
let batch = IndexerState.createBatch(state, IndexerState.processedCheckpointId(state), IndexerState.config(state).batchSize, isRollbackBatch);
|
|
31
29
|
let progressedChainsById = batch.progressedChainsById;
|
|
32
30
|
let isBelowReorgThreshold = !isInReorgThresholdBeforeUpdate && IndexerState.config(state).shouldRollbackOnReorg;
|
|
33
|
-
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs =>
|
|
34
|
-
let chainAfterBatch = progressedChainsById[ChainState.fetchState(cs).chainId];
|
|
35
|
-
return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : ChainState.fetchState(cs));
|
|
36
|
-
});
|
|
31
|
+
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => ChainState.isReadyToEnterReorgThresholdAfterBatch(cs, batch));
|
|
37
32
|
if (shouldEnterReorgThreshold) {
|
|
38
33
|
IndexerState.enterReorgThreshold(state);
|
|
39
34
|
}
|
|
@@ -75,7 +70,7 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
75
70
|
}
|
|
76
71
|
if (allCaughtUp && !IndexerState.keepProcessAlive(state)) {
|
|
77
72
|
return await ExitOnCaughtUp.run(state);
|
|
78
|
-
} else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(
|
|
73
|
+
} else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(ChainState.isAtHeadWithoutEndBlock)) {
|
|
79
74
|
return IndexerState.errorExit(state, ErrorHandling.make(new Error("No events found between startBlock and chain head. Cannot auto-detect endBlock."), undefined, undefined));
|
|
80
75
|
} else {
|
|
81
76
|
ChainMetadata.stage(state);
|
package/src/ChainFetching.res
CHANGED
|
@@ -11,7 +11,14 @@ type partitionQueryResponse = {
|
|
|
11
11
|
let runContractRegistersOrThrow = async (
|
|
12
12
|
~itemsWithContractRegister: array<Internal.item>,
|
|
13
13
|
~config: Config.t,
|
|
14
|
+
~chainState: ChainState.t,
|
|
15
|
+
~page: option<TransactionStore.t>,
|
|
14
16
|
) => {
|
|
17
|
+
// contractRegister handlers can read event.transaction, so materialise the
|
|
18
|
+
// selected fields onto the payloads before running them. All items belong to
|
|
19
|
+
// the chain being fetched, hence its single page store and mask.
|
|
20
|
+
await chainState->ChainState.materializePageItems(~items=itemsWithContractRegister, ~page)
|
|
21
|
+
|
|
15
22
|
let itemsWithDcs = []
|
|
16
23
|
|
|
17
24
|
let onRegister = (~item: Internal.item, ~contractAddress, ~contractName) => {
|
|
@@ -106,6 +113,7 @@ let rec onQueryResponse = async (
|
|
|
106
113
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
107
114
|
let {
|
|
108
115
|
parsedQueueItems,
|
|
116
|
+
transactionStore,
|
|
109
117
|
latestFetchedBlockNumber,
|
|
110
118
|
latestFetchedBlockTimestamp,
|
|
111
119
|
stats,
|
|
@@ -114,7 +122,7 @@ let rec onQueryResponse = async (
|
|
|
114
122
|
fromBlockQueried,
|
|
115
123
|
} = response
|
|
116
124
|
|
|
117
|
-
if knownHeight >
|
|
125
|
+
if knownHeight > chainState->ChainState.knownHeight {
|
|
118
126
|
Prometheus.SourceHeight.set(
|
|
119
127
|
~blockNumber=knownHeight,
|
|
120
128
|
~chainId=(chainState->ChainState.chainConfig).id,
|
|
@@ -212,6 +220,7 @@ let rec onQueryResponse = async (
|
|
|
212
220
|
blockTimestamp: latestFetchedBlockTimestamp,
|
|
213
221
|
},
|
|
214
222
|
~query,
|
|
223
|
+
~transactionStore,
|
|
215
224
|
)
|
|
216
225
|
ChainMetadata.stage(state)
|
|
217
226
|
scheduleFetch()
|
|
@@ -224,6 +233,8 @@ let rec onQueryResponse = async (
|
|
|
224
233
|
switch await runContractRegistersOrThrow(
|
|
225
234
|
~itemsWithContractRegister,
|
|
226
235
|
~config=state->IndexerState.config,
|
|
236
|
+
~chainState,
|
|
237
|
+
~page=transactionStore,
|
|
227
238
|
) {
|
|
228
239
|
| exception exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
|
|
229
240
|
| newItemsWithDcs => proceed(~newItemsWithDcs)
|
|
@@ -240,6 +251,7 @@ and applyQueryResponse = (
|
|
|
240
251
|
~knownHeight,
|
|
241
252
|
~latestFetchedBlock,
|
|
242
253
|
~query,
|
|
254
|
+
~transactionStore,
|
|
243
255
|
) => {
|
|
244
256
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
245
257
|
let wasFetchingAtHead = chainState->ChainState.isFetchingAtHead
|
|
@@ -250,6 +262,7 @@ and applyQueryResponse = (
|
|
|
250
262
|
~newItems,
|
|
251
263
|
~newItemsWithDcs,
|
|
252
264
|
~knownHeight,
|
|
265
|
+
~transactionStore,
|
|
253
266
|
)
|
|
254
267
|
|
|
255
268
|
// In auto-exit mode, set endBlock to the first event's block when events arrive.
|
|
@@ -294,7 +307,7 @@ let finishWaitingForNewBlock = (
|
|
|
294
307
|
->IndexerState.chainStates
|
|
295
308
|
->Dict.valuesToArray
|
|
296
309
|
->Array.every(cs => {
|
|
297
|
-
cs->ChainState.
|
|
310
|
+
cs->ChainState.isReadyToEnterReorgThreshold
|
|
298
311
|
})
|
|
299
312
|
|
|
300
313
|
// Kick processing in case there are block handlers to run.
|
|
@@ -316,7 +329,6 @@ let fetchChain = async (
|
|
|
316
329
|
) => {
|
|
317
330
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
318
331
|
if !(state->IndexerState.isResolvingReorg) && !(state->IndexerState.isStopped) {
|
|
319
|
-
let fetchState = chainState->ChainState.fetchState
|
|
320
332
|
let isRealtime = state->IndexerState.isRealtime
|
|
321
333
|
let sourceManager = chainState->ChainState.sourceManager
|
|
322
334
|
|
|
@@ -327,8 +339,7 @@ let fetchChain = async (
|
|
|
327
339
|
// Owns its error boundary: launch doesn't catch, so any failure here (the
|
|
328
340
|
// query, response handling, or dispatch itself) must stop the indexer.
|
|
329
341
|
try {
|
|
330
|
-
await
|
|
331
|
-
~fetchState,
|
|
342
|
+
await chainState->ChainState.dispatch(
|
|
332
343
|
~waitForNewBlock=(~knownHeight) =>
|
|
333
344
|
sourceManager->SourceManager.waitForNewBlock(~knownHeight, ~isRealtime, ~reducedPolling),
|
|
334
345
|
~onNewBlock=(~knownHeight) =>
|
|
@@ -347,7 +358,7 @@ let fetchChain = async (
|
|
|
347
358
|
try {
|
|
348
359
|
let response = await sourceManager->SourceManager.executeQuery(
|
|
349
360
|
~query,
|
|
350
|
-
~knownHeight=
|
|
361
|
+
~knownHeight=chainState->ChainState.knownHeight,
|
|
351
362
|
~isRealtime,
|
|
352
363
|
)
|
|
353
364
|
await onQueryResponse(
|
|
@@ -4,7 +4,6 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as Logging from "./Logging.res.mjs";
|
|
5
5
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
6
6
|
import * as ChainState from "./ChainState.res.mjs";
|
|
7
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
8
7
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
9
8
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
10
9
|
import * as ChainMetadata from "./ChainMetadata.res.mjs";
|
|
@@ -17,7 +16,8 @@ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
|
17
16
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
18
17
|
import * as ContractRegisterContext from "./ContractRegisterContext.res.mjs";
|
|
19
18
|
|
|
20
|
-
async function runContractRegistersOrThrow(itemsWithContractRegister, config) {
|
|
19
|
+
async function runContractRegistersOrThrow(itemsWithContractRegister, config, chainState, page) {
|
|
20
|
+
await ChainState.materializePageItems(chainState, itemsWithContractRegister, page);
|
|
21
21
|
let itemsWithDcs = [];
|
|
22
22
|
let onRegister = (item, contractAddress, contractName) => {
|
|
23
23
|
let dc_registrationBlock = item.blockNumber;
|
|
@@ -71,10 +71,10 @@ async function runContractRegistersOrThrow(itemsWithContractRegister, config) {
|
|
|
71
71
|
return itemsWithDcs;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
function applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, latestFetchedBlock, query) {
|
|
74
|
+
function applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, latestFetchedBlock, query, transactionStore) {
|
|
75
75
|
let chainState = IndexerState.getChainState(state, chain);
|
|
76
76
|
let wasFetchingAtHead = ChainState.isFetchingAtHead(chainState);
|
|
77
|
-
ChainState.handleQueryResult(chainState, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight);
|
|
77
|
+
ChainState.handleQueryResult(chainState, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, transactionStore);
|
|
78
78
|
if (IndexerState.exitAfterFirstEventBlock(state) && newItems.length !== 0) {
|
|
79
79
|
ChainState.setEndBlockToFirstEvent(chainState, newItems[0].blockNumber);
|
|
80
80
|
}
|
|
@@ -94,9 +94,10 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
|
|
|
94
94
|
let stats = response.stats;
|
|
95
95
|
let latestFetchedBlockTimestamp = response.latestFetchedBlockTimestamp;
|
|
96
96
|
let latestFetchedBlockNumber = response.latestFetchedBlockNumber;
|
|
97
|
+
let transactionStore = response.transactionStore;
|
|
97
98
|
let parsedQueueItems = response.parsedQueueItems;
|
|
98
99
|
let knownHeight = response.knownHeight;
|
|
99
|
-
if (knownHeight > ChainState.
|
|
100
|
+
if (knownHeight > ChainState.knownHeight(chainState)) {
|
|
100
101
|
Prometheus.SourceHeight.set(SourceManager.getActiveSource(ChainState.sourceManager(chainState)).name, ChainState.chainConfig(chainState).id, knownHeight);
|
|
101
102
|
}
|
|
102
103
|
Prometheus.FetchingBlockRange.increment(chain, stats["total time elapsed (s)"], Stdlib_Option.getOr(stats["parsing time (s)"], 0), parsedQueueItems.length, (latestFetchedBlockNumber - response.fromBlockQueried | 0) + 1 | 0);
|
|
@@ -133,7 +134,7 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
|
|
|
133
134
|
applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, {
|
|
134
135
|
blockNumber: latestFetchedBlockNumber,
|
|
135
136
|
blockTimestamp: latestFetchedBlockTimestamp
|
|
136
|
-
}, query);
|
|
137
|
+
}, query, transactionStore);
|
|
137
138
|
ChainMetadata.stage(state);
|
|
138
139
|
scheduleFetch();
|
|
139
140
|
return scheduleProcessing();
|
|
@@ -144,7 +145,7 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
|
|
|
144
145
|
}
|
|
145
146
|
let newItemsWithDcs;
|
|
146
147
|
try {
|
|
147
|
-
newItemsWithDcs = await runContractRegistersOrThrow(itemsWithContractRegister, IndexerState.config(state));
|
|
148
|
+
newItemsWithDcs = await runContractRegistersOrThrow(itemsWithContractRegister, IndexerState.config(state), chainState, transactionStore);
|
|
148
149
|
} catch (raw_exn) {
|
|
149
150
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
150
151
|
return IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, undefined));
|
|
@@ -159,7 +160,7 @@ function finishWaitingForNewBlock(state, chain, knownHeight, stateId, scheduleFe
|
|
|
159
160
|
let chainState = IndexerState.getChainState(state, chain);
|
|
160
161
|
ChainState.updateKnownHeight(chainState, knownHeight);
|
|
161
162
|
let isBelowReorgThreshold = !IndexerState.isInReorgThreshold(state) && IndexerState.config(state).shouldRollbackOnReorg;
|
|
162
|
-
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(
|
|
163
|
+
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(ChainState.isReadyToEnterReorgThreshold);
|
|
163
164
|
if (shouldEnterReorgThreshold) {
|
|
164
165
|
IndexerState.enterReorgThreshold(state);
|
|
165
166
|
}
|
|
@@ -172,14 +173,13 @@ async function fetchChain(state, chain, action, stateId, scheduleFetch, schedule
|
|
|
172
173
|
if (IndexerState.isResolvingReorg(state) || IndexerState.isStopped(state)) {
|
|
173
174
|
return;
|
|
174
175
|
}
|
|
175
|
-
let fetchState = ChainState.fetchState(chainState);
|
|
176
176
|
let isRealtime = IndexerState.isRealtime(state);
|
|
177
177
|
let sourceManager = ChainState.sourceManager(chainState);
|
|
178
178
|
let reducedPolling = !isRealtime;
|
|
179
179
|
try {
|
|
180
|
-
return await
|
|
180
|
+
return await ChainState.dispatch(chainState, async query => {
|
|
181
181
|
try {
|
|
182
|
-
let response = await SourceManager.executeQuery(sourceManager, query,
|
|
182
|
+
let response = await SourceManager.executeQuery(sourceManager, query, ChainState.knownHeight(chainState), isRealtime);
|
|
183
183
|
return await onQueryResponse(state, {
|
|
184
184
|
chain: chain,
|
|
185
185
|
response: response,
|
package/src/ChainMetadata.res
CHANGED
|
@@ -7,19 +7,9 @@ let stage = (state: IndexerState.t) => {
|
|
|
7
7
|
->IndexerState.chainStates
|
|
8
8
|
->Dict.valuesToArray
|
|
9
9
|
->Array.forEach(cs => {
|
|
10
|
-
let fetchState = cs->ChainState.fetchState
|
|
11
10
|
chainsData->Dict.set(
|
|
12
11
|
(cs->ChainState.chainConfig).id->Int.toString,
|
|
13
|
-
|
|
14
|
-
firstEventBlockNumber: fetchState.firstEventBlock->Null.fromOption,
|
|
15
|
-
isHyperSync: (
|
|
16
|
-
cs->ChainState.sourceManager->SourceManager.getActiveSource
|
|
17
|
-
).poweredByHyperSync,
|
|
18
|
-
latestFetchedBlockNumber: fetchState->FetchState.bufferBlockNumber,
|
|
19
|
-
timestampCaughtUpToHeadOrEndblock: cs
|
|
20
|
-
->ChainState.timestampCaughtUpToHeadOrEndblock
|
|
21
|
-
->Null.fromOption,
|
|
22
|
-
},
|
|
12
|
+
cs->ChainState.toChainMetadata,
|
|
23
13
|
)
|
|
24
14
|
})
|
|
25
15
|
|
|
@@ -2,21 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Writing from "./Writing.res.mjs";
|
|
4
4
|
import * as ChainState from "./ChainState.res.mjs";
|
|
5
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
6
|
-
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
7
5
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
8
|
-
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
9
6
|
|
|
10
7
|
function stage(state) {
|
|
11
8
|
let chainsData = {};
|
|
12
9
|
Object.values(IndexerState.chainStates(state)).forEach(cs => {
|
|
13
|
-
|
|
14
|
-
chainsData[ChainState.chainConfig(cs).id.toString()] = {
|
|
15
|
-
first_event_block: Stdlib_Null.fromOption(fetchState.firstEventBlock),
|
|
16
|
-
buffer_block: FetchState.bufferBlockNumber(fetchState),
|
|
17
|
-
ready_at: Stdlib_Null.fromOption(ChainState.timestampCaughtUpToHeadOrEndblock(cs)),
|
|
18
|
-
_is_hyper_sync: SourceManager.getActiveSource(ChainState.sourceManager(cs)).poweredByHyperSync
|
|
19
|
-
};
|
|
10
|
+
chainsData[ChainState.chainConfig(cs).id.toString()] = ChainState.toChainMetadata(cs);
|
|
20
11
|
});
|
|
21
12
|
Writing.setChainMeta(state, chainsData);
|
|
22
13
|
}
|
package/src/ChainState.res
CHANGED
|
@@ -17,6 +17,30 @@ type t = {
|
|
|
17
17
|
mutable pendingBudget: float,
|
|
18
18
|
mutable reorgDetection: ReorgDetection.t,
|
|
19
19
|
mutable safeCheckpointTracking: option<SafeCheckpointTracking.t>,
|
|
20
|
+
// Holds this chain's transactions (kept in Rust) keyed by (blockNumber,
|
|
21
|
+
// transactionIndex). Fetch responses merge their page in; entries are pruned
|
|
22
|
+
// as the chain progresses and dropped above the target on rollback.
|
|
23
|
+
transactionStore: TransactionStore.t,
|
|
24
|
+
// Bitmask of the transaction fields the store materialises at batch prep.
|
|
25
|
+
transactionFieldMask: float,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Per-chain shape returned by the status API.
|
|
29
|
+
type chainData = {
|
|
30
|
+
chainId: float,
|
|
31
|
+
poweredByHyperSync: bool,
|
|
32
|
+
firstEventBlockNumber: option<int>,
|
|
33
|
+
latestProcessedBlock: option<int>,
|
|
34
|
+
timestampCaughtUpToHeadOrEndblock: option<Date.t>,
|
|
35
|
+
numEventsProcessed: float,
|
|
36
|
+
latestFetchedBlockNumber: int,
|
|
37
|
+
// Need this for API backwards compatibility
|
|
38
|
+
@as("currentBlockHeight")
|
|
39
|
+
knownHeight: int,
|
|
40
|
+
numBatchesFetched: int,
|
|
41
|
+
startBlock: int,
|
|
42
|
+
endBlock: option<int>,
|
|
43
|
+
numAddresses: int,
|
|
20
44
|
}
|
|
21
45
|
|
|
22
46
|
let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
|
|
@@ -43,6 +67,7 @@ let make = (
|
|
|
43
67
|
~numEventsProcessed=0.,
|
|
44
68
|
~timestampCaughtUpToHeadOrEndblock=None,
|
|
45
69
|
~isProgressAtHead=false,
|
|
70
|
+
~transactionFieldMask=0.,
|
|
46
71
|
~logger: Pino.t,
|
|
47
72
|
): t => {
|
|
48
73
|
logger,
|
|
@@ -56,6 +81,8 @@ let make = (
|
|
|
56
81
|
pendingBudget: 0.,
|
|
57
82
|
reorgDetection,
|
|
58
83
|
safeCheckpointTracking,
|
|
84
|
+
transactionStore: TransactionStore.make(),
|
|
85
|
+
transactionFieldMask,
|
|
59
86
|
}
|
|
60
87
|
|
|
61
88
|
let makeInternal = (
|
|
@@ -301,6 +328,7 @@ let makeInternal = (
|
|
|
301
328
|
~committedProgressBlockNumber=progressBlockNumber,
|
|
302
329
|
~timestampCaughtUpToHeadOrEndblock,
|
|
303
330
|
~numEventsProcessed,
|
|
331
|
+
~transactionFieldMask=config.ecosystem.transactionFieldMask(eventConfigs),
|
|
304
332
|
~logger,
|
|
305
333
|
)
|
|
306
334
|
}
|
|
@@ -377,7 +405,6 @@ let makeFromDbState = (
|
|
|
377
405
|
// --- Read accessors. ---
|
|
378
406
|
|
|
379
407
|
let logger = (cs: t) => cs.logger
|
|
380
|
-
let fetchState = (cs: t) => cs.fetchState
|
|
381
408
|
let sourceManager = (cs: t) => cs.sourceManager
|
|
382
409
|
let chainConfig = (cs: t) => cs.chainConfig
|
|
383
410
|
let reorgDetection = (cs: t) => cs.reorgDetection
|
|
@@ -388,6 +415,19 @@ let numEventsProcessed = (cs: t) => cs.numEventsProcessed
|
|
|
388
415
|
let pendingBudget = (cs: t) => cs.pendingBudget
|
|
389
416
|
let timestampCaughtUpToHeadOrEndblock = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock
|
|
390
417
|
|
|
418
|
+
// Fetch-frontier reads. The FetchState is owned here; callers go through these
|
|
419
|
+
// rather than reaching into it.
|
|
420
|
+
let knownHeight = (cs: t) => cs.fetchState.knownHeight
|
|
421
|
+
let indexingAddresses = (cs: t) => cs.fetchState.indexingAddresses
|
|
422
|
+
let bufferSize = (cs: t) => cs.fetchState->FetchState.bufferSize
|
|
423
|
+
let bufferReadyCount = (cs: t) => cs.fetchState->FetchState.bufferReadyCount
|
|
424
|
+
let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPercentage
|
|
425
|
+
let getProgressPercentageAt = (cs: t, ~blockNumber) =>
|
|
426
|
+
cs.fetchState->FetchState.getProgressPercentageAt(~blockNumber)
|
|
427
|
+
let hasReadyItem = (cs: t) =>
|
|
428
|
+
cs.fetchState->FetchState.isActivelyIndexing && cs.fetchState->FetchState.hasReadyItem
|
|
429
|
+
let isReadyToEnterReorgThreshold = (cs: t) => cs.fetchState->FetchState.isReadyToEnterReorgThreshold
|
|
430
|
+
|
|
391
431
|
// Mark queries as in flight and reserve their estimated size against the shared
|
|
392
432
|
// buffer budget in one step, so the counter stays in sync with the pending
|
|
393
433
|
// queries it tracks.
|
|
@@ -404,6 +444,30 @@ let resetPendingQueries = (cs: t) => {
|
|
|
404
444
|
cs.pendingBudget = 0.
|
|
405
445
|
}
|
|
406
446
|
|
|
447
|
+
// Propose the chain's candidate queries against the shared buffer budget,
|
|
448
|
+
// accounting for this chain's own in-flight reservations.
|
|
449
|
+
let getNextQuery = (cs: t, ~budget) =>
|
|
450
|
+
cs.fetchState->FetchState.getNextQuery(~budget, ~chainPendingBudget=cs.pendingBudget)
|
|
451
|
+
|
|
452
|
+
// Run a fetch tick for this chain against its sources, feeding the owned fetch
|
|
453
|
+
// frontier to the source manager.
|
|
454
|
+
let dispatch = (
|
|
455
|
+
cs: t,
|
|
456
|
+
~executeQuery,
|
|
457
|
+
~waitForNewBlock,
|
|
458
|
+
~onNewBlock,
|
|
459
|
+
~action: FetchState.nextQuery,
|
|
460
|
+
~stateId,
|
|
461
|
+
) =>
|
|
462
|
+
cs.sourceManager->SourceManager.dispatch(
|
|
463
|
+
~fetchState=cs.fetchState,
|
|
464
|
+
~executeQuery,
|
|
465
|
+
~waitForNewBlock,
|
|
466
|
+
~onNewBlock,
|
|
467
|
+
~action,
|
|
468
|
+
~stateId,
|
|
469
|
+
)
|
|
470
|
+
|
|
407
471
|
// --- Derived (pure). ---
|
|
408
472
|
|
|
409
473
|
let hasProcessedToEndblock = (cs: t) => {
|
|
@@ -426,11 +490,34 @@ let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
|
|
|
426
490
|
// True once the fetch frontier has reached the head/endBlock for this chain.
|
|
427
491
|
let isFetchingAtHead = (cs: t) => cs.fetchState->FetchState.isFetchingAtHead
|
|
428
492
|
|
|
493
|
+
// Reached head on a chain with no configured endBlock — used by auto-exit to
|
|
494
|
+
// detect that no events were found in the start..head range.
|
|
495
|
+
let isAtHeadWithoutEndBlock = (cs: t) =>
|
|
496
|
+
cs.isProgressAtHead && cs.fetchState.endBlock->Option.isNone
|
|
497
|
+
|
|
429
498
|
// --- State transitions. The chain state is mutated only through these; each
|
|
430
499
|
// owns a cohesive update so callers don't juggle individual fields. ---
|
|
431
500
|
|
|
432
501
|
// Apply a fetch response: register any new dynamic contracts, append the items
|
|
433
502
|
// to the buffer and advance the known head.
|
|
503
|
+
// Materialise the chain store's selected transaction fields onto a batch's
|
|
504
|
+
// items at batch prep (the persistent-store path).
|
|
505
|
+
let materializeBatchItems = (cs: t, ~items: array<Internal.item>) =>
|
|
506
|
+
cs.transactionStore->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
|
|
507
|
+
|
|
508
|
+
// Materialise a fetch-response page's transactions onto its items before
|
|
509
|
+
// contract-register handlers read them. `None` pages (RPC/Fuel/Simulate keep the
|
|
510
|
+
// transaction inline) are a no-op.
|
|
511
|
+
let materializePageItems = (
|
|
512
|
+
cs: t,
|
|
513
|
+
~items: array<Internal.item>,
|
|
514
|
+
~page: option<TransactionStore.t>,
|
|
515
|
+
) =>
|
|
516
|
+
switch page {
|
|
517
|
+
| Some(store) => store->TransactionStore.materializeItems(~items, ~mask=cs.transactionFieldMask)
|
|
518
|
+
| None => Promise.resolve()
|
|
519
|
+
}
|
|
520
|
+
|
|
434
521
|
let handleQueryResult = (
|
|
435
522
|
cs: t,
|
|
436
523
|
~query: FetchState.query,
|
|
@@ -438,7 +525,15 @@ let handleQueryResult = (
|
|
|
438
525
|
~newItemsWithDcs,
|
|
439
526
|
~latestFetchedBlock,
|
|
440
527
|
~knownHeight,
|
|
528
|
+
~transactionStore as page: option<TransactionStore.t>,
|
|
441
529
|
) => {
|
|
530
|
+
// Merge this response's page into the chain store in lockstep with appending
|
|
531
|
+
// its items to the buffer. Inline-transaction sources contribute no page.
|
|
532
|
+
switch page {
|
|
533
|
+
| Some(page) => cs.transactionStore->TransactionStore.merge(page)
|
|
534
|
+
| None => ()
|
|
535
|
+
}
|
|
536
|
+
|
|
442
537
|
let fs = switch newItemsWithDcs {
|
|
443
538
|
| [] => cs.fetchState
|
|
444
539
|
| _ => cs.fetchState->FetchState.registerDynamicContracts(newItemsWithDcs)
|
|
@@ -491,6 +586,56 @@ let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
|
|
|
491
586
|
let enterReorgThreshold = (cs: t) =>
|
|
492
587
|
cs.fetchState = cs.fetchState->FetchState.updateInternal(~blockLag=cs.chainConfig.blockLag)
|
|
493
588
|
|
|
589
|
+
// Snapshot the chain's metadata fields for staging into the chains table.
|
|
590
|
+
let toChainMetadata = (cs: t): InternalTable.Chains.metaFields => {
|
|
591
|
+
firstEventBlockNumber: cs.fetchState.firstEventBlock->Null.fromOption,
|
|
592
|
+
isHyperSync: (cs.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
|
|
593
|
+
latestFetchedBlockNumber: cs.fetchState->FetchState.bufferBlockNumber,
|
|
594
|
+
timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock->Null.fromOption,
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Snapshot the chain's view for the status API.
|
|
598
|
+
let toChainData = (cs: t): chainData => {
|
|
599
|
+
chainId: cs.chainConfig.id->Int.toFloat,
|
|
600
|
+
poweredByHyperSync: (cs.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
|
|
601
|
+
firstEventBlockNumber: cs.fetchState.firstEventBlock,
|
|
602
|
+
latestProcessedBlock: cs.committedProgressBlockNumber === -1
|
|
603
|
+
? None
|
|
604
|
+
: Some(cs.committedProgressBlockNumber),
|
|
605
|
+
timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock,
|
|
606
|
+
numEventsProcessed: cs.numEventsProcessed,
|
|
607
|
+
latestFetchedBlockNumber: Pervasives.max(cs.fetchState->FetchState.bufferBlockNumber, 0),
|
|
608
|
+
knownHeight: cs->hasProcessedToEndblock
|
|
609
|
+
? cs.fetchState.endBlock->Option.getOr(cs.fetchState.knownHeight)
|
|
610
|
+
: cs.fetchState.knownHeight,
|
|
611
|
+
numBatchesFetched: 0,
|
|
612
|
+
startBlock: cs.fetchState.startBlock,
|
|
613
|
+
endBlock: cs.fetchState.endBlock,
|
|
614
|
+
numAddresses: cs.fetchState->FetchState.numAddresses,
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Snapshot the inputs a batch build needs from this chain.
|
|
618
|
+
let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
|
|
619
|
+
fetchState: cs.fetchState,
|
|
620
|
+
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
621
|
+
totalEventsProcessed: cs.numEventsProcessed,
|
|
622
|
+
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
623
|
+
reorgDetection: cs.reorgDetection,
|
|
624
|
+
chainConfig: cs.chainConfig,
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Whether the chain's post-batch fetch frontier is ready to cross into the reorg
|
|
628
|
+
// threshold, using the batch's progressed frontier when this chain advanced.
|
|
629
|
+
let isReadyToEnterReorgThresholdAfterBatch = (cs: t, ~batch: Batch.t) => {
|
|
630
|
+
let fetchState = switch batch.progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
631
|
+
cs.fetchState.chainId,
|
|
632
|
+
) {
|
|
633
|
+
| Some(chainAfterBatch) => chainAfterBatch.fetchState
|
|
634
|
+
| None => cs.fetchState
|
|
635
|
+
}
|
|
636
|
+
fetchState->FetchState.isReadyToEnterReorgThreshold
|
|
637
|
+
}
|
|
638
|
+
|
|
494
639
|
// Commit the post-batch fetch frontier for a chain that progressed in the batch,
|
|
495
640
|
// applying blockLag when this batch also crosses into the reorg threshold.
|
|
496
641
|
let advanceAfterBatch = (cs: t, ~batch: Batch.t, ~enteringReorgThreshold) =>
|
|
@@ -551,6 +696,8 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t) => {
|
|
|
551
696
|
|
|
552
697
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber
|
|
553
698
|
cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed
|
|
699
|
+
// Processed blocks' transactions are no longer needed.
|
|
700
|
+
cs.transactionStore->TransactionStore.prune(chainAfterBatch.progressBlockNumber)
|
|
554
701
|
cs.isProgressAtHead = cs.isProgressAtHead || chainAfterBatch.isProgressAtHeadWhenBatchCreated
|
|
555
702
|
switch cs.safeCheckpointTracking {
|
|
556
703
|
| Some(safeCheckpointTracking) =>
|
|
@@ -624,6 +771,7 @@ let rollback = (
|
|
|
624
771
|
| None => ()
|
|
625
772
|
}
|
|
626
773
|
cs.fetchState = cs.fetchState->FetchState.rollback(~targetBlockNumber=newProgressBlockNumber)
|
|
774
|
+
cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
|
|
627
775
|
cs.committedProgressBlockNumber = newProgressBlockNumber
|
|
628
776
|
cs.numEventsProcessed = newTotalEventsProcessed
|
|
629
777
|
| None =>
|
|
@@ -634,6 +782,7 @@ let rollback = (
|
|
|
634
782
|
)
|
|
635
783
|
cs.fetchState =
|
|
636
784
|
cs.fetchState->FetchState.rollback(~targetBlockNumber=rollbackTargetBlockNumber)
|
|
785
|
+
cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
|
|
637
786
|
cs.committedProgressBlockNumber = Pervasives.min(
|
|
638
787
|
cs.committedProgressBlockNumber,
|
|
639
788
|
rollbackTargetBlockNumber,
|