envio 3.3.0-alpha.2 → 3.3.0-alpha.4
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 +23 -21
- package/package.json +6 -6
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +26 -3
- package/src/ChainFetching.res.mjs +22 -3
- package/src/ChainState.res +3 -12
- package/src/ChainState.res.mjs +6 -8
- package/src/ChainState.resi +0 -2
- package/src/CrossChainState.res +15 -0
- package/src/CrossChainState.res.mjs +12 -0
- package/src/Ecosystem.res +0 -6
- package/src/EventConfigBuilder.res +45 -18
- package/src/EventConfigBuilder.res.mjs +28 -10
- package/src/EventProcessing.res +53 -37
- package/src/EventProcessing.res.mjs +41 -36
- package/src/FetchState.res +35 -42
- package/src/FetchState.res.mjs +37 -63
- package/src/HandlerLoader.res +10 -1
- package/src/HandlerLoader.res.mjs +15 -8
- package/src/Internal.res +8 -3
- package/src/Internal.res.mjs +1 -1
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/Main.res +17 -9
- package/src/Main.res.mjs +15 -6
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -10
- package/src/Prometheus.res.mjs +9 -9
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/Rollback.res +2 -2
- package/src/Rollback.res.mjs +3 -3
- package/src/SimulateItems.res +81 -15
- package/src/SimulateItems.res.mjs +63 -20
- package/src/TestIndexer.res +77 -39
- package/src/TestIndexer.res.mjs +45 -31
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/sources/EventRouter.res +17 -21
- package/src/sources/EventRouter.res.mjs +6 -9
- package/src/sources/Evm.res +10 -37
- package/src/sources/Evm.res.mjs +5 -40
- package/src/sources/Fuel.res +0 -4
- package/src/sources/Fuel.res.mjs +0 -7
- package/src/sources/HyperFuelSource.res +10 -11
- package/src/sources/HyperFuelSource.res.mjs +11 -11
- package/src/sources/HyperSync.res +3 -0
- package/src/sources/HyperSync.res.mjs +1 -0
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +5 -1
- package/src/sources/HyperSyncSource.res +42 -24
- package/src/sources/HyperSyncSource.res.mjs +30 -26
- package/src/sources/RpcSource.res +7 -8
- package/src/sources/RpcSource.res.mjs +8 -8
- package/src/sources/SimulateSource.res +1 -1
- package/src/sources/Source.res +1 -1
- package/src/sources/SourceManager.res +17 -16
- package/src/sources/SourceManager.res.mjs +10 -15
- package/src/sources/Svm.res +6 -11
- package/src/sources/Svm.res.mjs +6 -14
- package/src/sources/SvmHyperSyncSource.res +24 -18
- package/src/sources/SvmHyperSyncSource.res.mjs +22 -15
- package/src/sources/TransactionStore.res +62 -35
- package/src/sources/TransactionStore.res.mjs +44 -18
- package/svm.schema.json +30 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- package/src/bindings/Hrtime.resi +0 -30
package/src/Prometheus.res
CHANGED
|
@@ -212,7 +212,7 @@ module PreloadHandler = {
|
|
|
212
212
|
|
|
213
213
|
type operationRef = {
|
|
214
214
|
mutable pendingCount: int,
|
|
215
|
-
timerRef:
|
|
215
|
+
timerRef: Performance.timeRef,
|
|
216
216
|
}
|
|
217
217
|
let operations: dict<operationRef> = Dict.make()
|
|
218
218
|
|
|
@@ -227,11 +227,11 @@ module PreloadHandler = {
|
|
|
227
227
|
key,
|
|
228
228
|
{
|
|
229
229
|
pendingCount: 1,
|
|
230
|
-
timerRef:
|
|
230
|
+
timerRef: Performance.now(),
|
|
231
231
|
},
|
|
232
232
|
)
|
|
233
233
|
}
|
|
234
|
-
|
|
234
|
+
Performance.now()
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
let endOperation = (timerRef, ~contract, ~event) => {
|
|
@@ -242,13 +242,13 @@ module PreloadHandler = {
|
|
|
242
242
|
if operationRef.pendingCount === 0 {
|
|
243
243
|
timeCounter->SafeCounter.handleFloat(
|
|
244
244
|
~labels,
|
|
245
|
-
~value=operationRef.timerRef->
|
|
245
|
+
~value=operationRef.timerRef->Performance.secondsSince,
|
|
246
246
|
)
|
|
247
247
|
operations->Utils.Dict.deleteInPlace(key)
|
|
248
248
|
}
|
|
249
249
|
sumTimeCounter->SafeCounter.handleFloat(
|
|
250
250
|
~labels,
|
|
251
|
-
~value=timerRef->
|
|
251
|
+
~value=timerRef->Performance.secondsSince,
|
|
252
252
|
)
|
|
253
253
|
count->SafeCounter.increment(~labels)
|
|
254
254
|
}
|
|
@@ -764,7 +764,7 @@ module StorageLoad = {
|
|
|
764
764
|
|
|
765
765
|
type operationRef = {
|
|
766
766
|
mutable pendingCount: int,
|
|
767
|
-
timerRef:
|
|
767
|
+
timerRef: Performance.timeRef,
|
|
768
768
|
}
|
|
769
769
|
let operations = Dict.make()
|
|
770
770
|
|
|
@@ -780,12 +780,12 @@ module StorageLoad = {
|
|
|
780
780
|
(
|
|
781
781
|
{
|
|
782
782
|
pendingCount: 1,
|
|
783
|
-
timerRef:
|
|
783
|
+
timerRef: Performance.now(),
|
|
784
784
|
}: operationRef
|
|
785
785
|
),
|
|
786
786
|
)
|
|
787
787
|
}
|
|
788
|
-
|
|
788
|
+
Performance.now()
|
|
789
789
|
}
|
|
790
790
|
|
|
791
791
|
let endOperation = (timerRef, ~storage, ~operation, ~whereSize, ~size) => {
|
|
@@ -796,13 +796,13 @@ module StorageLoad = {
|
|
|
796
796
|
if operationRef.pendingCount === 0 {
|
|
797
797
|
timeCounter->SafeCounter.handleFloat(
|
|
798
798
|
~labels,
|
|
799
|
-
~value=operationRef.timerRef->
|
|
799
|
+
~value=operationRef.timerRef->Performance.secondsSince,
|
|
800
800
|
)
|
|
801
801
|
operations->Utils.Dict.deleteInPlace(key)
|
|
802
802
|
}
|
|
803
803
|
sumTimeCounter->SafeCounter.handleFloat(
|
|
804
804
|
~labels,
|
|
805
|
-
~value=timerRef->
|
|
805
|
+
~value=timerRef->Performance.secondsSince,
|
|
806
806
|
)
|
|
807
807
|
counter->SafeCounter.increment(~labels)
|
|
808
808
|
whereSizeCounter->SafeCounter.handleInt(~labels, ~value=whereSize)
|
package/src/Prometheus.res.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
5
4
|
import * as PromClient from "./bindings/PromClient.res.mjs";
|
|
5
|
+
import * as Performance from "./bindings/Performance.res.mjs";
|
|
6
6
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
7
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
8
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
@@ -290,10 +290,10 @@ function startOperation(contract, event) {
|
|
|
290
290
|
} else {
|
|
291
291
|
operations[key] = {
|
|
292
292
|
pendingCount: 1,
|
|
293
|
-
timerRef:
|
|
293
|
+
timerRef: Performance.now()
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
|
-
return
|
|
296
|
+
return Performance.now();
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
function endOperation(timerRef, contract, event) {
|
|
@@ -305,10 +305,10 @@ function endOperation(timerRef, contract, event) {
|
|
|
305
305
|
let operationRef = operations[key];
|
|
306
306
|
operationRef.pendingCount = operationRef.pendingCount - 1 | 0;
|
|
307
307
|
if (operationRef.pendingCount === 0) {
|
|
308
|
-
handleFloat(timeCounter$1, labels,
|
|
308
|
+
handleFloat(timeCounter$1, labels, Performance.secondsSince(operationRef.timerRef));
|
|
309
309
|
Utils.Dict.deleteInPlace(operations, key);
|
|
310
310
|
}
|
|
311
|
-
handleFloat(sumTimeCounter, labels,
|
|
311
|
+
handleFloat(sumTimeCounter, labels, Performance.secondsSince(timerRef));
|
|
312
312
|
increment(count$1, labels);
|
|
313
313
|
}
|
|
314
314
|
|
|
@@ -775,10 +775,10 @@ function startOperation$1(storage, operation) {
|
|
|
775
775
|
} else {
|
|
776
776
|
operations$1[key] = {
|
|
777
777
|
pendingCount: 1,
|
|
778
|
-
timerRef:
|
|
778
|
+
timerRef: Performance.now()
|
|
779
779
|
};
|
|
780
780
|
}
|
|
781
|
-
return
|
|
781
|
+
return Performance.now();
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
function endOperation$1(timerRef, storage, operation, whereSize, size) {
|
|
@@ -790,10 +790,10 @@ function endOperation$1(timerRef, storage, operation, whereSize, size) {
|
|
|
790
790
|
let operationRef = operations$1[key];
|
|
791
791
|
operationRef.pendingCount = operationRef.pendingCount - 1 | 0;
|
|
792
792
|
if (operationRef.pendingCount === 0) {
|
|
793
|
-
handleFloat(timeCounter$7, labels,
|
|
793
|
+
handleFloat(timeCounter$7, labels, Performance.secondsSince(operationRef.timerRef));
|
|
794
794
|
Utils.Dict.deleteInPlace(operations$1, key);
|
|
795
795
|
}
|
|
796
|
-
handleFloat(sumTimeCounter$3, labels,
|
|
796
|
+
handleFloat(sumTimeCounter$3, labels, Performance.secondsSince(timerRef));
|
|
797
797
|
increment(counter$8, labels);
|
|
798
798
|
handleInt(whereSizeCounter, labels, whereSize);
|
|
799
799
|
handleInt(sizeCounter$1, labels, size);
|
|
@@ -14,7 +14,7 @@ let runPrune = async (state: IndexerState.t) => {
|
|
|
14
14
|
await Utils.delay(1000)
|
|
15
15
|
}
|
|
16
16
|
let entityConfig = persistence.allEntities->Array.getUnsafe(idx)
|
|
17
|
-
let timeRef =
|
|
17
|
+
let timeRef = Performance.now()
|
|
18
18
|
try {
|
|
19
19
|
let () = await persistence.storage.pruneStaleEntityHistory(
|
|
20
20
|
~entityName=entityConfig.name,
|
|
@@ -34,7 +34,7 @@ let runPrune = async (state: IndexerState.t) => {
|
|
|
34
34
|
)
|
|
35
35
|
}
|
|
36
36
|
Prometheus.RollbackHistoryPrune.increment(
|
|
37
|
-
~timeSeconds=
|
|
37
|
+
~timeSeconds=Performance.secondsSince(timeRef),
|
|
38
38
|
~entityName=entityConfig.name,
|
|
39
39
|
)
|
|
40
40
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
5
4
|
import * as Logging from "./Logging.res.mjs";
|
|
6
5
|
import * as Throttler from "./Throttler.res.mjs";
|
|
7
6
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
7
|
+
import * as Performance from "./bindings/Performance.res.mjs";
|
|
8
8
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
9
9
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
10
10
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
@@ -21,7 +21,7 @@ async function runPrune(state) {
|
|
|
21
21
|
await Utils.delay(1000);
|
|
22
22
|
}
|
|
23
23
|
let entityConfig = persistence.allEntities[idx];
|
|
24
|
-
let timeRef =
|
|
24
|
+
let timeRef = Performance.now();
|
|
25
25
|
try {
|
|
26
26
|
await persistence.storage.pruneStaleEntityHistory(entityConfig.name, entityConfig.index, safeCheckpointId);
|
|
27
27
|
} catch (raw_exn) {
|
|
@@ -31,7 +31,7 @@ async function runPrune(state) {
|
|
|
31
31
|
safeCheckpointId: safeCheckpointId
|
|
32
32
|
}), `Failed to prune stale entity history`, exn);
|
|
33
33
|
}
|
|
34
|
-
Prometheus.RollbackHistoryPrune.increment(
|
|
34
|
+
Prometheus.RollbackHistoryPrune.increment(Performance.secondsSince(timeRef), entityConfig.name);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
package/src/Rollback.res
CHANGED
|
@@ -94,7 +94,7 @@ and executeRollback = async (
|
|
|
94
94
|
~scheduleFetch,
|
|
95
95
|
~scheduleProcessing,
|
|
96
96
|
) => {
|
|
97
|
-
let startTime =
|
|
97
|
+
let startTime = Performance.now()
|
|
98
98
|
|
|
99
99
|
let chainState = state->IndexerState.getChainState(~chain=reorgChain)
|
|
100
100
|
|
|
@@ -194,7 +194,7 @@ and executeRollback = async (
|
|
|
194
194
|
"targetCheckpointId": rollbackTargetCheckpointId,
|
|
195
195
|
})
|
|
196
196
|
Prometheus.RollbackSuccess.increment(
|
|
197
|
-
~timeSeconds=
|
|
197
|
+
~timeSeconds=Performance.secondsSince(startTime),
|
|
198
198
|
~rollbackedProcessedEvents=rollbackedProcessedEvents.contents,
|
|
199
199
|
)
|
|
200
200
|
|
package/src/Rollback.res.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
5
4
|
import * as Logging from "./Logging.res.mjs";
|
|
6
5
|
import * as Writing from "./Writing.res.mjs";
|
|
7
6
|
import * as ChainState from "./ChainState.res.mjs";
|
|
8
7
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
8
|
+
import * as Performance from "./bindings/Performance.res.mjs";
|
|
9
9
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
10
10
|
import * as Stdlib_Float from "@rescript/runtime/lib/es6/Stdlib_Float.js";
|
|
11
11
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
@@ -32,7 +32,7 @@ async function getLastKnownValidBlock(chainState, reorgBlockNumber, isRealtime)
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing) {
|
|
35
|
-
let startTime =
|
|
35
|
+
let startTime = Performance.now();
|
|
36
36
|
let chainState = IndexerState.getChainState(state, reorgChain);
|
|
37
37
|
let logger = Logging.createChildFrom(ChainState.logger(chainState), {
|
|
38
38
|
action: "Rollback",
|
|
@@ -70,7 +70,7 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
|
|
|
70
70
|
beforeCheckpointId: IndexerState.committedCheckpointId(state),
|
|
71
71
|
targetCheckpointId: rollbackTargetCheckpointId
|
|
72
72
|
});
|
|
73
|
-
Prometheus.RollbackSuccess.increment(
|
|
73
|
+
Prometheus.RollbackSuccess.increment(Performance.secondsSince(startTime), rollbackedProcessedEvents);
|
|
74
74
|
IndexerState.completeRollback(state, eventsProcessedDiffByChain);
|
|
75
75
|
scheduleFetch();
|
|
76
76
|
return scheduleProcessing();
|
package/src/SimulateItems.res
CHANGED
|
@@ -195,6 +195,82 @@ let findEventConfig = (~config: Config.t, ~contractName: string, ~eventName: str
|
|
|
195
195
|
found.contents
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
let dummySrcAddress = Address.unsafeFromString("0x0000000000000000000000000000000000000000")
|
|
199
|
+
|
|
200
|
+
// First address configured for `contractName` on the simulated chain. Only the
|
|
201
|
+
// simulated chain is consulted — a contract's address on another chain has no
|
|
202
|
+
// meaning here.
|
|
203
|
+
let firstContractAddress = (~chainConfig: Config.chain, ~contractName: string): option<
|
|
204
|
+
Address.t,
|
|
205
|
+
> => {
|
|
206
|
+
let found = ref(None)
|
|
207
|
+
chainConfig.contracts->Array.forEach(contract => {
|
|
208
|
+
if found.contents->Option.isNone && contract.name === contractName {
|
|
209
|
+
found := contract.addresses->Array.get(0)
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
found.contents
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let deriveSrcAddress = (
|
|
216
|
+
~providedSrcAddress: option<Address.t>,
|
|
217
|
+
~eventConfig: Internal.eventConfig,
|
|
218
|
+
~chainConfig: Config.chain,
|
|
219
|
+
): Address.t => {
|
|
220
|
+
switch providedSrcAddress {
|
|
221
|
+
| Some(addr) => addr
|
|
222
|
+
| None =>
|
|
223
|
+
if eventConfig.isWildcard {
|
|
224
|
+
dummySrcAddress
|
|
225
|
+
} else {
|
|
226
|
+
switch firstContractAddress(~chainConfig, ~contractName=eventConfig.contractName) {
|
|
227
|
+
| Some(addr) => addr
|
|
228
|
+
| None => dummySrcAddress
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// A non-wildcard event is gated by `clientAddressFilter` on srcAddress ownership,
|
|
235
|
+
// so a simulate item whose srcAddress isn't indexed on this chain would be
|
|
236
|
+
// silently dropped (handler never runs). Fail loudly instead.
|
|
237
|
+
//
|
|
238
|
+
// `config` must already have handler registrations applied, otherwise an event
|
|
239
|
+
// made wildcard purely through `indexer.onEvent({ wildcard: true })` reads back
|
|
240
|
+
// as non-wildcard and gets wrongly rejected.
|
|
241
|
+
let validateSrcAddresses = (
|
|
242
|
+
~simulateItems: array<JSON.t>,
|
|
243
|
+
~config: Config.t,
|
|
244
|
+
~chainConfig: Config.chain,
|
|
245
|
+
~indexingAddresses: array<Internal.indexingAddress>,
|
|
246
|
+
): unit => {
|
|
247
|
+
let known = Utils.Set.make()
|
|
248
|
+
indexingAddresses->Array.forEach(ia => known->Utils.Set.add(ia.address->Address.toString)->ignore)
|
|
249
|
+
simulateItems->Array.forEach(rawJson => {
|
|
250
|
+
let raw = rawJson->(Utils.magic: JSON.t => rawSimulateItem)
|
|
251
|
+
switch (raw->getContract, raw->getEvent) {
|
|
252
|
+
| (Some(contractName), Some(eventName)) =>
|
|
253
|
+
switch findEventConfig(~config, ~contractName, ~eventName) {
|
|
254
|
+
| Some(eventConfig) if !eventConfig.isWildcard =>
|
|
255
|
+
let item = rawJson->(Utils.magic: JSON.t => Envio.evmSimulateItem)
|
|
256
|
+
let srcAddress = deriveSrcAddress(
|
|
257
|
+
~providedSrcAddress=item.srcAddress,
|
|
258
|
+
~eventConfig,
|
|
259
|
+
~chainConfig,
|
|
260
|
+
)
|
|
261
|
+
if !(known->Utils.Set.has(srcAddress->Address.toString)) {
|
|
262
|
+
JsError.throwWithMessage(
|
|
263
|
+
`simulate: ${contractName}.${eventName} resolved to address ${srcAddress->Address.toString}, which isn't indexed on chain ${chainConfig.id->Int.toString}. ` ++
|
|
264
|
+
`Provide a "srcAddress" configured or registered for ${contractName} on this chain, or use a wildcard event.`,
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
| _ => ()
|
|
268
|
+
}
|
|
269
|
+
| _ => ()
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
198
274
|
let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Config.chain): array<
|
|
199
275
|
Internal.item,
|
|
200
276
|
> => {
|
|
@@ -238,21 +314,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
238
314
|
li
|
|
239
315
|
}
|
|
240
316
|
|
|
241
|
-
let srcAddress =
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
chainConfig.contracts->Array.forEach(contract => {
|
|
247
|
-
if contract.name === contractName {
|
|
248
|
-
switch contract.addresses->Array.get(0) {
|
|
249
|
-
| Some(a) => addr := a
|
|
250
|
-
| None => ()
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
})
|
|
254
|
-
addr.contents
|
|
255
|
-
}
|
|
317
|
+
let srcAddress = deriveSrcAddress(
|
|
318
|
+
~providedSrcAddress=item.srcAddress,
|
|
319
|
+
~eventConfig,
|
|
320
|
+
~chainConfig,
|
|
321
|
+
)
|
|
256
322
|
|
|
257
323
|
let rawItem = rawJson->(Utils.magic: JSON.t => {..})
|
|
258
324
|
let blockJson: option<JSON.t> =
|
|
@@ -135,6 +135,64 @@ function findEventConfig(config, contractName, eventName) {
|
|
|
135
135
|
return found.contents;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
let dummySrcAddress = "0x0000000000000000000000000000000000000000";
|
|
139
|
+
|
|
140
|
+
function firstContractAddress(chainConfig, contractName) {
|
|
141
|
+
let found = {
|
|
142
|
+
contents: undefined
|
|
143
|
+
};
|
|
144
|
+
chainConfig.contracts.forEach(contract => {
|
|
145
|
+
if (Stdlib_Option.isNone(found.contents) && contract.name === contractName) {
|
|
146
|
+
found.contents = contract.addresses[0];
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
return found.contents;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function deriveSrcAddress(providedSrcAddress, eventConfig, chainConfig) {
|
|
154
|
+
if (providedSrcAddress !== undefined) {
|
|
155
|
+
return Primitive_option.valFromOption(providedSrcAddress);
|
|
156
|
+
}
|
|
157
|
+
if (eventConfig.isWildcard) {
|
|
158
|
+
return dummySrcAddress;
|
|
159
|
+
}
|
|
160
|
+
let addr = firstContractAddress(chainConfig, eventConfig.contractName);
|
|
161
|
+
if (addr !== undefined) {
|
|
162
|
+
return Primitive_option.valFromOption(addr);
|
|
163
|
+
} else {
|
|
164
|
+
return dummySrcAddress;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function validateSrcAddresses(simulateItems, config, chainConfig, indexingAddresses) {
|
|
169
|
+
let known = new Set();
|
|
170
|
+
indexingAddresses.forEach(ia => {
|
|
171
|
+
known.add(ia.address);
|
|
172
|
+
});
|
|
173
|
+
simulateItems.forEach(rawJson => {
|
|
174
|
+
let match = rawJson.contract;
|
|
175
|
+
let match$1 = rawJson.event;
|
|
176
|
+
if (match === undefined) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (match$1 === undefined) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
let eventConfig = findEventConfig(config, match, match$1);
|
|
183
|
+
if (eventConfig === undefined) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (eventConfig.isWildcard) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
let srcAddress = deriveSrcAddress(rawJson.srcAddress, eventConfig, chainConfig);
|
|
190
|
+
if (!known.has(srcAddress)) {
|
|
191
|
+
return Stdlib_JsError.throwWithMessage(`simulate: ` + match + `.` + match$1 + ` resolved to address ` + srcAddress + `, which isn't indexed on chain ` + chainConfig.id.toString() + `. ` + (`Provide a "srcAddress" configured or registered for ` + match + ` on this chain, or use a wildcard event.`));
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
138
196
|
function parse(simulateItems, config, chainConfig) {
|
|
139
197
|
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
140
198
|
let chainId = chainConfig.id;
|
|
@@ -169,26 +227,7 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
169
227
|
currentLogIndex.contents = li$1 + 1 | 0;
|
|
170
228
|
logIndex = li$1;
|
|
171
229
|
}
|
|
172
|
-
let
|
|
173
|
-
let srcAddress;
|
|
174
|
-
if (addr !== undefined) {
|
|
175
|
-
srcAddress = Primitive_option.valFromOption(addr);
|
|
176
|
-
} else {
|
|
177
|
-
let addr$1 = {
|
|
178
|
-
contents: "0x0000000000000000000000000000000000000000"
|
|
179
|
-
};
|
|
180
|
-
chainConfig.contracts.forEach(contract => {
|
|
181
|
-
if (contract.name !== match) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
let a = contract.addresses[0];
|
|
185
|
-
if (a !== undefined) {
|
|
186
|
-
addr$1.contents = Primitive_option.valFromOption(a);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
srcAddress = addr$1.contents;
|
|
191
|
-
}
|
|
230
|
+
let srcAddress = deriveSrcAddress(rawJson.srcAddress, eventConfig, chainConfig);
|
|
192
231
|
let blockJson = rawJson.block;
|
|
193
232
|
let blockJson$1 = (blockJson == null) ? undefined : Primitive_option.some(blockJson);
|
|
194
233
|
let transactionJson = rawJson.transaction;
|
|
@@ -318,6 +357,10 @@ export {
|
|
|
318
357
|
fuelSimulateTransactionSchema,
|
|
319
358
|
parseFuelSimulateTransaction,
|
|
320
359
|
findEventConfig,
|
|
360
|
+
dummySrcAddress,
|
|
361
|
+
firstContractAddress,
|
|
362
|
+
deriveSrcAddress,
|
|
363
|
+
validateSrcAddresses,
|
|
321
364
|
parse,
|
|
322
365
|
patchConfig,
|
|
323
366
|
}
|
package/src/TestIndexer.res
CHANGED
|
@@ -43,6 +43,33 @@ let toIndexingAddress = (dc: InternalTable.EnvioAddresses.t): Internal.indexingA
|
|
|
43
43
|
registrationBlock: dc.registrationBlock,
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// All indexing addresses (config-seeded + dynamically registered) grouped by
|
|
47
|
+
// chain id string, derived from the envio_addresses entities.
|
|
48
|
+
let getIndexingAddressesByChain = (state: testIndexerState): dict<
|
|
49
|
+
array<Internal.indexingAddress>,
|
|
50
|
+
> => {
|
|
51
|
+
let byChain = Dict.make()
|
|
52
|
+
switch state.entities->Dict.get(InternalTable.EnvioAddresses.name) {
|
|
53
|
+
| Some(dcDict) =>
|
|
54
|
+
dcDict
|
|
55
|
+
->Dict.valuesToArray
|
|
56
|
+
->Array.forEach(entity => {
|
|
57
|
+
let dc = entity->castToEnvioAddresses
|
|
58
|
+
let chainIdStr = dc.chainId->Int.toString
|
|
59
|
+
let contracts = switch byChain->Dict.get(chainIdStr) {
|
|
60
|
+
| Some(arr) => arr
|
|
61
|
+
| None =>
|
|
62
|
+
let arr = []
|
|
63
|
+
byChain->Dict.set(chainIdStr, arr)
|
|
64
|
+
arr
|
|
65
|
+
}
|
|
66
|
+
contracts->Array.push(dc->toIndexingAddress)->ignore
|
|
67
|
+
})
|
|
68
|
+
| None => ()
|
|
69
|
+
}
|
|
70
|
+
byChain
|
|
71
|
+
}
|
|
72
|
+
|
|
46
73
|
let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFilter.t): JSON.t => {
|
|
47
74
|
// Loads for non-entity tables (e.g. effect caches `envio_effect_<name>`) reach
|
|
48
75
|
// here too. TestIndexer never persists those, so there's nothing to return —
|
|
@@ -566,22 +593,10 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
566
593
|
`Cannot access ${contract.name}.addresses while indexer.process() is running. ` ++ "Wait for process() to complete before reading contract addresses.",
|
|
567
594
|
)
|
|
568
595
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
->Dict.valuesToArray
|
|
574
|
-
->Array.forEach(
|
|
575
|
-
entity => {
|
|
576
|
-
let dc = entity->castToEnvioAddresses
|
|
577
|
-
if dc.contractName === contract.name && dc.chainId === chainConfig.id {
|
|
578
|
-
addresses->Array.push(dc->Config.EnvioAddresses.getAddress)->ignore
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
)
|
|
582
|
-
| None => ()
|
|
583
|
-
}
|
|
584
|
-
addresses
|
|
596
|
+
getIndexingAddressesByChain(state)
|
|
597
|
+
->Dict.get(chainConfig.id->Int.toString)
|
|
598
|
+
->Option.getOr([])
|
|
599
|
+
->Array.filterMap(ia => ia.contractName === contract.name ? Some(ia.address) : None)
|
|
585
600
|
},
|
|
586
601
|
},
|
|
587
602
|
)
|
|
@@ -652,7 +667,11 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
652
667
|
Int.compare(aId, bId)
|
|
653
668
|
})
|
|
654
669
|
|
|
655
|
-
// Parse and validate
|
|
670
|
+
// Parse and validate the block ranges upfront before starting any
|
|
671
|
+
// workers. srcAddress validation can't run here: it depends on which
|
|
672
|
+
// events are wildcard, and `config` reflects config.yaml only —
|
|
673
|
+
// handler-level `wildcard: true` takes effect only once registrations
|
|
674
|
+
// are applied.
|
|
656
675
|
let chainEntries = sortedChainKeys->Array.map(chainIdStr => {
|
|
657
676
|
let rawChainConfig = rawChains->Dict.getUnsafe(chainIdStr)
|
|
658
677
|
let chainId = switch chainIdStr->Int.fromString {
|
|
@@ -684,26 +703,9 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
684
703
|
let chains: dict<chainConfig> = Dict.make()
|
|
685
704
|
chains->Dict.set(chainIdStr, processChainConfig)
|
|
686
705
|
|
|
687
|
-
//
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
| Some(dcDict) =>
|
|
691
|
-
dcDict
|
|
692
|
-
->Dict.valuesToArray
|
|
693
|
-
->Array.forEach(entity => {
|
|
694
|
-
let dc = entity->castToEnvioAddresses
|
|
695
|
-
let dcChainIdStr = dc.chainId->Int.toString
|
|
696
|
-
let contracts = switch indexingAddressesByChain->Dict.get(dcChainIdStr) {
|
|
697
|
-
| Some(arr) => arr
|
|
698
|
-
| None =>
|
|
699
|
-
let arr = []
|
|
700
|
-
indexingAddressesByChain->Dict.set(dcChainIdStr, arr)
|
|
701
|
-
arr
|
|
702
|
-
}
|
|
703
|
-
contracts->Array.push(dc->toIndexingAddress)->ignore
|
|
704
|
-
})
|
|
705
|
-
| None => ()
|
|
706
|
-
}
|
|
706
|
+
// Rebuilt per chain so workers see contracts registered by earlier
|
|
707
|
+
// chains in the same process() call.
|
|
708
|
+
let indexingAddressesByChain = getIndexingAddressesByChain(state)
|
|
707
709
|
|
|
708
710
|
let initialState = makeInitialState(
|
|
709
711
|
~config,
|
|
@@ -865,7 +867,27 @@ let initTestWorker = () => {
|
|
|
865
867
|
| Some(_) => ()
|
|
866
868
|
}
|
|
867
869
|
|
|
868
|
-
let patchConfig = (config, _registrations) => {
|
|
870
|
+
let patchConfig = (config: Config.t, _registrations) => {
|
|
871
|
+
// `config` here has handler registrations applied, so `isWildcard`
|
|
872
|
+
// reflects `indexer.onEvent({ wildcard: true })` — which the main thread
|
|
873
|
+
// can't see. Validate srcAddresses against it before patching in the
|
|
874
|
+
// simulate source.
|
|
875
|
+
switch simulate {
|
|
876
|
+
| Some(simulateItems) =>
|
|
877
|
+
let chainConfig = config.chainMap->ChainMap.get(ChainMap.Chain.makeUnsafe(~chainId))
|
|
878
|
+
let indexingAddresses =
|
|
879
|
+
initialState.chains
|
|
880
|
+
->Array.find(c => c.id == chainId)
|
|
881
|
+
->Option.mapOr([], c => c.indexingAddresses)
|
|
882
|
+
SimulateItems.validateSrcAddresses(
|
|
883
|
+
~simulateItems,
|
|
884
|
+
~config,
|
|
885
|
+
~chainConfig,
|
|
886
|
+
~indexingAddresses,
|
|
887
|
+
)
|
|
888
|
+
| None => ()
|
|
889
|
+
}
|
|
890
|
+
|
|
869
891
|
let config = SimulateItems.patchConfig(~config, ~processConfig)
|
|
870
892
|
|
|
871
893
|
// In auto-exit mode, set batchSize=1 to process one block checkpoint at a time
|
|
@@ -875,7 +897,23 @@ let initTestWorker = () => {
|
|
|
875
897
|
config
|
|
876
898
|
}
|
|
877
899
|
}
|
|
878
|
-
Main.start(~persistence, ~isTest=true, ~patchConfig, ~exitAfterFirstEventBlock)
|
|
900
|
+
Main.start(~persistence, ~isTest=true, ~patchConfig, ~exitAfterFirstEventBlock)
|
|
901
|
+
->Promise.catch(exn => {
|
|
902
|
+
// `Main.start` rejects on any fatal error: a runtime failure arrives wrapped
|
|
903
|
+
// in `Main.FatalError` (already logged), a setup throw (e.g. patchConfig's
|
|
904
|
+
// srcAddress validation) arrives raw. The parent only learns of failures
|
|
905
|
+
// through the worker `error` event, which fires on an *uncaught* exception.
|
|
906
|
+
// Throwing synchronously in this catch would just reject the catch's own
|
|
907
|
+
// promise (swallowed by `ignore`); `setImmediate` re-throws outside the
|
|
908
|
+
// promise chain so it becomes uncaught and reaches the parent.
|
|
909
|
+
let toThrow = switch exn {
|
|
910
|
+
| Main.FatalError(inner) => inner
|
|
911
|
+
| _ => exn->Utils.prettifyExn
|
|
912
|
+
}
|
|
913
|
+
NodeJs.setImmediate(() => throw(toThrow))
|
|
914
|
+
Promise.resolve()
|
|
915
|
+
})
|
|
916
|
+
->ignore
|
|
879
917
|
| None =>
|
|
880
918
|
Logging.error("TestIndexerWorker: No worker data provided")
|
|
881
919
|
NodeJs.process->NodeJs.exitWithCode(Failure)
|