envio 3.2.1 → 3.3.0-alpha.1

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.
Files changed (106) hide show
  1. package/package.json +6 -7
  2. package/src/Batch.res +12 -15
  3. package/src/Batch.res.mjs +4 -5
  4. package/src/BatchProcessing.res +199 -0
  5. package/src/BatchProcessing.res.mjs +125 -0
  6. package/src/ChainFetching.res +373 -0
  7. package/src/ChainFetching.res.mjs +206 -0
  8. package/src/ChainMetadata.res +27 -0
  9. package/src/ChainMetadata.res.mjs +27 -0
  10. package/src/ChainState.res +643 -0
  11. package/src/ChainState.res.mjs +476 -0
  12. package/src/ChainState.resi +87 -0
  13. package/src/Config.res +13 -4
  14. package/src/Config.res.mjs +8 -3
  15. package/src/ContractRegisterContext.res +106 -0
  16. package/src/ContractRegisterContext.res.mjs +76 -0
  17. package/src/Core.res +3 -0
  18. package/src/CrossChainState.res +294 -0
  19. package/src/CrossChainState.res.mjs +221 -0
  20. package/src/CrossChainState.resi +39 -0
  21. package/src/Ecosystem.res +58 -0
  22. package/src/Ecosystem.res.mjs +43 -1
  23. package/src/Env.res +0 -2
  24. package/src/Env.res.mjs +0 -3
  25. package/src/EventConfigBuilder.res +2 -2
  26. package/src/EventProcessing.res +60 -46
  27. package/src/EventProcessing.res.mjs +33 -32
  28. package/src/EventUtils.res +0 -4
  29. package/src/EventUtils.res.mjs +0 -4
  30. package/src/ExitOnCaughtUp.res +10 -0
  31. package/src/ExitOnCaughtUp.res.mjs +22 -0
  32. package/src/FetchState.res +205 -73
  33. package/src/FetchState.res.mjs +242 -103
  34. package/src/InMemoryStore.res +36 -451
  35. package/src/InMemoryStore.res.mjs +20 -369
  36. package/src/IndexerLoop.res +43 -0
  37. package/src/IndexerLoop.res.mjs +46 -0
  38. package/src/IndexerState.res +536 -0
  39. package/src/IndexerState.res.mjs +543 -0
  40. package/src/IndexerState.resi +128 -0
  41. package/src/Internal.res +29 -8
  42. package/src/LoadLayer.res +20 -18
  43. package/src/LoadLayer.res.mjs +14 -12
  44. package/src/LoadLayer.resi +6 -3
  45. package/src/Logging.res +11 -44
  46. package/src/Logging.res.mjs +10 -42
  47. package/src/Main.res +54 -79
  48. package/src/Main.res.mjs +44 -56
  49. package/src/PgStorage.res +8 -71
  50. package/src/PgStorage.res.mjs +3 -47
  51. package/src/Prometheus.res +1 -13
  52. package/src/Prometheus.res.mjs +84 -96
  53. package/src/PruneStaleHistory.res +45 -0
  54. package/src/PruneStaleHistory.res.mjs +46 -0
  55. package/src/RawEvent.res +73 -0
  56. package/src/RawEvent.res.mjs +51 -0
  57. package/src/Rollback.res +204 -0
  58. package/src/Rollback.res.mjs +118 -0
  59. package/src/SimulateItems.res +12 -10
  60. package/src/SimulateItems.res.mjs +1 -1
  61. package/src/UserContext.res +26 -114
  62. package/src/UserContext.res.mjs +15 -78
  63. package/src/Writing.res +242 -0
  64. package/src/Writing.res.mjs +215 -0
  65. package/src/db/InternalTable.res +14 -27
  66. package/src/sources/Evm.res +40 -1
  67. package/src/sources/Evm.res.mjs +94 -84
  68. package/src/sources/Fuel.res +40 -1
  69. package/src/sources/Fuel.res.mjs +36 -26
  70. package/src/sources/HyperFuel.res +41 -120
  71. package/src/sources/HyperFuel.res.mjs +42 -80
  72. package/src/sources/HyperFuel.resi +1 -24
  73. package/src/sources/HyperFuelClient.res +16 -297
  74. package/src/sources/HyperFuelClient.res.mjs +5 -4
  75. package/src/sources/HyperFuelSource.res +33 -8
  76. package/src/sources/HyperFuelSource.res.mjs +56 -10
  77. package/src/sources/HyperSyncSource.res +5 -3
  78. package/src/sources/HyperSyncSource.res.mjs +1 -1
  79. package/src/sources/RpcSource.res +2 -2
  80. package/src/sources/RpcSource.res.mjs +1 -1
  81. package/src/sources/SourceManager.res +14 -23
  82. package/src/sources/SourceManager.res.mjs +27 -29
  83. package/src/sources/SourceManager.resi +4 -2
  84. package/src/sources/Svm.res +18 -1
  85. package/src/sources/Svm.res.mjs +30 -22
  86. package/src/sources/SvmHyperSyncSource.res +1 -1
  87. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  88. package/src/tui/Tui.res +38 -36
  89. package/src/tui/Tui.res.mjs +51 -51
  90. package/src/ChainFetcher.res +0 -519
  91. package/src/ChainFetcher.res.mjs +0 -314
  92. package/src/ChainManager.res +0 -358
  93. package/src/ChainManager.res.mjs +0 -291
  94. package/src/Ctx.res +0 -6
  95. package/src/Ctx.res.mjs +0 -2
  96. package/src/GlobalState.res +0 -1056
  97. package/src/GlobalState.res.mjs +0 -1086
  98. package/src/GlobalStateManager.res +0 -57
  99. package/src/GlobalStateManager.res.mjs +0 -68
  100. package/src/GlobalStateManager.resi +0 -7
  101. package/src/Ports.res +0 -5
  102. package/src/Ports.res.mjs +0 -9
  103. package/src/adapters/MarkBatchProcessedAdapter.res +0 -5
  104. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +0 -14
  105. package/src/sources/EnvioApiClient.res +0 -15
  106. package/src/sources/EnvioApiClient.res.mjs +0 -24
