envio 3.6.1 → 3.7.0
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/index.d.ts +197 -9
- package/package.json +6 -6
- package/src/Batch.res +64 -25
- package/src/Batch.res.mjs +69 -46
- package/src/ChainFetching.res +13 -17
- package/src/ChainFetching.res.mjs +9 -14
- package/src/ChainState.res +160 -119
- package/src/ChainState.res.mjs +75 -46
- package/src/ChainState.resi +15 -12
- package/src/Config.res +9 -2
- package/src/Config.res.mjs +4 -4
- package/src/Core.res +5 -0
- package/src/Ecosystem.res +0 -1
- package/src/Envio.res +16 -3
- package/src/EventConfigBuilder.res +155 -40
- package/src/EventConfigBuilder.res.mjs +76 -24
- package/src/EventProcessing.res +4 -8
- package/src/EventProcessing.res.mjs +4 -4
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/Internal.res +84 -24
- package/src/Internal.res.mjs +33 -2
- package/src/LazyLoader.res +12 -11
- package/src/LazyLoader.res.mjs +13 -7
- package/src/Main.res +22 -9
- package/src/Main.res.mjs +18 -5
- package/src/MemoryStorage.res +724 -0
- package/src/MemoryStorage.res.mjs +601 -0
- package/src/PgStorage.res +18 -7
- package/src/PgStorage.res.mjs +14 -11
- package/src/ReorgDetection.res +0 -222
- package/src/ReorgDetection.res.mjs +0 -163
- package/src/Rollback.res +14 -15
- package/src/Rollback.res.mjs +4 -5
- package/src/SimulateItems.res +2 -2
- package/src/Utils.res +3 -0
- package/src/bindings/ClickHouse.res +16 -11
- package/src/bindings/ClickHouse.res.mjs +10 -10
- package/src/bindings/Vitest.res +1 -1
- package/src/sources/BlockStore.res +115 -5
- package/src/sources/BlockStore.res.mjs +25 -0
- package/src/sources/Evm.res +0 -1
- package/src/sources/Evm.res.mjs +0 -1
- package/src/sources/EvmHyperSyncSource.res +19 -84
- package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
- package/src/sources/Fuel.res +24 -4
- package/src/sources/Fuel.res.mjs +23 -3
- package/src/sources/FuelHyperSync.res +8 -3
- package/src/sources/FuelHyperSync.res.mjs +5 -4
- package/src/sources/FuelHyperSync.resi +3 -1
- package/src/sources/FuelHyperSyncClient.res +7 -10
- package/src/sources/FuelHyperSyncSource.res +18 -45
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
- package/src/sources/HyperSync.res +49 -229
- package/src/sources/HyperSync.res.mjs +74 -191
- package/src/sources/HyperSync.resi +11 -35
- package/src/sources/HyperSyncClient.res +9 -79
- package/src/sources/HyperSyncClient.res.mjs +2 -6
- package/src/sources/RequestStat.res +5 -0
- package/src/sources/RequestStat.res.mjs +2 -0
- package/src/sources/RpcSource.res +149 -43
- package/src/sources/RpcSource.res.mjs +104 -41
- package/src/sources/SimulateSource.res +8 -5
- package/src/sources/SimulateSource.res.mjs +6 -6
- package/src/sources/Source.res +89 -16
- package/src/sources/Source.res.mjs +50 -1
- package/src/sources/SourceManager.res +255 -50
- package/src/sources/SourceManager.res.mjs +149 -55
- package/src/sources/SourceManager.resi +2 -6
- package/src/sources/Svm.res +0 -7
- package/src/sources/Svm.res.mjs +0 -8
- package/src/sources/SvmHyperSyncClient.res +21 -16
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
- package/src/sources/SvmHyperSyncSource.res +25 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/svm.schema.json +3 -2
|
@@ -152,7 +152,7 @@ async function waitForRateLimitReset(sourceManager, resetMs, retry, logger) {
|
|
|
152
152
|
let waitMs = Primitive_int.min(resetMs, 300000);
|
|
153
153
|
let log = retry >= 2 ? Logging.childWarn : Logging.childTrace;
|
|
154
154
|
log(logger, {
|
|
155
|
-
msg: `HyperSync source is rate-limited
|
|
155
|
+
msg: `HyperSync source is rate-limited - not critical, the indexer will retry in ` + (waitMs / 1000 | 0).toString() + `s. For higher limits upgrade your plan at https://envio.dev/app/api-tokens.`,
|
|
156
156
|
retry: retry,
|
|
157
157
|
waitMs: waitMs
|
|
158
158
|
});
|
|
@@ -170,11 +170,40 @@ async function waitForRateLimitReset(sourceManager, resetMs, retry, logger) {
|
|
|
170
170
|
sourceManager.activeRateLimitResetAtMs = undefined;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
function
|
|
173
|
+
function validateResponseBlockStore(method, blockStore, requiredBlockNumbersOpt) {
|
|
174
|
+
let requiredBlockNumbers = requiredBlockNumbersOpt !== undefined ? requiredBlockNumbersOpt : [];
|
|
175
|
+
let match = blockStore.responseConflict();
|
|
176
|
+
if (match !== null) {
|
|
177
|
+
throw {
|
|
178
|
+
RE_EXN_ID: Source.InconsistentResponse,
|
|
179
|
+
method: method,
|
|
180
|
+
blockNumber: match.blockNumber,
|
|
181
|
+
storedHash: match.storedHash,
|
|
182
|
+
receivedHash: match.receivedHash,
|
|
183
|
+
missingBlockNumbers: [],
|
|
184
|
+
Error: new Error()
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
let missingBlockNumbers = blockStore.missingHashes(requiredBlockNumbers);
|
|
188
|
+
if (missingBlockNumbers.length === 0) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
throw {
|
|
192
|
+
RE_EXN_ID: Source.InconsistentResponse,
|
|
193
|
+
method: method,
|
|
194
|
+
blockNumber: undefined,
|
|
195
|
+
storedHash: undefined,
|
|
196
|
+
receivedHash: undefined,
|
|
197
|
+
missingBlockNumbers: missingBlockNumbers,
|
|
198
|
+
Error: new Error()
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function onReorg(sourceManager) {
|
|
174
203
|
sourceManager.sourcesState.forEach(param => {
|
|
175
204
|
let cb = param.source.onReorg;
|
|
176
205
|
if (cb !== undefined) {
|
|
177
|
-
return cb(
|
|
206
|
+
return cb();
|
|
178
207
|
}
|
|
179
208
|
});
|
|
180
209
|
}
|
|
@@ -507,6 +536,71 @@ function getNextSource(sourceManager, isRealtime, excludedSources) {
|
|
|
507
536
|
}
|
|
508
537
|
}
|
|
509
538
|
|
|
539
|
+
function retryBackoffMillis(retry) {
|
|
540
|
+
return Primitive_float.min(100 * 2 ** retry, 60000) | 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillisOpt, excludedSources) {
|
|
544
|
+
let minBackoffMillis = minBackoffMillisOpt !== undefined ? minBackoffMillisOpt : 0;
|
|
545
|
+
let switchedToWorkingSource;
|
|
546
|
+
if (retry >= 2 && retry % 2 === 0) {
|
|
547
|
+
let now = Date.now();
|
|
548
|
+
sourceState.lastFailedAt = now;
|
|
549
|
+
let next = getNextSource(sourceManager, isRealtime, excludedSources);
|
|
550
|
+
if (next !== undefined) {
|
|
551
|
+
let failedAt = next.lastFailedAt;
|
|
552
|
+
switchedToWorkingSource = failedAt !== undefined ? now - failedAt >= sourceManager.recoveryTimeout : true;
|
|
553
|
+
} else {
|
|
554
|
+
switchedToWorkingSource = false;
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
switchedToWorkingSource = false;
|
|
558
|
+
}
|
|
559
|
+
if (switchedToWorkingSource) {
|
|
560
|
+
if (minBackoffMillis > 0) {
|
|
561
|
+
return await Utils.delay(minBackoffMillis);
|
|
562
|
+
} else {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
} else {
|
|
566
|
+
return await Utils.delay(Primitive_int.min(Primitive_int.max(backoffMillis, minBackoffMillis), 60000));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async function retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger, blockNumber, method, err, excludedSources) {
|
|
571
|
+
let backoffMillis = retryBackoffMillis(retry);
|
|
572
|
+
let log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
|
|
573
|
+
log(logger, {
|
|
574
|
+
msg: `Block #` + blockNumber.toString() + ` is not available on the ` + sourceState.source.name + ` source yet. Instances of a load-balanced backend drift slightly around the head, so this is expected - indexing continues after an automatic retry.`,
|
|
575
|
+
method: method,
|
|
576
|
+
retry: retry,
|
|
577
|
+
backOffMilliseconds: backoffMillis,
|
|
578
|
+
err: Utils.prettifyExn(err)
|
|
579
|
+
});
|
|
580
|
+
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async function retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger, method, err, excludedSources) {
|
|
584
|
+
let backoffMillis = retryBackoffMillis(retry);
|
|
585
|
+
let msg = `Received a partial indicator of a possible reorg from the ` + sourceState.source.name + ` source while fetching ` + method + `. Retrying the request to better identify whether a reorg happened.`;
|
|
586
|
+
let match = retry >= 13 ? [
|
|
587
|
+
Logging.childError,
|
|
588
|
+
msg + " It has disagreed with itself on every attempt for several minutes now, so this chain has stopped making progress - the endpoint is likely serving blocks and logs from nodes on different chains."
|
|
589
|
+
] : [
|
|
590
|
+
retry >= 2 ? Logging.childWarn : Logging.childTrace,
|
|
591
|
+
msg
|
|
592
|
+
];
|
|
593
|
+
match[0](logger, {
|
|
594
|
+
msg: match[1],
|
|
595
|
+
method: method,
|
|
596
|
+
retry: retry,
|
|
597
|
+
backOffMilliseconds: backoffMillis,
|
|
598
|
+
err: Utils.prettifyExn(err)
|
|
599
|
+
});
|
|
600
|
+
Stdlib_Option.forEach(sourceState.source.onReorg, cb => cb());
|
|
601
|
+
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
|
|
602
|
+
}
|
|
603
|
+
|
|
510
604
|
async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling) {
|
|
511
605
|
let sourcesState = sourceManager.sourcesState;
|
|
512
606
|
let logger = Logging.createChild({
|
|
@@ -606,31 +700,41 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
606
700
|
try {
|
|
607
701
|
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, FetchState.narrowSelectionToRange(query.selection, toBlock), query.itemsTarget, retry, logger$2);
|
|
608
702
|
recordRequestStats(sourceState, response.requestStats);
|
|
703
|
+
validateResponseBlockStore("getItems", response.blockStore, undefined);
|
|
609
704
|
sourceState.lastFailedAt = undefined;
|
|
610
705
|
if (response.knownHeight > sourceState.knownHeight) {
|
|
611
706
|
sourceState.knownHeight = response.knownHeight;
|
|
612
707
|
}
|
|
613
708
|
responseRef = response;
|
|
614
|
-
} catch (
|
|
615
|
-
let
|
|
616
|
-
if (
|
|
617
|
-
|
|
709
|
+
} catch (raw_err) {
|
|
710
|
+
let err = Primitive_exceptions.internalToException(raw_err);
|
|
711
|
+
if (err.RE_EXN_ID === Source.RateLimited) {
|
|
712
|
+
let match = err._1;
|
|
713
|
+
recordRequestStats(sourceState, match.requestStats);
|
|
714
|
+
await waitForRateLimitReset(sourceManager, match.resetMs, retry, logger$2);
|
|
715
|
+
retryRef = retryRef + 1 | 0;
|
|
716
|
+
} else if (err.RE_EXN_ID === Source.SourceBehindHead) {
|
|
717
|
+
recordRequestStats(sourceState, err.requestStats);
|
|
718
|
+
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$2, err.blockNumber, "getItems", err, excludedSourcesRef);
|
|
719
|
+
retryRef = retryRef + 1 | 0;
|
|
720
|
+
} else if (err.RE_EXN_ID === Source.InconsistentResponse) {
|
|
721
|
+
await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$2, "getItems", err, excludedSourcesRef);
|
|
618
722
|
retryRef = retryRef + 1 | 0;
|
|
619
|
-
} else if (
|
|
620
|
-
let error
|
|
723
|
+
} else if (err.RE_EXN_ID === Source.GetItemsError) {
|
|
724
|
+
let error = err._1;
|
|
621
725
|
let exit = 0;
|
|
622
|
-
switch (error
|
|
726
|
+
switch (error.TAG) {
|
|
623
727
|
case "UnsupportedSelection" :
|
|
624
728
|
case "FailedGettingFieldSelection" :
|
|
625
729
|
exit = 1;
|
|
626
730
|
break;
|
|
627
731
|
case "FailedGettingItems" :
|
|
628
|
-
let match = error
|
|
629
|
-
let attemptedToBlock = error
|
|
630
|
-
let exn = error
|
|
631
|
-
switch (match.TAG) {
|
|
732
|
+
let match$1 = error.retry;
|
|
733
|
+
let attemptedToBlock = error.attemptedToBlock;
|
|
734
|
+
let exn = error.exn;
|
|
735
|
+
switch (match$1.TAG) {
|
|
632
736
|
case "WithSuggestedToBlock" :
|
|
633
|
-
let toBlock$1 = match.toBlock;
|
|
737
|
+
let toBlock$1 = match$1.toBlock;
|
|
634
738
|
Logging.childTrace(logger$2, {
|
|
635
739
|
msg: "Failed getting data for the block range. Immediately retrying with the suggested block range from response.",
|
|
636
740
|
toBlock: attemptedToBlock,
|
|
@@ -640,33 +744,16 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
640
744
|
retryRef = 0;
|
|
641
745
|
break;
|
|
642
746
|
case "WithBackoff" :
|
|
643
|
-
let backoffMillis = match.backoffMillis;
|
|
747
|
+
let backoffMillis = match$1.backoffMillis;
|
|
644
748
|
let log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
|
|
645
749
|
log(logger$2, {
|
|
646
|
-
msg: match.message,
|
|
750
|
+
msg: match$1.message,
|
|
647
751
|
toBlock: attemptedToBlock,
|
|
648
752
|
backOffMilliseconds: backoffMillis,
|
|
649
753
|
retry: retry,
|
|
650
754
|
err: Utils.prettifyExn(exn)
|
|
651
755
|
});
|
|
652
|
-
|
|
653
|
-
if (shouldSwitch) {
|
|
654
|
-
let now = Date.now();
|
|
655
|
-
sourceState.lastFailedAt = now;
|
|
656
|
-
let nextSource = getNextSource(sourceManager, isRealtime, excludedSourcesRef);
|
|
657
|
-
let hasWorkingAlternative;
|
|
658
|
-
if (nextSource !== undefined) {
|
|
659
|
-
let failedAt = nextSource.lastFailedAt;
|
|
660
|
-
hasWorkingAlternative = failedAt !== undefined ? now - failedAt >= sourceManager.recoveryTimeout : true;
|
|
661
|
-
} else {
|
|
662
|
-
hasWorkingAlternative = false;
|
|
663
|
-
}
|
|
664
|
-
if (!hasWorkingAlternative) {
|
|
665
|
-
await Utils.delay(Primitive_int.min(backoffMillis, 60000));
|
|
666
|
-
}
|
|
667
|
-
} else {
|
|
668
|
-
await Utils.delay(Primitive_int.min(backoffMillis, 60000));
|
|
669
|
-
}
|
|
756
|
+
await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, undefined, excludedSourcesRef);
|
|
670
757
|
retryRef = retryRef + 1 | 0;
|
|
671
758
|
break;
|
|
672
759
|
case "ImpossibleForTheQuery" :
|
|
@@ -681,7 +768,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
681
768
|
}
|
|
682
769
|
excludedSources.add(sourceState);
|
|
683
770
|
Logging.childWarn(logger$2, {
|
|
684
|
-
msg: match.message + " - Attempting another source",
|
|
771
|
+
msg: match$1.message + " - Attempting another source",
|
|
685
772
|
toBlock: attemptedToBlock,
|
|
686
773
|
err: Utils.prettifyExn(exn)
|
|
687
774
|
});
|
|
@@ -693,16 +780,16 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
693
780
|
if (exit === 1) {
|
|
694
781
|
let notAlreadyDisabled = disableSource(sourceManager, sourceState);
|
|
695
782
|
if (notAlreadyDisabled) {
|
|
696
|
-
switch (error
|
|
783
|
+
switch (error.TAG) {
|
|
697
784
|
case "UnsupportedSelection" :
|
|
698
|
-
Logging.childError(logger$2, error
|
|
785
|
+
Logging.childError(logger$2, error.message);
|
|
699
786
|
break;
|
|
700
787
|
case "FailedGettingFieldSelection" :
|
|
701
788
|
Logging.childError(logger$2, {
|
|
702
|
-
msg: error
|
|
703
|
-
err: Utils.prettifyExn(error
|
|
704
|
-
blockNumber: error
|
|
705
|
-
logIndex: error
|
|
789
|
+
msg: error.message,
|
|
790
|
+
err: Utils.prettifyExn(error.exn),
|
|
791
|
+
blockNumber: error.blockNumber,
|
|
792
|
+
logIndex: error.logIndex
|
|
706
793
|
});
|
|
707
794
|
break;
|
|
708
795
|
case "FailedGettingItems" :
|
|
@@ -712,7 +799,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
712
799
|
retryRef = 0;
|
|
713
800
|
}
|
|
714
801
|
} else {
|
|
715
|
-
ErrorHandling.mkLogAndRaise(logger$2, "Failed to fetch block Range",
|
|
802
|
+
ErrorHandling.mkLogAndRaise(logger$2, "Failed to fetch block Range", err);
|
|
716
803
|
}
|
|
717
804
|
}
|
|
718
805
|
};
|
|
@@ -747,30 +834,37 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
747
834
|
recordRequestStats(sourceState, res.requestStats);
|
|
748
835
|
let data = res.result;
|
|
749
836
|
if (data.TAG === "Ok") {
|
|
837
|
+
let data$1 = data._0;
|
|
838
|
+
validateResponseBlockStore("getBlockHashes", data$1, blockNumbers);
|
|
750
839
|
sourceState.lastFailedAt = undefined;
|
|
751
|
-
responseRef = data
|
|
840
|
+
responseRef = Primitive_option.some(data$1);
|
|
752
841
|
} else {
|
|
753
842
|
throw data._0;
|
|
754
843
|
}
|
|
755
|
-
} catch (
|
|
756
|
-
let
|
|
757
|
-
if (
|
|
758
|
-
|
|
844
|
+
} catch (raw_err) {
|
|
845
|
+
let err = Primitive_exceptions.internalToException(raw_err);
|
|
846
|
+
if (err.RE_EXN_ID === Source.RateLimited) {
|
|
847
|
+
let match = err._1;
|
|
848
|
+
recordRequestStats(sourceState, match.requestStats);
|
|
849
|
+
await waitForRateLimitReset(sourceManager, match.resetMs, retry, logger$1);
|
|
850
|
+
retryRef = retryRef + 1 | 0;
|
|
851
|
+
} else if (err.RE_EXN_ID === Source.SourceBehindHead) {
|
|
852
|
+
recordRequestStats(sourceState, err.requestStats);
|
|
853
|
+
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$1, err.blockNumber, "getBlockHashes", err, undefined);
|
|
854
|
+
retryRef = retryRef + 1 | 0;
|
|
855
|
+
} else if (err.RE_EXN_ID === Source.InconsistentResponse) {
|
|
856
|
+
await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$1, "getBlockHashes", err, undefined);
|
|
759
857
|
retryRef = retryRef + 1 | 0;
|
|
760
858
|
} else {
|
|
761
|
-
let backoffMillis = retry
|
|
859
|
+
let backoffMillis = retryBackoffMillis(retry);
|
|
762
860
|
let log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
|
|
763
861
|
log(logger$1, {
|
|
764
862
|
msg: "Failed to fetch block hashes. Retrying.",
|
|
765
863
|
retry: retry,
|
|
766
864
|
backOffMilliseconds: backoffMillis,
|
|
767
|
-
err: Utils.prettifyExn(
|
|
865
|
+
err: Utils.prettifyExn(err)
|
|
768
866
|
});
|
|
769
|
-
|
|
770
|
-
if (shouldSwitch) {
|
|
771
|
-
sourceState.lastFailedAt = Date.now();
|
|
772
|
-
}
|
|
773
|
-
await Utils.delay(Primitive_int.min(backoffMillis, 60000));
|
|
867
|
+
await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined);
|
|
774
868
|
retryRef = retryRef + 1 | 0;
|
|
775
869
|
}
|
|
776
870
|
}
|
|
@@ -45,7 +45,7 @@ let queryingSeconds: t => float
|
|
|
45
45
|
|
|
46
46
|
let inFlightCount: t => int
|
|
47
47
|
|
|
48
|
-
let onReorg:
|
|
48
|
+
let onReorg: t => unit
|
|
49
49
|
|
|
50
50
|
let dispatch: (
|
|
51
51
|
t,
|
|
@@ -77,11 +77,7 @@ let makeGetHeightRetryInterval: (
|
|
|
77
77
|
~maxRetryInterval: int,
|
|
78
78
|
) => (~retry: int) => int
|
|
79
79
|
|
|
80
|
-
let getBlockHashes: (
|
|
81
|
-
t,
|
|
82
|
-
~blockNumbers: array<int>,
|
|
83
|
-
~isRealtime: bool,
|
|
84
|
-
) => promise<array<ReorgDetection.blockDataWithTimestamp>>
|
|
80
|
+
let getBlockHashes: (t, ~blockNumbers: array<int>, ~isRealtime: bool) => promise<BlockStore.t>
|
|
85
81
|
|
|
86
82
|
// Total time the indexer spent waiting on rate limits, including any
|
|
87
83
|
// currently-in-progress window measured against Date.now() at call time.
|
package/src/sources/Svm.res
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
|
|
2
|
-
delete fields.hash
|
|
3
|
-
delete fields.height
|
|
4
|
-
delete fields.time
|
|
5
|
-
}`)
|
|
6
|
-
|
|
7
1
|
// Ordered transaction field names, the field codes shared with the Rust store
|
|
8
2
|
// (`SvmTxField`). Derived from the typed field list so the two can't drift;
|
|
9
3
|
// `Internal.allSvmTransactionFields` is pinned to the Rust ordinal order by a test.
|
|
@@ -32,7 +26,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
32
26
|
blockNumberName: "height",
|
|
33
27
|
blockTimestampName: "time",
|
|
34
28
|
blockHashName: "hash",
|
|
35
|
-
cleanUpRawEventFieldsInPlace,
|
|
36
29
|
onBlockMethodName: "onSlot",
|
|
37
30
|
// SVM filter shape: `{slot: {_gte?, _lte?, _every?}}`.
|
|
38
31
|
// Inner range chunk parsed by `blockRangeSchema` in `Main.res`.
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -12,12 +12,6 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
12
12
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
13
13
|
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
14
14
|
|
|
15
|
-
let cleanUpRawEventFieldsInPlace = (fields => {
|
|
16
|
-
delete fields.hash
|
|
17
|
-
delete fields.height
|
|
18
|
-
delete fields.time
|
|
19
|
-
});
|
|
20
|
-
|
|
21
15
|
let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allSvmTransactionFields);
|
|
22
16
|
|
|
23
17
|
let blockFields = [
|
|
@@ -37,7 +31,6 @@ function make(logger) {
|
|
|
37
31
|
blockNumberName: "height",
|
|
38
32
|
blockTimestampName: "time",
|
|
39
33
|
blockHashName: "hash",
|
|
40
|
-
cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
|
|
41
34
|
onBlockMethodName: "onSlot",
|
|
42
35
|
onBlockFilterSchema: S$RescriptSchema.object(s => s.f("slot", S$RescriptSchema.option(S$RescriptSchema.unknown))),
|
|
43
36
|
onEventBlockFilterSchema: S$RescriptSchema.object(param => {}),
|
|
@@ -99,7 +92,6 @@ function makeRPCSource(chainId, rpc, sourceForOpt) {
|
|
|
99
92
|
let transactionFields = Internal.allSvmTransactionFields;
|
|
100
93
|
|
|
101
94
|
export {
|
|
102
|
-
cleanUpRawEventFieldsInPlace,
|
|
103
95
|
transactionFields,
|
|
104
96
|
eventTransactionFieldMask,
|
|
105
97
|
blockFields,
|
|
@@ -4,7 +4,6 @@ type cfg = {
|
|
|
4
4
|
/** Optional bearer token for the HyperSync server. */
|
|
5
5
|
apiToken?: string,
|
|
6
6
|
httpReqTimeoutMillis?: int,
|
|
7
|
-
maxNumRetries?: int,
|
|
8
7
|
retryBaseMs?: int,
|
|
9
8
|
retryCeilingMs?: int,
|
|
10
9
|
}
|
|
@@ -67,10 +66,8 @@ module Registration = {
|
|
|
67
66
|
},
|
|
68
67
|
)
|
|
69
68
|
),
|
|
70
|
-
transactionFields:
|
|
71
|
-
blockFields:
|
|
72
|
-
->(Utils.magic: Utils.Set.t<Internal.svmBlockField> => Utils.Set.t<string>)
|
|
73
|
-
->Utils.Set.toArray,
|
|
69
|
+
transactionFields: reg.fieldSelection.transactionFields->Utils.Set.toArray,
|
|
70
|
+
blockFields: reg.fieldSelection.blockFields->Utils.Set.toArray,
|
|
74
71
|
accounts: eventConfig.accounts,
|
|
75
72
|
argsJson: ?switch eventConfig.args {
|
|
76
73
|
| JSON.Null => None
|
|
@@ -110,18 +107,20 @@ module QueryTypes = {
|
|
|
110
107
|
|
|
111
108
|
type fieldSelection = {block?: array<blockField>, transaction?: array<transactionField>}
|
|
112
109
|
|
|
113
|
-
/** Filter for selecting
|
|
114
|
-
instruction must match at least one value in every non-empty field.
|
|
110
|
+
/** Filter for selecting instruction calls. All non-empty fields are AND-ed:
|
|
111
|
+
an instruction must match at least one value in every non-empty field.
|
|
115
112
|
|
|
116
113
|
Discriminator filters (d1..d8) take hex-encoded byte prefixes ("0x" optional).
|
|
117
114
|
Account filters (a0..a9) take base58 pubkey strings. */
|
|
118
115
|
type instructionSelection = {
|
|
119
|
-
|
|
116
|
+
executingAccount?: array<string>,
|
|
120
117
|
d1?: array<string>,
|
|
121
118
|
d2?: array<string>,
|
|
122
119
|
d4?: array<string>,
|
|
123
120
|
d8?: array<string>,
|
|
124
121
|
isInner?: bool,
|
|
122
|
+
/** Success of the parent transaction; absent matches both. */
|
|
123
|
+
txSuccess?: bool,
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
// The `get` query surface, used only for block-data range queries; event
|
|
@@ -129,7 +128,7 @@ module QueryTypes = {
|
|
|
129
128
|
type query = {
|
|
130
129
|
fromSlot: int,
|
|
131
130
|
toSlot?: int,
|
|
132
|
-
|
|
131
|
+
instructionCalls?: array<instructionSelection>,
|
|
133
132
|
includeAllBlocks?: bool,
|
|
134
133
|
fields?: fieldSelection,
|
|
135
134
|
maxNumBlocks?: int,
|
|
@@ -155,24 +154,29 @@ module ResponseTypes = {
|
|
|
155
154
|
extraAccounts: array<string>,
|
|
156
155
|
}
|
|
157
156
|
|
|
158
|
-
type
|
|
157
|
+
type instructionCall = {
|
|
159
158
|
slot: int,
|
|
160
159
|
transactionIndex: int,
|
|
161
160
|
instructionAddress: array<int>,
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
/** The invoked program's account. */
|
|
162
|
+
executingAccount: string,
|
|
163
|
+
accountArguments: array<string>,
|
|
164
164
|
data: string,
|
|
165
165
|
d1?: string,
|
|
166
166
|
d2?: string,
|
|
167
167
|
d4?: string,
|
|
168
168
|
d8?: string,
|
|
169
169
|
isInner: bool,
|
|
170
|
-
|
|
170
|
+
/** Success of the parent transaction, not of this invocation. */
|
|
171
|
+
txSuccess: bool,
|
|
172
|
+
/** Per-invocation failure reason (e.g. "custom program error: 0x1"). */
|
|
173
|
+
error?: string,
|
|
174
|
+
computeUnitsConsumed?: bigint,
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
type queryResponseData = {
|
|
174
178
|
blocks: array<block>,
|
|
175
|
-
|
|
179
|
+
instructionCalls: array<instructionCall>,
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
type queryResponse = {
|
|
@@ -241,6 +245,9 @@ type queryResponse = ResponseTypes.queryResponse
|
|
|
241
245
|
|
|
242
246
|
type t = {
|
|
243
247
|
getHeight: unit => promise<int>,
|
|
248
|
+
// Block-hash query construction, pagination, and cursor-backed skipped-slot
|
|
249
|
+
// coverage live in Rust.
|
|
250
|
+
getBlockHashes: (~blockNumbers: array<int>) => promise<(BlockStore.t, array<RequestStat.t>)>,
|
|
244
251
|
// Block-data range queries only; the store pages it returns are empty.
|
|
245
252
|
get: (~query: query) => promise<(queryResponse, TransactionStore.t, BlockStore.t)>,
|
|
246
253
|
// Returns the routed items plus pages of raw transactions and blocks (kept
|
|
@@ -265,7 +272,6 @@ let make = (
|
|
|
265
272
|
~url,
|
|
266
273
|
~apiToken=?,
|
|
267
274
|
~httpReqTimeoutMillis=?,
|
|
268
|
-
~maxNumRetries=?,
|
|
269
275
|
~retryBaseMs=?,
|
|
270
276
|
~retryCeilingMs=?,
|
|
271
277
|
~eventRegistrations=[],
|
|
@@ -277,7 +283,6 @@ let make = (
|
|
|
277
283
|
url,
|
|
278
284
|
?apiToken,
|
|
279
285
|
?httpReqTimeoutMillis,
|
|
280
|
-
?maxNumRetries,
|
|
281
286
|
?retryBaseMs,
|
|
282
287
|
?retryCeilingMs,
|
|
283
288
|
},
|
|
@@ -27,8 +27,8 @@ function fromOnEventRegistrations(onEventRegistrations) {
|
|
|
27
27
|
position: filter.position,
|
|
28
28
|
values: filter.values
|
|
29
29
|
}))),
|
|
30
|
-
transactionFields: Array.from(
|
|
31
|
-
blockFields: Array.from(
|
|
30
|
+
transactionFields: Array.from(reg.fieldSelection.transactionFields),
|
|
31
|
+
blockFields: Array.from(reg.fieldSelection.blockFields),
|
|
32
32
|
accounts: eventConfig.accounts,
|
|
33
33
|
argsJson: tmp,
|
|
34
34
|
definedTypesJson: tmp$1
|
|
@@ -46,14 +46,13 @@ let ResponseTypes = {};
|
|
|
46
46
|
|
|
47
47
|
let EventItems = {};
|
|
48
48
|
|
|
49
|
-
function make(url, apiToken, httpReqTimeoutMillis,
|
|
49
|
+
function make(url, apiToken, httpReqTimeoutMillis, retryBaseMs, retryCeilingMs, eventRegistrationsOpt, addressStore) {
|
|
50
50
|
let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
|
|
51
51
|
let envioVersion = Utils.EnvioPackage.value.version;
|
|
52
52
|
return Core.getAddon().SvmHyperSyncClient.fromConfig({
|
|
53
53
|
url: url,
|
|
54
54
|
apiToken: apiToken,
|
|
55
55
|
httpReqTimeoutMillis: httpReqTimeoutMillis,
|
|
56
|
-
maxNumRetries: maxNumRetries,
|
|
57
56
|
retryBaseMs: retryBaseMs,
|
|
58
57
|
retryCeilingMs: retryCeilingMs
|
|
59
58
|
}, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
|