envio 3.12.0 → 3.12.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 (48) hide show
  1. package/package.json +6 -6
  2. package/src/ChainFetching.res +7 -3
  3. package/src/ChainFetching.res.mjs +1 -1
  4. package/src/ChainState.res +12 -3
  5. package/src/ChainState.res.mjs +3 -3
  6. package/src/ChainState.resi +1 -1
  7. package/src/CrossChainState.res +1 -1
  8. package/src/CrossChainState.res.mjs +1 -1
  9. package/src/FetchState.res +51 -46
  10. package/src/FetchState.res.mjs +54 -36
  11. package/src/InMemoryTable.res +155 -67
  12. package/src/InMemoryTable.res.mjs +151 -60
  13. package/src/LoadLayer.res +57 -34
  14. package/src/LoadLayer.res.mjs +45 -62
  15. package/src/LoadLayer.resi +1 -1
  16. package/src/PgStorage.res +92 -62
  17. package/src/PgStorage.res.mjs +77 -50
  18. package/src/TestIndexer.res +9 -25
  19. package/src/TestIndexer.res.mjs +4 -3
  20. package/src/UserContext.res +13 -32
  21. package/src/UserContext.res.mjs +1 -7
  22. package/src/Utils.res +1 -4
  23. package/src/Utils.res.mjs +7 -16
  24. package/src/db/EntityFilter.res +487 -275
  25. package/src/db/EntityFilter.res.mjs +557 -309
  26. package/src/db/Table.res +21 -6
  27. package/src/db/Table.res.mjs +13 -4
  28. package/src/sources/BlockStore.res +7 -2
  29. package/src/sources/EvmHyperSyncSource.res +2 -0
  30. package/src/sources/EvmHyperSyncSource.res.mjs +2 -2
  31. package/src/sources/FuelHyperSyncSource.res +1 -0
  32. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  33. package/src/sources/HyperSync.res +4 -0
  34. package/src/sources/HyperSync.res.mjs +4 -2
  35. package/src/sources/HyperSync.resi +1 -0
  36. package/src/sources/HyperSyncClient.res +3 -0
  37. package/src/sources/HyperSyncSSE.res +1 -1
  38. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  39. package/src/sources/RpcSource.res +1 -0
  40. package/src/sources/RpcSource.res.mjs +1 -1
  41. package/src/sources/SimulateSource.res +1 -0
  42. package/src/sources/SimulateSource.res.mjs +1 -1
  43. package/src/sources/Source.res +7 -0
  44. package/src/sources/SourceManager.res +4 -3
  45. package/src/sources/SourceManager.res.mjs +2 -2
  46. package/src/sources/SvmHyperSyncClient.res +5 -0
  47. package/src/sources/SvmHyperSyncSource.res +3 -0
  48. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.12.0",
3
+ "version": "3.12.1",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -68,10 +68,10 @@
68
68
  "tsx": "4.21.0"
69
69
  },
70
70
  "optionalDependencies": {
71
- "envio-linux-x64": "3.12.0",
72
- "envio-linux-x64-musl": "3.12.0",
73
- "envio-linux-arm64": "3.12.0",
74
- "envio-darwin-x64": "3.12.0",
75
- "envio-darwin-arm64": "3.12.0"
71
+ "envio-linux-x64": "3.12.1",
72
+ "envio-linux-x64-musl": "3.12.1",
73
+ "envio-linux-arm64": "3.12.1",
74
+ "envio-darwin-x64": "3.12.1",
75
+ "envio-darwin-arm64": "3.12.1"
76
76
  }
77
77
  }
