envio 3.6.1 → 3.7.0-subgraph
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 +18 -2
- package/src/Config.res.mjs +9 -6
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +11 -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 +10 -14
- package/src/EventProcessing.res.mjs +12 -10
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/InMemoryStore.res +1 -6
- package/src/InMemoryStore.res.mjs +1 -3
- 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 +756 -0
- package/src/MemoryStorage.res.mjs +623 -0
- package/src/Persistence.res +4 -2
- package/src/PgStorage.res +24 -8
- package/src/PgStorage.res.mjs +15 -12
- 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/UserContext.res +358 -51
- package/src/UserContext.res.mjs +271 -35
- 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/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +217 -0
- package/src/subgraph/conformance.ts +99 -0
- package/src/subgraph/division.ts +110 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1871 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +845 -0
- package/src/subgraph/scope.ts +63 -0
- package/svm.schema.json +3 -2
package/src/PgStorage.res
CHANGED
|
@@ -630,14 +630,19 @@ let classifyWriteError = (~specificError: ref<option<exn>>, ~table: Table.table,
|
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
-
//
|
|
634
|
-
|
|
633
|
+
// Batch set queries, cached per table. The query text bakes in the schema and
|
|
634
|
+
// the chain-id mode, so the cache belongs to the storage instance those came
|
|
635
|
+
// from — `make` creates one and threads it down. A process-wide cache would
|
|
636
|
+
// hand a second storage the first one's schema.
|
|
637
|
+
let makeSetQueryCache = () => Utils.WeakMap.make()
|
|
638
|
+
|
|
635
639
|
let setOrThrow = async (
|
|
636
640
|
sql,
|
|
637
641
|
~items,
|
|
638
642
|
~table: Table.table,
|
|
639
643
|
~itemSchema,
|
|
640
644
|
~pgSchema,
|
|
645
|
+
~setQueryCache,
|
|
641
646
|
~chainIdMode: ChainId.mode=Int32,
|
|
642
647
|
) => {
|
|
643
648
|
if items->Array.length === 0 {
|
|
@@ -953,6 +958,7 @@ let rec writeBatch = async (
|
|
|
953
958
|
~config: Config.t,
|
|
954
959
|
~allEntities: array<Internal.entityConfig>,
|
|
955
960
|
~setEffectCacheOrThrow,
|
|
961
|
+
~setQueryCache,
|
|
956
962
|
~updatedEffectsCache,
|
|
957
963
|
~updatedEntities: array<Persistence.updatedEntity>,
|
|
958
964
|
~sinkPromise: option<promise<option<exn>>>,
|
|
@@ -973,11 +979,8 @@ let rec writeBatch = async (
|
|
|
973
979
|
let rows = batch.items->Array.filterMap(item =>
|
|
974
980
|
switch item {
|
|
975
981
|
| Internal.Event(_) =>
|
|
976
|
-
let
|
|
977
|
-
|
|
978
|
-
->ChainId.toString}-${item
|
|
979
|
-
->Internal.getItemBlockNumber
|
|
980
|
-
->Int.toString}-${item->Internal.getItemLogIndex->Int.toString}`
|
|
982
|
+
let eventItem = item->Internal.castUnsafeEventItem
|
|
983
|
+
let coordinate = `${eventItem.chainId->ChainId.toString}-${eventItem.blockNumber->Int.toString}-${eventItem.logIndex->Int.toString}`
|
|
981
984
|
if seenLogCoordinates->Utils.Set.has(coordinate) {
|
|
982
985
|
None
|
|
983
986
|
} else {
|
|
@@ -1006,6 +1009,7 @@ let rec writeBatch = async (
|
|
|
1006
1009
|
~itemSchema=InternalTable.RawEvents.schema,
|
|
1007
1010
|
~pgSchema,
|
|
1008
1011
|
~chainIdMode,
|
|
1012
|
+
~setQueryCache,
|
|
1009
1013
|
)
|
|
1010
1014
|
}, ~items=rawEvents)
|
|
1011
1015
|
} catch {
|
|
@@ -1161,6 +1165,7 @@ let rec writeBatch = async (
|
|
|
1161
1165
|
~table=entityHistory.table,
|
|
1162
1166
|
~pgSchema,
|
|
1163
1167
|
~chainIdMode,
|
|
1168
|
+
~setQueryCache,
|
|
1164
1169
|
),
|
|
1165
1170
|
)
|
|
1166
1171
|
->ignore
|
|
@@ -1178,6 +1183,7 @@ let rec writeBatch = async (
|
|
|
1178
1183
|
~itemSchema=entityConfig->getRowSchema,
|
|
1179
1184
|
~pgSchema,
|
|
1180
1185
|
~chainIdMode,
|
|
1186
|
+
~setQueryCache,
|
|
1181
1187
|
),
|
|
1182
1188
|
)
|
|
1183
1189
|
}
|
|
@@ -1340,6 +1346,7 @@ let rec writeBatch = async (
|
|
|
1340
1346
|
~escapeTables,
|
|
1341
1347
|
~batch,
|
|
1342
1348
|
~pgSchema,
|
|
1349
|
+
~setQueryCache,
|
|
1343
1350
|
~rollback,
|
|
1344
1351
|
~isInReorgThreshold,
|
|
1345
1352
|
~config,
|
|
@@ -1483,6 +1490,7 @@ let make = (
|
|
|
1483
1490
|
let storageName = "postgres"
|
|
1484
1491
|
|
|
1485
1492
|
let indexManager = IndexManager.make()
|
|
1493
|
+
let setQueryCache = makeSetQueryCache()
|
|
1486
1494
|
|
|
1487
1495
|
let loadCatalogRows = (sql, ~indexName=?) =>
|
|
1488
1496
|
sql
|
|
@@ -2064,6 +2072,8 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2064
2072
|
~table,
|
|
2065
2073
|
~itemSchema=itemSchema->S.toUnknown,
|
|
2066
2074
|
~pgSchema,
|
|
2075
|
+
~setQueryCache,
|
|
2076
|
+
~chainIdMode,
|
|
2067
2077
|
)
|
|
2068
2078
|
}
|
|
2069
2079
|
|
|
@@ -2298,7 +2308,12 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2298
2308
|
}
|
|
2299
2309
|
})
|
|
2300
2310
|
|
|
2301
|
-
(
|
|
2311
|
+
(
|
|
2312
|
+
removals,
|
|
2313
|
+
restoredEntitiesResult
|
|
2314
|
+
->S.parseOrThrow(entityConfig.table->Table.pgRowsSchema)
|
|
2315
|
+
->(Utils.magic: array<unknown> => array<Internal.entity>),
|
|
2316
|
+
)
|
|
2302
2317
|
}
|
|
2303
2318
|
|
|
2304
2319
|
let writeBatchMethod = async (
|
|
@@ -2347,6 +2362,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
|
|
|
2347
2362
|
sql,
|
|
2348
2363
|
~batch,
|
|
2349
2364
|
~pgSchema,
|
|
2365
|
+
~setQueryCache,
|
|
2350
2366
|
~rollback,
|
|
2351
2367
|
~isInReorgThreshold,
|
|
2352
2368
|
~config,
|
package/src/PgStorage.res.mjs
CHANGED
|
@@ -471,9 +471,11 @@ function classifyWriteError(specificError, table, exn) {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
|
|
474
|
+
function makeSetQueryCache() {
|
|
475
|
+
return new WeakMap();
|
|
476
|
+
}
|
|
475
477
|
|
|
476
|
-
async function setOrThrow(sql, items, table, itemSchema, pgSchema, chainIdModeOpt) {
|
|
478
|
+
async function setOrThrow(sql, items, table, itemSchema, pgSchema, setQueryCache, chainIdModeOpt) {
|
|
477
479
|
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
478
480
|
if (items.length === 0) {
|
|
479
481
|
return;
|
|
@@ -656,7 +658,7 @@ function executeSet(sql, items, dbFunction) {
|
|
|
656
658
|
}
|
|
657
659
|
}
|
|
658
660
|
|
|
659
|
-
async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, escapeTables) {
|
|
661
|
+
async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, setQueryCache, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, escapeTables) {
|
|
660
662
|
try {
|
|
661
663
|
let chainIdMode = config.chainIdMode;
|
|
662
664
|
let shouldSaveHistory = Config.shouldSaveHistory(config, isInReorgThreshold);
|
|
@@ -670,7 +672,7 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
670
672
|
if (item.kind !== 0) {
|
|
671
673
|
return;
|
|
672
674
|
}
|
|
673
|
-
let coordinate = ChainId.toString(
|
|
675
|
+
let coordinate = ChainId.toString(item.chainId) + `-` + item.blockNumber.toString() + `-` + item.logIndex.toString();
|
|
674
676
|
if (seenLogCoordinates.has(coordinate)) {
|
|
675
677
|
return;
|
|
676
678
|
} else {
|
|
@@ -687,7 +689,7 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
687
689
|
}
|
|
688
690
|
let setRawEvents = async sql => {
|
|
689
691
|
try {
|
|
690
|
-
return await executeSet(sql, rawEvents, (sql, items) => setOrThrow(sql, items, InternalTable.RawEvents.table, InternalTable.RawEvents.schema, pgSchema, chainIdMode));
|
|
692
|
+
return await executeSet(sql, rawEvents, (sql, items) => setOrThrow(sql, items, InternalTable.RawEvents.table, InternalTable.RawEvents.schema, pgSchema, setQueryCache, chainIdMode));
|
|
691
693
|
} catch (raw_exn) {
|
|
692
694
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
693
695
|
return classifyWriteError(specificError, InternalTable.RawEvents.table, exn);
|
|
@@ -791,14 +793,14 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
791
793
|
}));
|
|
792
794
|
}
|
|
793
795
|
let entityHistory = getEntityHistory(entityConfig);
|
|
794
|
-
promises.push(setOrThrow(sql, batchSetUpdates, entityHistory.table, entityHistory.setChangeSchema, pgSchema, chainIdMode));
|
|
796
|
+
promises.push(setOrThrow(sql, batchSetUpdates, entityHistory.table, entityHistory.setChangeSchema, pgSchema, setQueryCache, chainIdMode));
|
|
795
797
|
}
|
|
796
798
|
}
|
|
797
799
|
if (Utils.$$Array.notEmpty(entitiesToSet)) {
|
|
798
800
|
if (shouldRemoveInvalidUtf8) {
|
|
799
801
|
removeInvalidUtf8InPlace(entitiesToSet);
|
|
800
802
|
}
|
|
801
|
-
promises.push(setOrThrow(sql, entitiesToSet, entityConfig.table, getRowSchema(entityConfig), pgSchema, chainIdMode));
|
|
803
|
+
promises.push(setOrThrow(sql, entitiesToSet, entityConfig.table, getRowSchema(entityConfig), pgSchema, setQueryCache, chainIdMode));
|
|
802
804
|
}
|
|
803
805
|
if (Utils.$$Array.notEmpty(idsToDelete)) {
|
|
804
806
|
promises.push(deleteByIdsOrThrow(sql, pgSchema, idsToDelete, entityConfig.table, Primitive_option.some(scopeChainId)));
|
|
@@ -870,7 +872,7 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
870
872
|
if (exn$1.RE_EXN_ID === PgEncodingError) {
|
|
871
873
|
let escapeTables$1 = escapeTables !== undefined ? Primitive_option.valFromOption(escapeTables) : new Set();
|
|
872
874
|
escapeTables$1.add(exn$1.table);
|
|
873
|
-
return await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, Primitive_option.some(escapeTables$1));
|
|
875
|
+
return await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, setQueryCache, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, Primitive_option.some(escapeTables$1));
|
|
874
876
|
}
|
|
875
877
|
throw exn$1;
|
|
876
878
|
}
|
|
@@ -954,6 +956,7 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
|
|
|
954
956
|
let cacheDirPath = Path.resolve(".envio", "cache");
|
|
955
957
|
let storageName = "postgres";
|
|
956
958
|
let indexManager = IndexManager.make();
|
|
959
|
+
let setQueryCache = new WeakMap();
|
|
957
960
|
let loadCatalogRows = (sql, indexName) => sql.unsafe(IndexCatalog.makeQuery(pgSchema, indexName)).then(rows => S$RescriptSchema.parseOrThrow(rows, IndexCatalog.rowsSchema));
|
|
958
961
|
let refreshIndexCatalog = async () => {
|
|
959
962
|
let rows = await loadCatalogRows(sql, undefined);
|
|
@@ -1325,7 +1328,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::` + addrCh
|
|
|
1325
1328
|
msg: `Committed ` + missing.length.toString() + ` schema indexes and the ready timestamp in ` + formatSeconds(timeRef) + `s.`
|
|
1326
1329
|
});
|
|
1327
1330
|
};
|
|
1328
|
-
let setOrThrow$1 = (items, table, itemSchema) => setOrThrow(sql, items, table, itemSchema, pgSchema,
|
|
1331
|
+
let setOrThrow$1 = (items, table, itemSchema) => setOrThrow(sql, items, table, itemSchema, pgSchema, setQueryCache, chainIdMode);
|
|
1329
1332
|
let setEffectCacheOrThrow = async (table, itemSchema, items, initialize) => {
|
|
1330
1333
|
if (initialize) {
|
|
1331
1334
|
await sql.unsafe(makeCreateTableQuery(table, pgSchema, false, undefined));
|
|
@@ -1461,7 +1464,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::` + addrCh
|
|
|
1461
1464
|
});
|
|
1462
1465
|
return [
|
|
1463
1466
|
removals,
|
|
1464
|
-
restoredEntitiesResult
|
|
1467
|
+
S$RescriptSchema.parseOrThrow(restoredEntitiesResult, Table.pgRowsSchema(entityConfig.table))
|
|
1465
1468
|
];
|
|
1466
1469
|
};
|
|
1467
1470
|
let writeBatchMethod = async (batch, rollback, isInReorgThreshold, config, allEntities, updatedEffectsCache, updatedEntities, chainMetaData, onWrite) => {
|
|
@@ -1487,7 +1490,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::` + addrCh
|
|
|
1487
1490
|
sinkPromise = undefined;
|
|
1488
1491
|
}
|
|
1489
1492
|
let primaryTimerRef = Performance.now();
|
|
1490
|
-
await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, pgUpdates, sinkPromise, chainMetaData, undefined);
|
|
1493
|
+
await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, setQueryCache, updatedEffectsCache, pgUpdates, sinkPromise, chainMetaData, undefined);
|
|
1491
1494
|
return onWrite(storageName, Performance.secondsSince(primaryTimerRef));
|
|
1492
1495
|
};
|
|
1493
1496
|
let close = () => sql.end();
|
|
@@ -1590,7 +1593,7 @@ export {
|
|
|
1590
1593
|
pgErrorMessageSchema,
|
|
1591
1594
|
PgEncodingError,
|
|
1592
1595
|
classifyWriteError,
|
|
1593
|
-
|
|
1596
|
+
makeSetQueryCache,
|
|
1594
1597
|
setOrThrow,
|
|
1595
1598
|
makeSchemaTableNamesQuery,
|
|
1596
1599
|
makeEffectCacheTableNamesQuery,
|
package/src/ReorgDetection.res
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
type blockDataWithTimestamp = {
|
|
2
|
-
blockHash: string,
|
|
3
|
-
blockNumber: int,
|
|
4
|
-
blockTimestamp: int,
|
|
5
|
-
}
|
|
6
|
-
|
|
7
1
|
type blockData = {
|
|
8
|
-
// Block hash is used for actual comparison to test for reorg
|
|
9
2
|
blockHash: string,
|
|
10
3
|
blockNumber: int,
|
|
11
4
|
}
|
|
12
5
|
|
|
13
|
-
external generalizeBlockDataWithTimestamp: blockDataWithTimestamp => blockData = "%identity"
|
|
14
|
-
|
|
15
6
|
type reorgDetected = {
|
|
16
7
|
scannedBlock: blockData,
|
|
17
8
|
receivedBlock: blockData,
|
|
@@ -30,216 +21,3 @@ let reorgDetectedToLogParams = (reorgDetected: reorgDetected, ~shouldRollbackOnR
|
|
|
30
21
|
}
|
|
31
22
|
|
|
32
23
|
type reorgResult = NoReorg | ReorgDetected(reorgDetected)
|
|
33
|
-
|
|
34
|
-
type t = {
|
|
35
|
-
// Whether to rollback on reorg
|
|
36
|
-
// Even if it's disabled, we still track reorgs checkpoints in memory
|
|
37
|
-
// and log when we detect an unhandled reorg
|
|
38
|
-
shouldRollbackOnReorg: bool,
|
|
39
|
-
// Number of blocks behind head, we want to keep track
|
|
40
|
-
// as a threshold for reorgs. If for eg. this is 200,
|
|
41
|
-
// it means we are accounting for reorgs up to 200 blocks
|
|
42
|
-
// behind the head
|
|
43
|
-
maxReorgDepth: int,
|
|
44
|
-
// A hash map of recent blockdata by block number to make comparison checks
|
|
45
|
-
// for reorgs.
|
|
46
|
-
dataByBlockNumber: dict<blockData>,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let make = (
|
|
50
|
-
~chainReorgCheckpoints: array<Internal.reorgCheckpoint>,
|
|
51
|
-
~maxReorgDepth,
|
|
52
|
-
~shouldRollbackOnReorg,
|
|
53
|
-
) => {
|
|
54
|
-
let dataByBlockNumber = Dict.make()
|
|
55
|
-
|
|
56
|
-
chainReorgCheckpoints->Array.forEach(block => {
|
|
57
|
-
dataByBlockNumber->Utils.Dict.setByInt(
|
|
58
|
-
block.blockNumber,
|
|
59
|
-
{
|
|
60
|
-
blockHash: block.blockHash,
|
|
61
|
-
blockNumber: block.blockNumber,
|
|
62
|
-
},
|
|
63
|
-
)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
shouldRollbackOnReorg,
|
|
68
|
-
maxReorgDepth,
|
|
69
|
-
dataByBlockNumber,
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let getDataByBlockNumberCopyInThreshold = ({dataByBlockNumber, maxReorgDepth}: t, ~knownHeight) => {
|
|
74
|
-
// Js engine automatically orders numeric object keys
|
|
75
|
-
let ascBlockNumberKeys = dataByBlockNumber->Dict.keysToArray
|
|
76
|
-
let thresholdBlockNumber = knownHeight - maxReorgDepth
|
|
77
|
-
|
|
78
|
-
let copy = Dict.make()
|
|
79
|
-
|
|
80
|
-
for idx in 0 to ascBlockNumberKeys->Array.length - 1 {
|
|
81
|
-
let blockNumberKey = ascBlockNumberKeys->Array.getUnsafe(idx)
|
|
82
|
-
let scannedBlock = dataByBlockNumber->Dict.getUnsafe(blockNumberKey)
|
|
83
|
-
let isInReorgThreshold = scannedBlock.blockNumber >= thresholdBlockNumber
|
|
84
|
-
if isInReorgThreshold {
|
|
85
|
-
copy->Dict.set(blockNumberKey, scannedBlock)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
copy
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Registers observed (blockNumber, blockHash) pairs from a range fetch, prunes
|
|
93
|
-
* unneeded data, and returns the updated state.
|
|
94
|
-
*
|
|
95
|
-
* Iterates the provided block hashes, skips entries outside the reorg threshold,
|
|
96
|
-
* and compares each one against the previously scanned data. Returns on the first
|
|
97
|
-
* mismatch as `ReorgDetected`.
|
|
98
|
-
*
|
|
99
|
-
* Resets internal state if shouldRollbackOnReorg is false (detect-only mode).
|
|
100
|
-
*/
|
|
101
|
-
let registerReorgGuard = (
|
|
102
|
-
{maxReorgDepth, shouldRollbackOnReorg} as self: t,
|
|
103
|
-
~blockHashes: array<blockData>,
|
|
104
|
-
~knownHeight,
|
|
105
|
-
) => {
|
|
106
|
-
let dataByBlockNumberCopyInThreshold = self->getDataByBlockNumberCopyInThreshold(~knownHeight)
|
|
107
|
-
let thresholdBlockNumber = knownHeight - maxReorgDepth
|
|
108
|
-
|
|
109
|
-
let maybeReorgDetected = ref(None)
|
|
110
|
-
let idx = ref(0)
|
|
111
|
-
while maybeReorgDetected.contents === None && idx.contents < blockHashes->Array.length {
|
|
112
|
-
let receivedBlock = blockHashes->Array.getUnsafe(idx.contents)
|
|
113
|
-
if receivedBlock.blockNumber >= thresholdBlockNumber {
|
|
114
|
-
let key = receivedBlock.blockNumber->Int.toString
|
|
115
|
-
// The working copy contains both previously scanned blocks AND blocks
|
|
116
|
-
// already written by earlier iterations of this same call, so a duplicate
|
|
117
|
-
// block number with a mismatching hash inside `blockHashes` itself is
|
|
118
|
-
// flagged as a reorg.
|
|
119
|
-
switch dataByBlockNumberCopyInThreshold->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
120
|
-
| Some(scannedBlock) if scannedBlock.blockHash !== receivedBlock.blockHash =>
|
|
121
|
-
maybeReorgDetected := Some({receivedBlock, scannedBlock})
|
|
122
|
-
| _ => dataByBlockNumberCopyInThreshold->Dict.set(key, receivedBlock)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
idx := idx.contents + 1
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
switch maybeReorgDetected.contents {
|
|
129
|
-
| Some(reorgDetected) => (
|
|
130
|
-
shouldRollbackOnReorg
|
|
131
|
-
? self
|
|
132
|
-
: make(~chainReorgCheckpoints=[], ~maxReorgDepth, ~shouldRollbackOnReorg),
|
|
133
|
-
ReorgDetected(reorgDetected),
|
|
134
|
-
)
|
|
135
|
-
| None => (
|
|
136
|
-
{
|
|
137
|
-
maxReorgDepth,
|
|
138
|
-
dataByBlockNumber: dataByBlockNumberCopyInThreshold,
|
|
139
|
-
shouldRollbackOnReorg,
|
|
140
|
-
},
|
|
141
|
-
NoReorg,
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
Returns the latest block number which matches block number and hashes in the provided array
|
|
148
|
-
If it doesn't exist in the reorg threshold it returns NotFound
|
|
149
|
-
*/
|
|
150
|
-
let getLatestValidScannedBlock = (
|
|
151
|
-
reorgDetection: t,
|
|
152
|
-
~blockNumbersAndHashes: array<blockDataWithTimestamp>,
|
|
153
|
-
) => {
|
|
154
|
-
let verifiedDataByBlockNumber = Dict.make()
|
|
155
|
-
for idx in 0 to blockNumbersAndHashes->Array.length - 1 {
|
|
156
|
-
let blockData = blockNumbersAndHashes->Array.getUnsafe(idx)
|
|
157
|
-
verifiedDataByBlockNumber->Dict.set(blockData.blockNumber->Int.toString, blockData)
|
|
158
|
-
}
|
|
159
|
-
// Js engine automatically orders numeric object keys
|
|
160
|
-
let ascBlockNumberKeys = verifiedDataByBlockNumber->Dict.keysToArray
|
|
161
|
-
|
|
162
|
-
let getPrevScannedBlockNumber = idx =>
|
|
163
|
-
ascBlockNumberKeys
|
|
164
|
-
->Array.get(idx - 1)
|
|
165
|
-
->Option.map(key => {
|
|
166
|
-
(verifiedDataByBlockNumber->Dict.getUnsafe(key)).blockNumber
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
let rec loop = idx => {
|
|
170
|
-
switch ascBlockNumberKeys->Array.get(idx) {
|
|
171
|
-
| Some(blockNumberKey) =>
|
|
172
|
-
switch reorgDetection.dataByBlockNumber->Utils.Dict.dangerouslyGetNonOption(blockNumberKey) {
|
|
173
|
-
| Some(scannedBlock)
|
|
174
|
-
if (verifiedDataByBlockNumber->Dict.getUnsafe(blockNumberKey)).blockHash ===
|
|
175
|
-
scannedBlock.blockHash =>
|
|
176
|
-
loop(idx + 1)
|
|
177
|
-
| _ => getPrevScannedBlockNumber(idx)
|
|
178
|
-
}
|
|
179
|
-
| None => getPrevScannedBlockNumber(idx)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
loop(0)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
Return a BlockNumbersAndHashes.t rolled back to where blockData is less
|
|
187
|
-
than the provided blockNumber
|
|
188
|
-
*/
|
|
189
|
-
let rollbackToValidBlockNumber = (
|
|
190
|
-
{dataByBlockNumber, maxReorgDepth, shouldRollbackOnReorg}: t,
|
|
191
|
-
~blockNumber: int,
|
|
192
|
-
) => {
|
|
193
|
-
// Js engine automatically orders numeric object keys
|
|
194
|
-
let ascBlockNumberKeys = dataByBlockNumber->Dict.keysToArray
|
|
195
|
-
|
|
196
|
-
let newDataByBlockNumber = Dict.make()
|
|
197
|
-
|
|
198
|
-
let rec loop = idx => {
|
|
199
|
-
switch ascBlockNumberKeys->Array.get(idx) {
|
|
200
|
-
| Some(blockNumberKey) => {
|
|
201
|
-
let scannedBlock = dataByBlockNumber->Dict.getUnsafe(blockNumberKey)
|
|
202
|
-
let shouldKeep = scannedBlock.blockNumber <= blockNumber
|
|
203
|
-
if shouldKeep {
|
|
204
|
-
newDataByBlockNumber->Dict.set(blockNumberKey, scannedBlock)
|
|
205
|
-
loop(idx + 1)
|
|
206
|
-
} else {
|
|
207
|
-
()
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
| None => ()
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
loop(0)
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
maxReorgDepth,
|
|
217
|
-
dataByBlockNumber: newDataByBlockNumber,
|
|
218
|
-
shouldRollbackOnReorg,
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let getThresholdBlockNumbersBelowBlock = (self: t, ~blockNumber: int, ~knownHeight) => {
|
|
223
|
-
let arr = []
|
|
224
|
-
|
|
225
|
-
// Js engine automatically orders numeric object keys
|
|
226
|
-
let ascBlockNumberKeys = self.dataByBlockNumber->Dict.keysToArray
|
|
227
|
-
let thresholdBlockNumber = knownHeight - self.maxReorgDepth
|
|
228
|
-
|
|
229
|
-
for idx in 0 to ascBlockNumberKeys->Array.length - 1 {
|
|
230
|
-
let blockNumberKey = ascBlockNumberKeys->Array.getUnsafe(idx)
|
|
231
|
-
let scannedBlock = self.dataByBlockNumber->Dict.getUnsafe(blockNumberKey)
|
|
232
|
-
let isInReorgThreshold = scannedBlock.blockNumber >= thresholdBlockNumber
|
|
233
|
-
if isInReorgThreshold && scannedBlock.blockNumber < blockNumber {
|
|
234
|
-
arr->Array.push(scannedBlock.blockNumber)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
arr
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let getHashByBlockNumber = (reorgDetection: t, ~blockNumber) => {
|
|
241
|
-
switch reorgDetection.dataByBlockNumber->Utils.Dict.dangerouslyGetByIntNonOption(blockNumber) {
|
|
242
|
-
| Some(v) => Null.Value(v.blockHash)
|
|
243
|
-
| None => Null.Null
|
|
244
|
-
}
|
|
245
|
-
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
3
|
|
|
5
4
|
function reorgDetectedToLogParams(reorgDetected, shouldRollbackOnReorg) {
|
|
6
5
|
let scannedBlock = reorgDetected.scannedBlock;
|
|
@@ -14,169 +13,7 @@ function reorgDetectedToLogParams(reorgDetected, shouldRollbackOnReorg) {
|
|
|
14
13
|
};
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
function make(chainReorgCheckpoints, maxReorgDepth, shouldRollbackOnReorg) {
|
|
18
|
-
let dataByBlockNumber = {};
|
|
19
|
-
chainReorgCheckpoints.forEach(block => {
|
|
20
|
-
dataByBlockNumber[block.block_number] = {
|
|
21
|
-
blockHash: block.block_hash,
|
|
22
|
-
blockNumber: block.block_number
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
return {
|
|
26
|
-
shouldRollbackOnReorg: shouldRollbackOnReorg,
|
|
27
|
-
maxReorgDepth: maxReorgDepth,
|
|
28
|
-
dataByBlockNumber: dataByBlockNumber
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getDataByBlockNumberCopyInThreshold(param, knownHeight) {
|
|
33
|
-
let dataByBlockNumber = param.dataByBlockNumber;
|
|
34
|
-
let ascBlockNumberKeys = Object.keys(dataByBlockNumber);
|
|
35
|
-
let thresholdBlockNumber = knownHeight - param.maxReorgDepth | 0;
|
|
36
|
-
let copy = {};
|
|
37
|
-
for (let idx = 0, idx_finish = ascBlockNumberKeys.length; idx < idx_finish; ++idx) {
|
|
38
|
-
let blockNumberKey = ascBlockNumberKeys[idx];
|
|
39
|
-
let scannedBlock = dataByBlockNumber[blockNumberKey];
|
|
40
|
-
let isInReorgThreshold = scannedBlock.blockNumber >= thresholdBlockNumber;
|
|
41
|
-
if (isInReorgThreshold) {
|
|
42
|
-
copy[blockNumberKey] = scannedBlock;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return copy;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function registerReorgGuard(self, blockHashes, knownHeight) {
|
|
49
|
-
let maxReorgDepth = self.maxReorgDepth;
|
|
50
|
-
let shouldRollbackOnReorg = self.shouldRollbackOnReorg;
|
|
51
|
-
let dataByBlockNumberCopyInThreshold = getDataByBlockNumberCopyInThreshold(self, knownHeight);
|
|
52
|
-
let thresholdBlockNumber = knownHeight - maxReorgDepth | 0;
|
|
53
|
-
let maybeReorgDetected;
|
|
54
|
-
let idx = 0;
|
|
55
|
-
while (maybeReorgDetected === undefined && idx < blockHashes.length) {
|
|
56
|
-
let receivedBlock = blockHashes[idx];
|
|
57
|
-
if (receivedBlock.blockNumber >= thresholdBlockNumber) {
|
|
58
|
-
let key = receivedBlock.blockNumber.toString();
|
|
59
|
-
let scannedBlock = dataByBlockNumberCopyInThreshold[key];
|
|
60
|
-
if (scannedBlock !== undefined && scannedBlock.blockHash !== receivedBlock.blockHash) {
|
|
61
|
-
maybeReorgDetected = {
|
|
62
|
-
scannedBlock: scannedBlock,
|
|
63
|
-
receivedBlock: receivedBlock
|
|
64
|
-
};
|
|
65
|
-
} else {
|
|
66
|
-
dataByBlockNumberCopyInThreshold[key] = receivedBlock;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
idx = idx + 1 | 0;
|
|
70
|
-
};
|
|
71
|
-
let reorgDetected = maybeReorgDetected;
|
|
72
|
-
if (reorgDetected !== undefined) {
|
|
73
|
-
return [
|
|
74
|
-
shouldRollbackOnReorg ? self : make([], maxReorgDepth, shouldRollbackOnReorg),
|
|
75
|
-
{
|
|
76
|
-
TAG: "ReorgDetected",
|
|
77
|
-
_0: reorgDetected
|
|
78
|
-
}
|
|
79
|
-
];
|
|
80
|
-
} else {
|
|
81
|
-
return [
|
|
82
|
-
{
|
|
83
|
-
shouldRollbackOnReorg: shouldRollbackOnReorg,
|
|
84
|
-
maxReorgDepth: maxReorgDepth,
|
|
85
|
-
dataByBlockNumber: dataByBlockNumberCopyInThreshold
|
|
86
|
-
},
|
|
87
|
-
"NoReorg"
|
|
88
|
-
];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getLatestValidScannedBlock(reorgDetection, blockNumbersAndHashes) {
|
|
93
|
-
let verifiedDataByBlockNumber = {};
|
|
94
|
-
for (let idx = 0, idx_finish = blockNumbersAndHashes.length; idx < idx_finish; ++idx) {
|
|
95
|
-
let blockData = blockNumbersAndHashes[idx];
|
|
96
|
-
verifiedDataByBlockNumber[blockData.blockNumber.toString()] = blockData;
|
|
97
|
-
}
|
|
98
|
-
let ascBlockNumberKeys = Object.keys(verifiedDataByBlockNumber);
|
|
99
|
-
let getPrevScannedBlockNumber = idx => Stdlib_Option.map(ascBlockNumberKeys[idx - 1 | 0], key => verifiedDataByBlockNumber[key].blockNumber);
|
|
100
|
-
let _idx = 0;
|
|
101
|
-
while (true) {
|
|
102
|
-
let idx$1 = _idx;
|
|
103
|
-
let blockNumberKey = ascBlockNumberKeys[idx$1];
|
|
104
|
-
if (blockNumberKey === undefined) {
|
|
105
|
-
return getPrevScannedBlockNumber(idx$1);
|
|
106
|
-
}
|
|
107
|
-
let scannedBlock = reorgDetection.dataByBlockNumber[blockNumberKey];
|
|
108
|
-
if (scannedBlock === undefined) {
|
|
109
|
-
return getPrevScannedBlockNumber(idx$1);
|
|
110
|
-
}
|
|
111
|
-
if (verifiedDataByBlockNumber[blockNumberKey].blockHash !== scannedBlock.blockHash) {
|
|
112
|
-
return getPrevScannedBlockNumber(idx$1);
|
|
113
|
-
}
|
|
114
|
-
_idx = idx$1 + 1 | 0;
|
|
115
|
-
continue;
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function rollbackToValidBlockNumber(param, blockNumber) {
|
|
120
|
-
let dataByBlockNumber = param.dataByBlockNumber;
|
|
121
|
-
let ascBlockNumberKeys = Object.keys(dataByBlockNumber);
|
|
122
|
-
let newDataByBlockNumber = {};
|
|
123
|
-
let loop = _idx => {
|
|
124
|
-
while (true) {
|
|
125
|
-
let idx = _idx;
|
|
126
|
-
let blockNumberKey = ascBlockNumberKeys[idx];
|
|
127
|
-
if (blockNumberKey === undefined) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
let scannedBlock = dataByBlockNumber[blockNumberKey];
|
|
131
|
-
let shouldKeep = scannedBlock.blockNumber <= blockNumber;
|
|
132
|
-
if (!shouldKeep) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
newDataByBlockNumber[blockNumberKey] = scannedBlock;
|
|
136
|
-
_idx = idx + 1 | 0;
|
|
137
|
-
continue;
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
loop(0);
|
|
141
|
-
return {
|
|
142
|
-
shouldRollbackOnReorg: param.shouldRollbackOnReorg,
|
|
143
|
-
maxReorgDepth: param.maxReorgDepth,
|
|
144
|
-
dataByBlockNumber: newDataByBlockNumber
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function getThresholdBlockNumbersBelowBlock(self, blockNumber, knownHeight) {
|
|
149
|
-
let arr = [];
|
|
150
|
-
let ascBlockNumberKeys = Object.keys(self.dataByBlockNumber);
|
|
151
|
-
let thresholdBlockNumber = knownHeight - self.maxReorgDepth | 0;
|
|
152
|
-
for (let idx = 0, idx_finish = ascBlockNumberKeys.length; idx < idx_finish; ++idx) {
|
|
153
|
-
let blockNumberKey = ascBlockNumberKeys[idx];
|
|
154
|
-
let scannedBlock = self.dataByBlockNumber[blockNumberKey];
|
|
155
|
-
let isInReorgThreshold = scannedBlock.blockNumber >= thresholdBlockNumber;
|
|
156
|
-
if (isInReorgThreshold && scannedBlock.blockNumber < blockNumber) {
|
|
157
|
-
arr.push(scannedBlock.blockNumber);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return arr;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function getHashByBlockNumber(reorgDetection, blockNumber) {
|
|
164
|
-
let v = reorgDetection.dataByBlockNumber[blockNumber];
|
|
165
|
-
if (v !== undefined) {
|
|
166
|
-
return v.blockHash;
|
|
167
|
-
} else {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
16
|
export {
|
|
173
17
|
reorgDetectedToLogParams,
|
|
174
|
-
make,
|
|
175
|
-
getDataByBlockNumberCopyInThreshold,
|
|
176
|
-
registerReorgGuard,
|
|
177
|
-
getLatestValidScannedBlock,
|
|
178
|
-
rollbackToValidBlockNumber,
|
|
179
|
-
getThresholdBlockNumbersBelowBlock,
|
|
180
|
-
getHashByBlockNumber,
|
|
181
18
|
}
|
|
182
19
|
/* No side effect */
|
package/src/Rollback.res
CHANGED
|
@@ -10,27 +10,29 @@ let getLastKnownValidBlock = async (
|
|
|
10
10
|
~reorgBlockNumber: int,
|
|
11
11
|
~isRealtime: bool,
|
|
12
12
|
) => {
|
|
13
|
-
//
|
|
13
|
+
// Before the search, not after: it re-fetches the scanned hashes through the
|
|
14
|
+
// sources, and a source answering from a cache it filled on the orphaned
|
|
15
|
+
// chain would confirm blocks that no longer exist - stopping the rollback
|
|
16
|
+
// short of the real fork.
|
|
17
|
+
chainState->ChainState.sourceManager->SourceManager.onReorg
|
|
18
|
+
|
|
19
|
+
// Don't include the reorg block itself - different source instances
|
|
14
20
|
// may have mismatching hashes at the head, so we always rollback
|
|
15
21
|
// the block where we detected the reorg.
|
|
16
22
|
let scannedBlockNumbers =
|
|
17
|
-
chainState
|
|
18
|
-
->ChainState.reorgDetection
|
|
19
|
-
->ReorgDetection.getThresholdBlockNumbersBelowBlock(
|
|
20
|
-
~blockNumber=reorgBlockNumber,
|
|
21
|
-
~knownHeight=chainState->ChainState.knownHeight,
|
|
22
|
-
)
|
|
23
|
+
chainState->ChainState.getReorgThresholdBlockNumbersBelow(~blockNumber=reorgBlockNumber)
|
|
23
24
|
|
|
24
25
|
switch scannedBlockNumbers {
|
|
25
26
|
| [] => chainState->ChainState.getHighestBlockBelowThreshold
|
|
26
27
|
| _ => {
|
|
27
|
-
let
|
|
28
|
+
let blockStore = await chainState
|
|
28
29
|
->ChainState.sourceManager
|
|
29
30
|
->SourceManager.getBlockHashes(~blockNumbers=scannedBlockNumbers, ~isRealtime)
|
|
30
31
|
|
|
31
|
-
switch chainState
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
switch chainState->ChainState.getLatestValidScannedBlock(
|
|
33
|
+
~blockStore,
|
|
34
|
+
~blockNumbers=scannedBlockNumbers,
|
|
35
|
+
) {
|
|
34
36
|
| Some(blockNumber) => blockNumber
|
|
35
37
|
| None => chainState->ChainState.getHighestBlockBelowThreshold
|
|
36
38
|
}
|
|
@@ -54,15 +56,12 @@ let rec rollback = async (
|
|
|
54
56
|
let chainState = state->IndexerState.getChainState(~chainId)
|
|
55
57
|
|
|
56
58
|
state->IndexerState.enterFindingReorgDepth
|
|
59
|
+
|
|
57
60
|
let rollbackTargetBlockNumber = await chainState->getLastKnownValidBlock(
|
|
58
61
|
~reorgBlockNumber,
|
|
59
62
|
~isRealtime=state->IndexerState.isRealtime,
|
|
60
63
|
)
|
|
61
64
|
|
|
62
|
-
chainState
|
|
63
|
-
->ChainState.sourceManager
|
|
64
|
-
->SourceManager.onReorg(~rollbackTargetBlock=rollbackTargetBlockNumber)
|
|
65
|
-
|
|
66
65
|
state->IndexerState.foundReorgDepth(~chainId, ~rollbackTargetBlockNumber)
|
|
67
66
|
// Rendezvous with the processing loop: whichever of {depth found, loop
|
|
68
67
|
// idle} happens last triggers the rollback; the earlier one finds the
|