@@ -1,519 +0,0 @@
1
- //A filter should return true if the event should be kept and isValid should return
2
- //false when the filter should be removed/cleaned up
3
- type processingFilter = {
4
- filter: Internal.item => bool,
5
- isValid: (~fetchState: FetchState.t) => bool,
6
- }
7
-
8
- type t = {
9
- logger: Pino.t,
10
- fetchState: FetchState.t,
11
- sourceManager: SourceManager.t,
12
- chainConfig: Config.chain,
13
- isProgressAtHead: bool,
14
- timestampCaughtUpToHeadOrEndblock: option<Date.t>,
15
- committedProgressBlockNumber: int,
16
- numEventsProcessed: float,
17
- reorgDetection: ReorgDetection.t,
18
- safeCheckpointTracking: option<SafeCheckpointTracking.t>,
19
- }
20
-
21
- //CONSTRUCTION
22
- let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
23
- let addresses = []
24
- chainConfig.contracts->Array.forEach(contract => {
25
- contract.addresses->Array.forEach(address => {
26
- addresses->Array.push({
27
- Internal.address,
28
- contractName: contract.name,
29
- registrationBlock: -1,
30
- })
31
- })
32
- })
33
- addresses
34
- }
35
-
36
- let make = (
37
- ~chainConfig: Config.chain,
38
- ~indexingAddresses: array<Internal.indexingAddress>,
39
- ~startBlock,
40
- ~endBlock,
41
- ~firstEventBlock=None,
42
- ~progressBlockNumber,
43
- ~config: Config.t,
44
- ~registrations: HandlerRegister.registrations,
45
- ~targetBufferSize,
46
- ~logger,
47
- ~timestampCaughtUpToHeadOrEndblock,
48
- ~numEventsProcessed,
49
- ~isInReorgThreshold,
50
- ~isRealtime,
51
- ~reorgCheckpoints: array<Internal.reorgCheckpoint>,
52
- ~maxReorgDepth,
53
- ~knownHeight=0,
54
- ~reducedPollingInterval=?,
55
- ): t => {
56
- // We don't need the router itself, but only validation logic,
57
- // since now event router is created for selection of events
58
- // and validation doesn't work correctly in routers.
59
- // Ideally to split it into two different parts.
60
- let eventRouter = EventRouter.empty()
61
-
62
- // Aggregate events we want to fetch
63
- let eventConfigs: array<Internal.eventConfig> = []
64
-
65
- let notRegisteredEvents = []
66
-
67
- chainConfig.contracts->Array.forEach(contract => {
68
- let contractName = contract.name
69
-
70
- contract.events->Array.forEach(eventConfig => {
71
- let {isWildcard} = eventConfig
72
- let hasContractRegister = eventConfig.contractRegister->Option.isSome
73
-
74
- // Should validate the events
75
- eventRouter->EventRouter.addOrThrow(
76
- eventConfig.id,
77
- (),
78
- ~contractName,
79
- ~chain=ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id),
80
- ~eventName=eventConfig.name,
81
- ~isWildcard,
82
- )
83
-
84
- // Filter out non-preRegistration events on preRegistration phase
85
- // so we don't care about it in fetch state and workers anymore
86
- let shouldBeIncluded = if config.enableRawEvents {
87
- true
88
- } else {
89
- let isRegistered = hasContractRegister || eventConfig.handler->Option.isSome
90
- if !isRegistered {
91
- notRegisteredEvents->Array.push(eventConfig)
92
- }
93
- isRegistered
94
- }
95
-
96
- // Check if event has Static([]) filters (from a dynamic where
97
- // callback returning `false` / SkipAll for this chain).
98
- // If so, skip it entirely - it should never be fetched
99
- let shouldSkip = try {
100
- let getEventFiltersOrThrow = (
101
- eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
102
- ).getEventFiltersOrThrow
103
-
104
- // Check for non-evm chains
105
- if (
106
- getEventFiltersOrThrow->(Utils.magic: (ChainMap.Chain.t => Internal.eventFilters) => bool)
107
- ) {
108
- switch getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)) {
109
- | Static([]) => true
110
- | _ => false
111
- }
112
- } else {
113
- false
114
- }
115
- } catch {
116
- // Can throw when filter is invalid
117
- // Don't skip an event in this case. Let it throw in a better place - source code
118
- | _ => false
119
- }
120
-
121
- if shouldBeIncluded && !shouldSkip {
122
- eventConfigs->Array.push(eventConfig)
123
- }
124
- })
125
-
126
- switch contract.startBlock {
127
- | Some(startBlock) if startBlock < chainConfig.startBlock =>
128
- JsError.throwWithMessage(
129
- `The start block for contract "${contractName}" is less than the chain start block. This is not supported yet.`,
130
- )
131
- | _ => ()
132
- }
133
- })
134
-
135
- if notRegisteredEvents->Utils.Array.notEmpty {
136
- logger->Logging.childInfo(
137
- `The event${if notRegisteredEvents->Array.length > 1 {
138
- "s"
139
- } else {
140
- ""
141
- }} ${notRegisteredEvents
142
- ->Array.map(eventConfig => `${eventConfig.contractName}.${eventConfig.name}`)
143
- ->Array.joinUnsafe(", ")} don't have an event handler and skipped for indexing.`,
144
- )
145
- }
146
-
147
- let onBlockConfigs =
148
- registrations.onBlockByChainId->Utils.Dict.dangerouslyGetNonOption(chainConfig.id->Int.toString)
149
- switch onBlockConfigs {
150
- | Some(onBlockConfigs) =>
151
- // TODO: Move it to the HandlerRegister module
152
- // so the error is thrown with better stack trace
153
- onBlockConfigs->Array.forEach(onBlockConfig => {
154
- if onBlockConfig.startBlock->Option.getOr(startBlock) < startBlock {
155
- JsError.throwWithMessage(
156
- `The start block for onBlock handler "${onBlockConfig.name}" is less than the chain start block (${startBlock->Int.toString}). This is not supported yet.`,
157
- )
158
- }
159
- switch endBlock {
160
- | Some(chainEndBlock) =>
161
- if onBlockConfig.endBlock->Option.getOr(chainEndBlock) > chainEndBlock {
162
- JsError.throwWithMessage(
163
- `The end block for onBlock handler "${onBlockConfig.name}" is greater than the chain end block (${chainEndBlock->Int.toString}). This is not supported yet.`,
164
- )
165
- }
166
- | None => ()
167
- }
168
- })
169
- | None => ()
170
- }
171
-
172
- let fetchState = FetchState.make(
173
- ~maxAddrInPartition=config.maxAddrInPartition,
174
- ~addresses=indexingAddresses,
175
- ~progressBlockNumber,
176
- ~startBlock,
177
- ~endBlock,
178
- ~eventConfigs,
179
- ~targetBufferSize,
180
- ~knownHeight,
181
- ~chainId=chainConfig.id,
182
- // FIXME: Shouldn't set with full history
183
- ~blockLag=Pervasives.max(
184
- !config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth,
185
- chainConfig.blockLag,
186
- ),
187
- ~onBlockConfigs?,
188
- ~firstEventBlock,
189
- )
190
-
191
- let chainReorgCheckpoints = reorgCheckpoints->Array.filterMap(reorgCheckpoint => {
192
- if reorgCheckpoint.chainId === chainConfig.id {
193
- Some(reorgCheckpoint)
194
- } else {
195
- None
196
- }
197
- })
198
-
199
- // Create sources lazily here - this is where API token validation happens
200
- let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
201
- let lowercaseAddresses = config.lowercaseAddresses
202
- let sources = switch chainConfig.sourceConfig {
203
- | Config.EvmSourceConfig({hypersync, rpcs}) =>
204
- // Build Internal.evmContractConfig from contracts for EvmChain.makeSources
205
- let evmContracts: array<Internal.evmContractConfig> = chainConfig.contracts->Array.map((
206
- contract
207
- ): Internal.evmContractConfig => {
208
- name: contract.name,
209
- abi: contract.abi,
210
- events: contract.events->(
211
- Utils.magic: array<Internal.eventConfig> => array<Internal.evmEventConfig>
212
- ),
213
- })
214
- let evmRpcs: array<EvmChain.rpc> = rpcs->Array.map((rpc): EvmChain.rpc => {
215
- let syncConfig = rpc.syncConfig
216
- let ws = rpc.ws
217
- {
218
- url: rpc.url,
219
- sourceFor: rpc.sourceFor,
220
- ?syncConfig,
221
- ?ws,
222
- }
223
- })
224
- EvmChain.makeSources(
225
- ~chain,
226
- ~contracts=evmContracts,
227
- ~hyperSync=hypersync,
228
- ~rpcs=evmRpcs,
229
- ~lowercaseAddresses,
230
- )
231
- | Config.FuelSourceConfig({hypersync}) => [HyperFuelSource.make({chain, endpointUrl: hypersync})]
232
- | Config.SvmSourceConfig({hypersync, rpc}) =>
233
- switch (hypersync, rpc) {
234
- | (None, None) =>
235
- JsError.throwWithMessage(
236
- `Chain ${chain->ChainMap.Chain.toChainId->Int.toString} has no SVM data source`,
237
- )
238
- | (None, Some(rpc)) => [Svm.makeRPCSource(~chain, ~rpc)]
239
- | (Some(hypersyncUrl), _) =>
240
- // HyperSync drives instruction sync. A configured RPC is ignored for now
241
- // (RPC fallback isn't wired up yet).
242
- let svmEventConfigs =
243
- chainConfig.contracts
244
- ->Array.flatMap(contract => contract.events)
245
- ->(Utils.magic: array<Internal.eventConfig> => array<Internal.svmInstructionEventConfig>)
246
- let apiToken = Env.envioApiToken
247
- [
248
- SvmHyperSyncSource.make({
249
- chain,
250
- endpointUrl: hypersyncUrl,
251
- apiToken,
252
- eventConfigs: svmEventConfigs,
253
- clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
254
- }),
255
- ]
256
- }
257
- // For tests: use ready-to-use sources directly
258
- | Config.CustomSources(sources) => sources
259
- }
260
-
261
- {
262
- logger,
263
- chainConfig,
264
- sourceManager: SourceManager.make(
265
- ~sources,
266
- ~maxPartitionConcurrency=Env.maxPartitionConcurrency,
267
- ~isRealtime,
268
- ~reducedPollingInterval?,
269
- ),
270
- reorgDetection: ReorgDetection.make(
271
- ~chainReorgCheckpoints,
272
- ~maxReorgDepth,
273
- ~shouldRollbackOnReorg=config.shouldRollbackOnReorg,
274
- ),
275
- safeCheckpointTracking: SafeCheckpointTracking.make(
276
- ~maxReorgDepth,
277
- ~shouldRollbackOnReorg=config.shouldRollbackOnReorg,
278
- ~chainReorgCheckpoints,
279
- ),
280
- isProgressAtHead: false,
281
- fetchState,
282
- committedProgressBlockNumber: progressBlockNumber,
283
- timestampCaughtUpToHeadOrEndblock,
284
- numEventsProcessed,
285
- }
286
- }
287
-
288
- let makeFromConfig = (
289
- chainConfig: Config.chain,
290
- ~config,
291
- ~registrations,
292
- ~targetBufferSize,
293
- ~knownHeight,
294
- ) => {
295
- let logger = Logging.createChild(~params={"chainId": chainConfig.id})
296
-
297
- make(
298
- ~chainConfig,
299
- ~config,
300
- ~registrations,
301
- ~startBlock=chainConfig.startBlock,
302
- ~endBlock=chainConfig.endBlock,
303
- ~reorgCheckpoints=[],
304
- ~maxReorgDepth=chainConfig.maxReorgDepth,
305
- ~progressBlockNumber=-1,
306
- ~timestampCaughtUpToHeadOrEndblock=None,
307
- ~numEventsProcessed=0.,
308
- ~targetBufferSize,
309
- ~logger,
310
- ~indexingAddresses=configAddresses(chainConfig),
311
- ~isInReorgThreshold=false,
312
- ~isRealtime=false,
313
- ~knownHeight,
314
- )
315
- }
316
-
317
- /**
318
- * This function allows a chain fetcher to be created from metadata, in particular this is useful for restarting an indexer and making sure it fetches blocks from the same place.
319
- */
320
- let makeFromDbState = (
321
- chainConfig: Config.chain,
322
- ~resumedChainState: Persistence.initialChainState,
323
- ~reorgCheckpoints,
324
- ~isInReorgThreshold,
325
- ~isRealtime,
326
- ~config,
327
- ~registrations,
328
- ~targetBufferSize,
329
- ~reducedPollingInterval=?,
330
- ) => {
331
- let chainId = chainConfig.id
332
- let logger = Logging.createChild(~params={"chainId": chainId})
333
-
334
- Prometheus.ProgressEventsCount.set(~processedCount=resumedChainState.numEventsProcessed, ~chainId)
335
-
336
- let progressBlockNumber =
337
- // Can be -1 when not set
338
- resumedChainState.progressBlockNumber >= 0
339
- ? resumedChainState.progressBlockNumber
340
- : resumedChainState.startBlock - 1
341
-
342
- make(
343
- ~indexingAddresses=resumedChainState.indexingAddresses,
344
- ~chainConfig,
345
- ~startBlock=resumedChainState.startBlock,
346
- ~endBlock=resumedChainState.endBlock,
347
- ~config,
348
- ~registrations,
349
- ~reorgCheckpoints,
350
- ~maxReorgDepth=resumedChainState.maxReorgDepth,
351
- ~firstEventBlock=resumedChainState.firstEventBlockNumber,
352
- ~progressBlockNumber,
353
- ~timestampCaughtUpToHeadOrEndblock=Env.updateSyncTimeOnRestart
354
- ? None
355
- : resumedChainState.timestampCaughtUpToHeadOrEndblock,
356
- ~numEventsProcessed=resumedChainState.numEventsProcessed,
357
- ~logger,
358
- ~targetBufferSize,
359
- ~isInReorgThreshold,
360
- ~isRealtime,
361
- ~knownHeight=resumedChainState.sourceBlockNumber,
362
- ~reducedPollingInterval?,
363
- )
364
- }
365
-
366
- let runContractRegistersOrThrow = async (
367
- ~itemsWithContractRegister: array<Internal.item>,
368
- ~config: Config.t,
369
- ) => {
370
- let itemsWithDcs = []
371
-
372
- let onRegister = (~item: Internal.item, ~contractAddress, ~contractName) => {
373
- let eventItem = item->Internal.castUnsafeEventItem
374
- let {blockNumber} = eventItem
375
-
376
- let dc: Internal.indexingAddress = {
377
- address: contractAddress,
378
- contractName,
379
- registrationBlock: blockNumber,
380
- }
381
-
382
- switch item->Internal.getItemDcs {
383
- | None => {
384
- item->Internal.setItemDcs([dc])
385
- itemsWithDcs->Array.push(item)
386
- }
387
- | Some(dcs) => dcs->Array.push(dc)
388
- }
389
- }
390
-
391
- let promises = []
392
- for idx in 0 to itemsWithContractRegister->Array.length - 1 {
393
- let item = itemsWithContractRegister->Array.getUnsafe(idx)
394
- let eventItem = item->Internal.castUnsafeEventItem
395
- let contractRegister = switch eventItem {
396
- | {eventConfig: {contractRegister: Some(contractRegister)}} => contractRegister
397
- | {eventConfig: {contractRegister: None, name: eventName}} =>
398
- // Unexpected case, since we should pass only events with contract register to this function
399
- JsError.throwWithMessage("Contract register is not set for event " ++ eventName)
400
- }
401
-
402
- let errorMessage = "Event contractRegister failed, please fix the error to keep the indexer running smoothly"
403
-
404
- // Catch sync and async errors
405
- try {
406
- let params: UserContext.contractRegisterParams = {
407
- item,
408
- onRegister,
409
- config,
410
- isResolved: false,
411
- }
412
- let result = contractRegister(UserContext.getContractRegisterArgs(params))
413
-
414
- // Even though `contractRegister` always returns a promise,
415
- // in the ReScript type, but it might return a non-promise value for TS API.
416
- if result->Utils.Promise.isCatchable {
417
- promises->Array.push(
418
- result
419
- ->Promise.thenResolve(r => {
420
- params.isResolved = true
421
- r
422
- })
423
- ->Promise.catch(exn => {
424
- params.isResolved = true
425
- exn->ErrorHandling.mkLogAndRaise(~msg=errorMessage, ~logger=item->Logging.getItemLogger)
426
- }),
427
- )
428
- } else {
429
- params.isResolved = true
430
- }
431
- } catch {
432
- | exn =>
433
- exn->ErrorHandling.mkLogAndRaise(~msg=errorMessage, ~logger=item->Logging.getItemLogger)
434
- }
435
- }
436
-
437
- if promises->Utils.Array.notEmpty {
438
- let _ = await Promise.all(promises)
439
- }
440
-
441
- itemsWithDcs
442
- }
443
-
444
- let handleQueryResult = (
445
- chainFetcher: t,
446
- ~query: FetchState.query,
447
- ~newItems,
448
- ~newItemsWithDcs,
449
- ~latestFetchedBlock,
450
- ~knownHeight,
451
- ) => {
452
- let fs = switch newItemsWithDcs {
453
- | [] => chainFetcher.fetchState
454
- | _ => chainFetcher.fetchState->FetchState.registerDynamicContracts(newItemsWithDcs)
455
- }
456
-
457
- {
458
- ...chainFetcher,
459
- fetchState: fs
460
- ->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
461
- ->FetchState.updateKnownHeight(~knownHeight),
462
- }
463
- }
464
-
465
- /**
466
- Gets the latest item on the front of the queue and returns updated fetcher
467
- */
468
- let hasProcessedToEndblock = (self: t) => {
469
- let {committedProgressBlockNumber, fetchState} = self
470
- switch fetchState.endBlock {
471
- | Some(endBlock) => committedProgressBlockNumber >= endBlock
472
- | None => false
473
- }
474
- }
475
-
476
- let hasNoMoreEventsToProcess = (self: t) => {
477
- self.fetchState->FetchState.bufferSize === 0
478
- }
479
-
480
- let getHighestBlockBelowThreshold = (cf: t): int => {
481
- let highestBlockBelowThreshold = cf.fetchState.knownHeight - cf.chainConfig.maxReorgDepth
482
- highestBlockBelowThreshold < 0 ? 0 : highestBlockBelowThreshold
483
- }
484
-
485
- /**
486
- Finds the last known valid block number below the reorg block
487
- If not found, returns the highest block below threshold
488
- */
489
- let getLastKnownValidBlock = async (chainFetcher: t, ~reorgBlockNumber: int, ~isRealtime: bool) => {
490
- // Don't include the reorg block itself — different source instances
491
- // may have mismatching hashes at the head, so we always rollback
492
- // the block where we detected the reorg.
493
- let scannedBlockNumbers =
494
- chainFetcher.reorgDetection->ReorgDetection.getThresholdBlockNumbersBelowBlock(
495
- ~blockNumber=reorgBlockNumber,
496
- ~knownHeight=chainFetcher.fetchState.knownHeight,
497
- )
498
-
499
- switch scannedBlockNumbers {
500
- | [] => chainFetcher->getHighestBlockBelowThreshold
501
- | _ => {
502
- let blockNumbersAndHashes = await chainFetcher.sourceManager->SourceManager.getBlockHashes(
503
- ~blockNumbers=scannedBlockNumbers,
504
- ~isRealtime,
505
- )
506
-
507
- switch chainFetcher.reorgDetection->ReorgDetection.getLatestValidScannedBlock(
508
- ~blockNumbersAndHashes,
509
- ) {
510
- | Some(blockNumber) => blockNumber
511
- | None => chainFetcher->getHighestBlockBelowThreshold
512
- }
513
- }
514
- }
515
- }
516
-
517
- let isActivelyIndexing = (chainFetcher: t) => chainFetcher.fetchState->FetchState.isActivelyIndexing
518
-
519
- let isReady = (chainFetcher: t) => chainFetcher.timestampCaughtUpToHeadOrEndblock !== None