@@ -326,9 +326,13 @@ let fetchChain = async (
326
326
  let isRealtime = state->IndexerState.isRealtime
327
327
  let sourceManager = chainState->ChainState.sourceManager
328
328
 
329
- // Only affects the WaitingForNewBlock branch of dispatch, where
330
- // there's nothing to fetch. During backfill any such chain is idle.
331
- let reducedPolling = !isRealtime
329
+ // Only affects the WaitingForNewBlock branch of dispatch, where there's
330
+ // nothing to fetch. The line is whether any height is known at all, not
331
+ // whether the head is fetchable yet: a chain held back by its lag is still
332
+ // tracking a head it has, and asking faster would buy nothing. A chain with
333
+ // no height has nothing to show for itself until the first one lands, so it
334
+ // polls at the source's own cadence and earns the normal stall window.
335
+ let reducedPolling = !isRealtime && chainState->ChainState.knownHeight > 0
332
336
 
333
337
  // Owns its error boundary: launch doesn't catch, so any failure here (the
334
338
  // query, response handling, or dispatch itself) must stop the indexer.
@@ -180,7 +180,7 @@ async function fetchChain(state, chainId, action, stateId, scheduleFetch, schedu
180
180
  }
181
181
  let isRealtime = IndexerState.isRealtime(state);
182
182
  let sourceManager = ChainState.sourceManager(chainState);
183
- let reducedPolling = !isRealtime;
183
+ let reducedPolling = !isRealtime && ChainState.knownHeight(chainState) > 0;
184
184
  try {
185
185
  return await ChainState.dispatch(chainState, async query => {
186
186
  try {
@@ -1021,7 +1021,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
1021
1021
  // in-threshold block hashes are copied up front because the build reuses the
1022
1022
  // whole set; the block time comes as a lookup instead, since the key it needs -
1023
1023
  // the batch's own progress block - isn't known until the build computes it.
1024
- let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
1024
+ let toChainBeforeBatch = (cs: t, ~isRealtime): Batch.chainBeforeBatch => {
1025
1025
  let {blockNumbers, hashes} =
1026
1026
  cs.blockStore->BlockStore.getHashes(
1027
1027
  ~fromBlock=Pervasives.max(cs.fetchState.knownHeight - cs.maxReorgDepth, 0),
@@ -1037,8 +1037,13 @@ let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
1037
1037
  totalEventsProcessed: cs.numEventsProcessed,
1038
1038
  sourceBlockNumber: cs.fetchState.knownHeight,
1039
1039
  scannedHashes: {blockNumbers, hashByBlockNumber},
1040
+ // A slot that produced no block only counts as skipped where the query
1041
+ // covered every slot in its range, which is what a chain at the head asks
1042
+ // for.
1040
1043
  blockTimeAt: blockNumber =>
1041
- cs.blockStore->BlockStore.getTimestamp(blockNumber)->Null.toOption,
1044
+ cs.blockStore
1045
+ ->BlockStore.getTimestamp(blockNumber, ~allowSkippedSlot=isRealtime)
1046
+ ->Null.toOption,
1042
1047
  shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
1043
1048
  chainConfig: cs.chainConfig,
1044
1049
  }
@@ -1194,8 +1199,12 @@ let markReady = (cs: t, ~readyAt) =>
1194
1199
  let rollbackCommittedProgress = (cs: t, blockNumber) =>
1195
1200
  if blockNumber !== cs.committedProgressBlockNumber {
1196
1201
  cs.committedProgressBlockNumber = blockNumber
1202
+ // Exact block only: the rolled-back region is about to be refetched, and
1203
+ // the next batch re-establishes the time either way.
1197
1204
  cs.committedProgressBlockTime =
1198
- cs.blockStore->BlockStore.getTimestamp(blockNumber)->Null.toOption
1205
+ cs.blockStore
1206
+ ->BlockStore.getTimestamp(blockNumber, ~allowSkippedSlot=false)
1207
+ ->Null.toOption
1199
1208
  }
1200
1209
 
1201
1210
  type progressDiff = {blockNumber: int, eventsProcessed: float}
@@ -718,7 +718,7 @@ function toMetrics(cs) {
718
718
  };
719
719
  }
720
720
 
721
- function toChainBeforeBatch(cs) {
721
+ function toChainBeforeBatch(cs, isRealtime) {
722
722
  let match = cs.blockStore.getHashes(Primitive_int.max(cs.fetchState.knownHeight - cs.maxReorgDepth | 0, 0), cs.fetchState.knownHeight + 1 | 0);
723
723
  let hashes = match.hashes;
724
724
  let blockNumbers = match.blockNumbers;
@@ -732,7 +732,7 @@ function toChainBeforeBatch(cs) {
732
732
  blockNumbers: blockNumbers,
733
733
  hashByBlockNumber: hashByBlockNumber
734
734
  },
735
- blockTimeAt: blockNumber => Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber)),
735
+ blockTimeAt: blockNumber => Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber, isRealtime)),
736
736
  shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
737
737
  progressBlockNumber: cs.committedProgressBlockNumber,
738
738
  sourceBlockNumber: cs.fetchState.knownHeight,
@@ -835,7 +835,7 @@ function markReady(cs, readyAt) {
835
835
  function rollbackCommittedProgress(cs, blockNumber) {
836
836
  if (blockNumber !== cs.committedProgressBlockNumber) {
837
837
  cs.committedProgressBlockNumber = blockNumber;
838
- cs.committedProgressBlockTime = Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber));
838
+ cs.committedProgressBlockTime = Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber, false));
839
839
  return;
840
840
  }
841
841
  }
@@ -119,7 +119,7 @@ let dispatch: (
119
119
  // Views.
120
120
  let toMetrics: t => Metrics.chainMetrics
121
121
  let toChainMetadata: t => InternalTable.Chains.metaFields
122
- let toChainBeforeBatch: t => Batch.chainBeforeBatch
122
+ let toChainBeforeBatch: (t, ~isRealtime: bool) => Batch.chainBeforeBatch
123
123
  let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
124
124
 
125
125
  // Derived (pure).
@@ -114,7 +114,7 @@ let createBatch = (
114
114
  ~history=config->HistoryPolicy.decide(~shouldSaveHistory=crossChainState->shouldSaveHistory),
115
115
  ~frontier,
116
116
  ~chainsBeforeBatch=crossChainState.chainStates->Utils.Dict.mapValues(
117
- ChainState.toChainBeforeBatch,
117
+ ChainState.toChainBeforeBatch(~isRealtime=crossChainState.isRealtime, ...),
118
118
  ),
119
119
  ~batchSizeTarget,
120
120
  )
@@ -91,7 +91,7 @@ function getSafeCheckpointIdByChain(crossChainState, sequence, committedFrontier
91
91
  }
92
92
 
93
93
  function createBatch(crossChainState, config, frontier, batchSizeTarget) {
94
- return Batch.make(config.checkpointSequence, frontier, Utils.Dict.mapValues(crossChainState.chainStates, ChainState.toChainBeforeBatch), batchSizeTarget, HistoryPolicy.decide(config, shouldSaveHistory(crossChainState)));
94
+ return Batch.make(config.checkpointSequence, frontier, Utils.Dict.mapValues(crossChainState.chainStates, none => ChainState.toChainBeforeBatch(none, crossChainState.isRealtime)), batchSizeTarget, HistoryPolicy.decide(config, shouldSaveHistory(crossChainState)));
95
95
  }
96
96
 
97
97
  function enterReorgThreshold(crossChainState) {
@@ -50,6 +50,22 @@ let makeSelection = (~onEventRegistrations, ~dependsOnAddresses, ~clientFiltered
50
50
  startBlock: ?deriveSelectionStartBlock(onEventRegistrations),
51
51
  }
52
52
 
53
+ // A partition's frontier never sits below the block before its selection can
54
+ // first match: the blocks below hold nothing for it, so they count as fetched
55
+ // the same way the blocks before an address's start block do. The chain's
56
+ // buffer frontier is its lowest partition frontier, so a partition left at the
57
+ // chain start would hold every other partition's events back as unprocessable
58
+ // and keep the chain's query target — sized from that frontier — short of
59
+ // anything worth asking for. A start block the chain has not reached yet leaves
60
+ // the partition waiting there, like an address partition does, rather than
61
+ // scanning empty ranges up to it; bufferBlockNumber caps the chain's frontier at
62
+ // the head meanwhile.
63
+ let floorAtSelectionStart = (latestFetchedBlock, ~selection) =>
64
+ switch selection.startBlock {
65
+ | Some(startBlock) => Pervasives.max(latestFetchedBlock, startBlock - 1)
66
+ | None => latestFetchedBlock
67
+ }
68
+
53
69
  type pendingQuery = {
54
70
  fromBlock: int,
55
71
  toBlock: option<int>,
@@ -398,6 +414,10 @@ module OptimizedPartitions = {
398
414
  ~dynamicContracts: Utils.Set.t<string>,
399
415
  ~clientFilteredContracts: Utils.Set.t<string>,
400
416
  ) => {
417
+ let partitions = partitions->Array.map(p => {
418
+ let floored = p.latestFetchedBlock->floorAtSelectionStart(~selection=p.selection)
419
+ floored === p.latestFetchedBlock ? p : {...p, latestFetchedBlock: floored}
420
+ })
401
421
  let newPartitions = []
402
422
  let mergingPartitions = Dict.make()
403
423
  let nextPartitionIndexRef = ref(nextPartitionIndex)
@@ -847,28 +867,17 @@ type t = {
847
867
  clientFilterAddressThreshold: option<int>,
848
868
  }
849
869
 
870
+ // The latest block whose items are all in the buffer: the lowest partition
871
+ // frontier, held back by the onBlock pointer. Without onBlock registrations the
872
+ // pointer tracks the known height, so this also caps the frontier at the head: a
873
+ // partition can sit past it, on a response that reported a block the chain
874
+ // hadn't heard of yet or on a start block the chain hasn't reached.
850
875
  @inline
851
876
  let bufferBlockNumber = ({latestOnBlockBlockNumber, optimizedPartitions}: t) => {
852
877
  switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
853
878
  | None => latestOnBlockBlockNumber
854
879
  | Some(latestFullyFetchedBlock) =>
855
- latestOnBlockBlockNumber < latestFullyFetchedBlock
856
- ? latestOnBlockBlockNumber
857
- : latestFullyFetchedBlock
858
- }
859
- }
860
-
861
- /**
862
- * Returns the latest block which is ready to be consumed
863
- */
864
- @inline
865
- let bufferBlock = ({optimizedPartitions, latestOnBlockBlockNumber}: t) => {
866
- switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
867
- | None => latestOnBlockBlockNumber
868
- | Some(latestFullyFetchedBlock) =>
869
- latestOnBlockBlockNumber < latestFullyFetchedBlock
870
- ? latestOnBlockBlockNumber
871
- : latestFullyFetchedBlock
880
+ Pervasives.min(latestOnBlockBlockNumber, latestFullyFetchedBlock)
872
881
  }
873
882
  }
874
883
 
@@ -1138,14 +1147,20 @@ let updateInternal = (
1138
1147
  // Use maxOnBlockBufferSize to get the last target item in the buffer
1139
1148
  // (sorted, so this is the highest-block item within the buffer cap).
1140
1149
  // All this needed to prevent OOM when adding too many block items to the queue
1141
- let maxBlockNumber = switch base->Array.get(fetchState.maxOnBlockBufferSize - 1) {
1142
- | Some(item) => item->Internal.getItemBlockNumber
1143
- | None =>
1144
- switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
1145
- | None => knownHeight
1146
- | Some(latestFullyFetchedBlock) => latestFullyFetchedBlock
1147
- }
1148
- }
1150
+ // Never past the head: a partition can sit there, on a start block the
1151
+ // chain hasn't reached, and a response is applied before the height it
1152
+ // reported, so its events can run past the height still known here.
1153
+ let maxBlockNumber = Pervasives.min(
1154
+ switch base->Array.get(fetchState.maxOnBlockBufferSize - 1) {
1155
+ | Some(item) => item->Internal.getItemBlockNumber
1156
+ | None =>
1157
+ switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
1158
+ | None => knownHeight
1159
+ | Some(latestFullyFetchedBlock) => latestFullyFetchedBlock
1160
+ }
1161
+ },
1162
+ knownHeight,
1163
+ )
1149
1164
  appendOnBlockItems(
1150
1165
  ~mutItems=blockItems,
1151
1166
  ~onBlockRegistrations,
@@ -2165,27 +2180,11 @@ let walkPartitionPending = (
2165
2180
  pqIdx := pqIdx.contents + 1
2166
2181
  }
2167
2182
 
2168
- // Nothing in this partition's selection can match below its earliest start
2169
- // block, so forward work skips straight to it instead of scanning up to it and
2170
- // discarding whole pages. Only the cursor moves — `latestFetchedBlock` still
2171
- // advances solely on a response, so no block is ever reported fetched that
2172
- // wasn't. Bounded by the head and the query end block: past either, the
2173
- // partition would offer no candidate at all, so it queries as before rather
2174
- // than going quiet until the chain reaches its start block.
2175
- let cursor = switch p.selection.startBlock {
2176
- | Some(startBlock) if startBlock > cursor.contents =>
2177
- switch Utils.Math.minOptInt(Some(headBlockNumber), queryEndBlock) {
2178
- | Some(reachable) if startBlock <= reachable => startBlock
2179
- | _ => cursor.contents
2180
- }
2181
- | _ => cursor.contents
2182
- }
2183
-
2184
2183
  canContinue.contents
2185
2184
  ? Some({
2186
2185
  partitionId,
2187
2186
  p,
2188
- cursor,
2187
+ cursor: cursor.contents,
2189
2188
  chunksUsedThisCall: chunksUsedThisCall.contents,
2190
2189
  inFlightCount,
2191
2190
  queryEndBlock,
@@ -2780,10 +2779,17 @@ let make = (
2780
2779
  // fetching, so without seeding the buffer here getNextQuery would return
2781
2780
  // NothingToQuery and the indexer would get stuck.
2782
2781
  let buffer = []
2783
- let latestOnBlockBlockNumber = if knownHeight > 0 && onBlockRegistrations->Utils.Array.notEmpty {
2782
+ let latestOnBlockBlockNumber = switch onBlockRegistrations {
2783
+ // As updateInternal keeps it: with nothing to generate per block the pointer
2784
+ // is the head cap on the buffer frontier, and left at the progress block it
2785
+ // would hold the frontier there until the first height update — below a
2786
+ // partition that starts later, whose queries the chain sizes off that
2787
+ // frontier and so never reaches.
2788
+ | [] if knownHeight > 0 => knownHeight
2789
+ | onBlockRegistrations if knownHeight > 0 =>
2784
2790
  let maxBlockNumber = switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
2785
2791
  | None => knownHeight
2786
- | Some(latestFullyFetchedBlock) => latestFullyFetchedBlock
2792
+ | Some(latestFullyFetchedBlock) => Pervasives.min(latestFullyFetchedBlock, knownHeight)
2787
2793
  }
2788
2794
  appendOnBlockItems(
2789
2795
  ~mutItems=buffer,
@@ -2793,8 +2799,7 @@ let make = (
2793
2799
  ~maxBlockNumber,
2794
2800
  ~maxOnBlockBufferSize,
2795
2801
  )
2796
- } else {
2797
- progressBlockNumber
2802
+ | _ => progressBlockNumber
2798
2803
  }
2799
2804
 
2800
2805
  let fetchState = {
@@ -47,6 +47,15 @@ function makeSelection(onEventRegistrations, dependsOnAddresses, clientFilteredC
47
47
  };
48
48
  }
49
49
 
50
+ function floorAtSelectionStart(latestFetchedBlock, selection) {
51
+ let startBlock = selection.startBlock;
52
+ if (startBlock !== undefined) {
53
+ return Primitive_int.max(latestFetchedBlock, startBlock - 1 | 0);
54
+ } else {
55
+ return latestFetchedBlock;
56
+ }
57
+ }
58
+
50
59
  function withAddresses(p, addresses) {
51
60
  return {
52
61
  id: p.id,
@@ -305,6 +314,26 @@ function getAnchorSafeBlock(p) {
305
314
  }
306
315
 
307
316
  function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContracts, clientFilteredContracts) {
317
+ let partitions$1 = partitions.map(p => {
318
+ let floored = floorAtSelectionStart(p.latestFetchedBlock, p.selection);
319
+ if (floored === p.latestFetchedBlock) {
320
+ return p;
321
+ } else {
322
+ return {
323
+ id: p.id,
324
+ latestFetchedBlock: floored,
325
+ selection: p.selection,
326
+ addresses: p.addresses,
327
+ mergeBlock: p.mergeBlock,
328
+ dynamicContract: p.dynamicContract,
329
+ mutPendingQueries: p.mutPendingQueries,
330
+ sourceRangeCapacity: p.sourceRangeCapacity,
331
+ prevSourceRangeCapacity: p.prevSourceRangeCapacity,
332
+ eventDensity: p.eventDensity,
333
+ latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
334
+ };
335
+ }
336
+ });
308
337
  let newPartitions = [];
309
338
  let mergingPartitions = {};
310
339
  let nextPartitionIndexRef = {
@@ -313,7 +342,7 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
313
342
  let anchorSafeBlocks = {};
314
343
  let anchoredContractsSet = new Set();
315
344
  if (clientFilteredContracts.size > 0) {
316
- partitions.forEach(p => {
345
+ partitions$1.forEach(p => {
317
346
  let match = p.mergeBlock;
318
347
  let match$1 = p.selection.clientFilteredContracts;
319
348
  if (match !== undefined) {
@@ -332,8 +361,8 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
332
361
  });
333
362
  });
334
363
  }
335
- for (let idx = 0, idx_finish = partitions.length; idx < idx_finish; ++idx) {
336
- let p = partitions[idx];
364
+ for (let idx = 0, idx_finish = partitions$1.length; idx < idx_finish; ++idx) {
365
+ let p = partitions$1[idx];
337
366
  let mergeBlock = p.mergeBlock;
338
367
  if (mergeBlock !== undefined) {
339
368
  if (p.latestFetchedBlock < mergeBlock || isFetching(p)) {
@@ -676,20 +705,8 @@ function bufferBlockNumber(param) {
676
705
  let optimizedPartitions = param.optimizedPartitions;
677
706
  let id = optimizedPartitions.idsInAscOrder[0];
678
707
  let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
679
- if (latestFullyFetchedBlock !== undefined && latestOnBlockBlockNumber >= latestFullyFetchedBlock) {
680
- return latestFullyFetchedBlock;
681
- } else {
682
- return latestOnBlockBlockNumber;
683
- }
684
- }
685
-
686
- function bufferBlock(param) {
687
- let latestOnBlockBlockNumber = param.latestOnBlockBlockNumber;
688
- let optimizedPartitions = param.optimizedPartitions;
689
- let id = optimizedPartitions.idsInAscOrder[0];
690
- let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
691
- if (latestFullyFetchedBlock !== undefined && latestOnBlockBlockNumber >= latestFullyFetchedBlock) {
692
- return latestFullyFetchedBlock;
708
+ if (latestFullyFetchedBlock !== undefined) {
709
+ return Primitive_int.min(latestOnBlockBlockNumber, latestFullyFetchedBlock);
693
710
  } else {
694
711
  return latestOnBlockBlockNumber;
695
712
  }
@@ -897,14 +914,15 @@ function updateInternal(fetchState, optimizedPartitionsOpt, mutItems, mutItemsSo
897
914
  let latestOnBlockBlockNumber;
898
915
  if (onBlockRegistrations.length !== 0) {
899
916
  let item = base[fetchState.maxOnBlockBufferSize - 1 | 0];
900
- let maxBlockNumber;
917
+ let tmp;
901
918
  if (item !== undefined) {
902
- maxBlockNumber = item.blockNumber;
919
+ tmp = item.blockNumber;
903
920
  } else {
904
921
  let id = optimizedPartitions.idsInAscOrder[0];
905
922
  let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
906
- maxBlockNumber = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock : knownHeight;
923
+ tmp = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock : knownHeight;
907
924
  }
925
+ let maxBlockNumber = Primitive_int.min(tmp, knownHeight);
908
926
  latestOnBlockBlockNumber = appendOnBlockItems(blockItems, onBlockRegistrations, fetchState.startBlock, fetchState.latestOnBlockBlockNumber, maxBlockNumber, fetchState.maxOnBlockBufferSize);
909
927
  } else {
910
928
  latestOnBlockBlockNumber = knownHeight;
@@ -1486,19 +1504,11 @@ function walkPartitionPending(p, partitionId, inFlightCount, candidates, headBlo
1486
1504
  }
1487
1505
  pqIdx = pqIdx + 1 | 0;
1488
1506
  };
1489
- let startBlock = p.selection.startBlock;
1490
- let cursor$1;
1491
- if (startBlock !== undefined && startBlock > cursor) {
1492
- let reachable = Utils.$$Math.minOptInt(headBlockNumber, queryEndBlock);
1493
- cursor$1 = reachable !== undefined && startBlock <= reachable ? startBlock : cursor;
1494
- } else {
1495
- cursor$1 = cursor;
1496
- }
1497
1507
  if (canContinue) {
1498
1508
  return {
1499
1509
  partitionId: partitionId,
1500
1510
  p: p,
1501
- cursor: cursor$1,
1511
+ cursor: cursor,
1502
1512
  chunksUsedThisCall: chunksUsedThisCall,
1503
1513
  inFlightCount: inFlightCount,
1504
1514
  queryEndBlock: queryEndBlock,
@@ -1800,13 +1810,21 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1800
1810
  }
1801
1811
  let buffer = [];
1802
1812
  let latestOnBlockBlockNumber;
1803
- if (knownHeight > 0 && Utils.$$Array.notEmpty(onBlockRegistrations)) {
1804
- let id = optimizedPartitions.idsInAscOrder[0];
1805
- let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
1806
- let maxBlockNumber = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock : knownHeight;
1807
- latestOnBlockBlockNumber = appendOnBlockItems(buffer, onBlockRegistrations, startBlock, progressBlockNumber, maxBlockNumber, maxOnBlockBufferSize);
1813
+ let exit = 0;
1814
+ if (onBlockRegistrations.length !== 0 || knownHeight <= 0) {
1815
+ exit = 1;
1808
1816
  } else {
1809
- latestOnBlockBlockNumber = progressBlockNumber;
1817
+ latestOnBlockBlockNumber = knownHeight;
1818
+ }
1819
+ if (exit === 1) {
1820
+ if (knownHeight > 0) {
1821
+ let id = optimizedPartitions.idsInAscOrder[0];
1822
+ let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
1823
+ let maxBlockNumber = latestFullyFetchedBlock !== undefined ? Primitive_int.min(latestFullyFetchedBlock, knownHeight) : knownHeight;
1824
+ latestOnBlockBlockNumber = appendOnBlockItems(buffer, onBlockRegistrations, startBlock, progressBlockNumber, maxBlockNumber, maxOnBlockBufferSize);
1825
+ } else {
1826
+ latestOnBlockBlockNumber = progressBlockNumber;
1827
+ }
1810
1828
  }
1811
1829
  return {
1812
1830
  optimizedPartitions: optimizedPartitions,
@@ -2111,6 +2129,7 @@ let chunkRangeGrowthFactor = 1.8;
2111
2129
  export {
2112
2130
  deriveSelectionStartBlock,
2113
2131
  makeSelection,
2132
+ floorAtSelectionStart,
2114
2133
  withAddresses,
2115
2134
  narrowSelectionToRange,
2116
2135
  densityItemsTarget,
@@ -2120,7 +2139,6 @@ export {
2120
2139
  getMinQueryRange,
2121
2140
  OptimizedPartitions,
2122
2141
  bufferBlockNumber,
2123
- bufferBlock,
2124
2142
  bufferReadyCount,
2125
2143
  getRegistrationIndex,
2126
2144
  comparePath,