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
|
@@ -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 Source from "./Source.res.mjs";
|
|
6
5
|
import * as Logging from "../Logging.res.mjs";
|
|
7
6
|
import * as FetchState from "../FetchState.res.mjs";
|
|
8
7
|
import * as Prometheus from "../Prometheus.res.mjs";
|
|
8
|
+
import * as Performance from "../bindings/Performance.res.mjs";
|
|
9
9
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
10
10
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
11
11
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
@@ -139,7 +139,7 @@ function make(sources, isRealtime, newBlockStallTimeoutOpt, newBlockStallTimeout
|
|
|
139
139
|
disabled: false,
|
|
140
140
|
lastFailedAt: undefined
|
|
141
141
|
})),
|
|
142
|
-
statusStart:
|
|
142
|
+
statusStart: Performance.now(),
|
|
143
143
|
status: "Idle",
|
|
144
144
|
newBlockStallTimeout: newBlockStallTimeout,
|
|
145
145
|
newBlockStallTimeoutRealtime: newBlockStallTimeoutRealtime,
|
|
@@ -148,6 +148,7 @@ function make(sources, isRealtime, newBlockStallTimeoutOpt, newBlockStallTimeout
|
|
|
148
148
|
getHeightRetryInterval: getHeightRetryInterval,
|
|
149
149
|
activeSource: initialActiveSource,
|
|
150
150
|
waitingForNewBlockStateId: undefined,
|
|
151
|
+
waitingLogged: false,
|
|
151
152
|
fetchingPartitionsCount: 0,
|
|
152
153
|
recoveryTimeout: recoveryTimeout,
|
|
153
154
|
hasRealtime: hasRealtime,
|
|
@@ -172,8 +173,8 @@ function trackNewStatus(sourceManager, newStatus) {
|
|
|
172
173
|
promCounter = Prometheus.IndexingQueryTime.counter;
|
|
173
174
|
break;
|
|
174
175
|
}
|
|
175
|
-
Prometheus.SafeCounter.handleFloat(promCounter, sourceManager.activeSource.chain,
|
|
176
|
-
sourceManager.statusStart =
|
|
176
|
+
Prometheus.SafeCounter.handleFloat(promCounter, sourceManager.activeSource.chain, Performance.secondsSince(sourceManager.statusStart));
|
|
177
|
+
sourceManager.statusStart = Performance.now();
|
|
177
178
|
sourceManager.status = newStatus;
|
|
178
179
|
}
|
|
179
180
|
|
|
@@ -408,10 +409,9 @@ async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPo
|
|
|
408
409
|
chainId: sourceManager.activeSource.chain,
|
|
409
410
|
knownHeight: knownHeight
|
|
410
411
|
});
|
|
411
|
-
if (
|
|
412
|
-
Logging.childTrace(logger, `Waiting for new blocks with reduced polling (` + (sourceManager.reducedPollingInterval / 1000 | 0).toString() + `s). Chain is caught up, waiting for other chains to backfill.`);
|
|
413
|
-
|
|
414
|
-
Logging.childTrace(logger, "Initiating check for new blocks.");
|
|
412
|
+
if (!sourceManager.waitingLogged) {
|
|
413
|
+
Logging.childTrace(logger, reducedPolling ? `Waiting for new blocks with reduced polling (` + (sourceManager.reducedPollingInterval / 1000 | 0).toString() + `s). Chain is caught up, waiting for other chains to backfill.` : "Initiating check for new blocks.");
|
|
414
|
+
sourceManager.waitingLogged = true;
|
|
415
415
|
}
|
|
416
416
|
let mainSources = getNextSources(sourceManager, isRealtime, undefined);
|
|
417
417
|
let status = {
|
|
@@ -453,6 +453,7 @@ async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPo
|
|
|
453
453
|
source: source.name,
|
|
454
454
|
newBlockHeight: newBlockHeight
|
|
455
455
|
});
|
|
456
|
+
sourceManager.waitingLogged = false;
|
|
456
457
|
status.contents = "Done";
|
|
457
458
|
return newBlockHeight;
|
|
458
459
|
}
|
|
@@ -499,13 +500,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
499
500
|
retry: retry
|
|
500
501
|
});
|
|
501
502
|
try {
|
|
502
|
-
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, query.
|
|
503
|
-
Logging.childTrace(logger$2, {
|
|
504
|
-
msg: "Fetched block range from server",
|
|
505
|
-
toBlock: response.latestFetchedBlockNumber,
|
|
506
|
-
numEvents: response.parsedQueueItems.length,
|
|
507
|
-
stats: response.stats
|
|
508
|
-
});
|
|
503
|
+
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection, retry, logger$2);
|
|
509
504
|
sourceState.lastFailedAt = undefined;
|
|
510
505
|
responseRef = response;
|
|
511
506
|
} catch (raw_error) {
|
package/src/sources/Svm.res
CHANGED
|
@@ -20,16 +20,12 @@ let transactionFields = [
|
|
|
20
20
|
"tokenBalances",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
let transactionFieldMask = (eventConfigs: array<Internal.eventConfig>): float =>
|
|
27
|
-
eventConfigs->TransactionStore.mask(~codes=transactionFieldCodes)
|
|
23
|
+
// One instruction's selected transaction fields → store selection bitmask.
|
|
24
|
+
// Computed per event at config build and cached on the event config.
|
|
25
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
28
26
|
|
|
29
27
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
30
28
|
name: Svm,
|
|
31
|
-
blockFields: ["slot"],
|
|
32
|
-
transactionFields,
|
|
33
29
|
blockNumberName: "height",
|
|
34
30
|
blockTimestampName: "time",
|
|
35
31
|
blockHashName: "hash",
|
|
@@ -42,7 +38,6 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
42
38
|
// parse. The schema is a no-op object that always surfaces `None`.
|
|
43
39
|
onEventBlockFilterSchema: S.object(_ => None),
|
|
44
40
|
logger,
|
|
45
|
-
transactionFieldMask,
|
|
46
41
|
toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
|
|
47
42
|
toEventLogger: eventItem => {
|
|
48
43
|
let instruction =
|
|
@@ -94,9 +89,9 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
|
|
|
94
89
|
getBlockHashes: (~blockNumbers as _, ~logger as _) =>
|
|
95
90
|
JsError.throwWithMessage("Svm does not support getting block hashes"),
|
|
96
91
|
getHeightOrThrow: async () => {
|
|
97
|
-
let timerRef =
|
|
92
|
+
let timerRef = Performance.now()
|
|
98
93
|
let height = await GetFinalizedSlot.route->Rest.fetch((), ~client)
|
|
99
|
-
let seconds = timerRef->
|
|
94
|
+
let seconds = timerRef->Performance.secondsSince
|
|
100
95
|
Prometheus.SourceRequestCount.increment(~sourceName=name, ~chainId, ~method="getSlot")
|
|
101
96
|
Prometheus.SourceRequestCount.addSeconds(
|
|
102
97
|
~sourceName=name,
|
|
@@ -110,7 +105,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
|
|
|
110
105
|
~fromBlock as _,
|
|
111
106
|
~toBlock as _,
|
|
112
107
|
~addressesByContractName as _,
|
|
113
|
-
~
|
|
108
|
+
~contractNameByAddress as _,
|
|
114
109
|
~knownHeight as _,
|
|
115
110
|
~partitionId as _,
|
|
116
111
|
~selection as _,
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import * as Rpc from "./Rpc.res.mjs";
|
|
4
4
|
import * as Rest from "../vendored/Rest.res.mjs";
|
|
5
5
|
import * as Utils from "../Utils.res.mjs";
|
|
6
|
-
import * as Hrtime from "../bindings/Hrtime.res.mjs";
|
|
7
6
|
import * as Logging from "../Logging.res.mjs";
|
|
8
7
|
import * as Prometheus from "../Prometheus.res.mjs";
|
|
8
|
+
import * as Performance from "../bindings/Performance.res.mjs";
|
|
9
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
10
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
11
11
|
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
@@ -30,17 +30,11 @@ let transactionFields = [
|
|
|
30
30
|
"tokenBalances"
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
-
let
|
|
34
|
-
|
|
35
|
-
function transactionFieldMask(eventConfigs) {
|
|
36
|
-
return TransactionStore.mask(eventConfigs, transactionFieldCodes);
|
|
37
|
-
}
|
|
33
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
|
|
38
34
|
|
|
39
35
|
function make(logger) {
|
|
40
36
|
return {
|
|
41
37
|
name: "svm",
|
|
42
|
-
blockFields: ["slot"],
|
|
43
|
-
transactionFields: transactionFields,
|
|
44
38
|
blockNumberName: "height",
|
|
45
39
|
blockTimestampName: "time",
|
|
46
40
|
blockHashName: "hash",
|
|
@@ -50,7 +44,6 @@ function make(logger) {
|
|
|
50
44
|
onEventBlockFilterSchema: S$RescriptSchema.object(param => {}),
|
|
51
45
|
logger: logger,
|
|
52
46
|
toEvent: eventItem => eventItem.payload,
|
|
53
|
-
transactionFieldMask: transactionFieldMask,
|
|
54
47
|
toEventLogger: eventItem => {
|
|
55
48
|
let instruction = eventItem.payload;
|
|
56
49
|
return Logging.createChildFrom(logger, {
|
|
@@ -89,9 +82,9 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
89
82
|
pollingInterval: 10000,
|
|
90
83
|
getBlockHashes: (param, param$1) => Stdlib_JsError.throwWithMessage("Svm does not support getting block hashes"),
|
|
91
84
|
getHeightOrThrow: async () => {
|
|
92
|
-
let timerRef =
|
|
85
|
+
let timerRef = Performance.now();
|
|
93
86
|
let height = await Rest.fetch(route, undefined, client);
|
|
94
|
-
let seconds =
|
|
87
|
+
let seconds = Performance.secondsSince(timerRef);
|
|
95
88
|
Prometheus.SourceRequestCount.increment(name, chain, "getSlot");
|
|
96
89
|
Prometheus.SourceRequestCount.addSeconds(name, chain, "getSlot", seconds);
|
|
97
90
|
return height;
|
|
@@ -103,10 +96,9 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
103
96
|
export {
|
|
104
97
|
cleanUpRawEventFieldsInPlace,
|
|
105
98
|
transactionFields,
|
|
106
|
-
|
|
107
|
-
transactionFieldMask,
|
|
99
|
+
eventTransactionFieldMask,
|
|
108
100
|
make,
|
|
109
101
|
GetFinalizedSlot,
|
|
110
102
|
makeRPCSource,
|
|
111
103
|
}
|
|
112
|
-
/*
|
|
104
|
+
/* eventTransactionFieldMask Not a pure module */
|
|
@@ -216,11 +216,11 @@ let probeRouter = (
|
|
|
216
216
|
instr: SvmHyperSyncClient.ResponseTypes.instruction,
|
|
217
217
|
byteLengthsDesc: array<int>,
|
|
218
218
|
~contractAddress,
|
|
219
|
-
~
|
|
219
|
+
~contractNameByAddress,
|
|
220
220
|
) => {
|
|
221
221
|
let probe = (dN: option<string>) => {
|
|
222
222
|
let tag = EventRouter.getSvmEventId(~programId, ~discriminator=dN)
|
|
223
|
-
router->EventRouter.get(~tag, ~contractAddress, ~
|
|
223
|
+
router->EventRouter.get(~tag, ~contractAddress, ~contractNameByAddress)
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
let result = byteLengthsDesc->Array.reduce(None, (acc, len) =>
|
|
@@ -319,26 +319,32 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
319
319
|
)
|
|
320
320
|
fields
|
|
321
321
|
}
|
|
322
|
-
//
|
|
323
|
-
// stored transaction record
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
322
|
+
// The transaction table is fetched only when a selected field is actually read
|
|
323
|
+
// off a stored transaction record. `transactionIndex` materialises from the
|
|
324
|
+
// store key and `tokenBalances` lives in its own table, so neither requires it.
|
|
325
|
+
let needsTransactions =
|
|
326
|
+
selectedTxFields
|
|
327
|
+
->Utils.Set.toArray
|
|
328
|
+
->Array.some(field =>
|
|
329
|
+
switch field {
|
|
330
|
+
| Internal.TransactionIndex | Internal.TokenBalances => false
|
|
331
|
+
| _ => true
|
|
332
|
+
}
|
|
333
|
+
)
|
|
328
334
|
|
|
329
335
|
let getItemsOrThrow = async (
|
|
330
336
|
~fromBlock,
|
|
331
337
|
~toBlock,
|
|
332
338
|
~addressesByContractName as _,
|
|
333
|
-
~
|
|
339
|
+
~contractNameByAddress,
|
|
334
340
|
~knownHeight,
|
|
335
341
|
~partitionId as _,
|
|
336
342
|
~selection as _,
|
|
337
343
|
~retry,
|
|
338
344
|
~logger,
|
|
339
345
|
) => {
|
|
340
|
-
let totalTimeRef =
|
|
341
|
-
let pageFetchRef =
|
|
346
|
+
let totalTimeRef = Performance.now()
|
|
347
|
+
let pageFetchRef = Performance.now()
|
|
342
348
|
|
|
343
349
|
let instructionSelections = buildInstructionSelections(eventConfigs)
|
|
344
350
|
// Under the server's default merge mode, requesting a table's columns is
|
|
@@ -384,9 +390,9 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
384
390
|
),
|
|
385
391
|
)
|
|
386
392
|
}
|
|
387
|
-
let pageFetchTime = pageFetchRef->
|
|
393
|
+
let pageFetchTime = pageFetchRef->Performance.secondsSince
|
|
388
394
|
|
|
389
|
-
let parsingRef =
|
|
395
|
+
let parsingRef = Performance.now()
|
|
390
396
|
|
|
391
397
|
// Per-slot unix timestamp lookup from the response's `blocks` table. Slots
|
|
392
398
|
// without a block row (rare; usually skipped slots) fall back to `None`.
|
|
@@ -434,7 +440,7 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
434
440
|
instr,
|
|
435
441
|
byteLengths,
|
|
436
442
|
~contractAddress,
|
|
437
|
-
~
|
|
443
|
+
~contractNameByAddress,
|
|
438
444
|
)
|
|
439
445
|
|
|
440
446
|
switch maybeConfig {
|
|
@@ -492,7 +498,7 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
492
498
|
let _ = logger
|
|
493
499
|
})
|
|
494
500
|
|
|
495
|
-
let parsingTimeElapsed = parsingRef->
|
|
501
|
+
let parsingTimeElapsed = parsingRef->Performance.secondsSince
|
|
496
502
|
let highestSlot = resp.nextSlot - 1
|
|
497
503
|
let latestBlockTime =
|
|
498
504
|
blockTimeBySlot
|
|
@@ -507,7 +513,7 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
507
513
|
blockHash: b.blockhash,
|
|
508
514
|
})
|
|
509
515
|
|
|
510
|
-
let totalTimeElapsed = totalTimeRef->
|
|
516
|
+
let totalTimeElapsed = totalTimeRef->Performance.secondsSince
|
|
511
517
|
|
|
512
518
|
{
|
|
513
519
|
latestFetchedBlockTimestamp: latestBlockTime,
|
|
@@ -597,9 +603,9 @@ let make = ({chain, endpointUrl, apiToken, eventConfigs, clientTimeoutMillis}: o
|
|
|
597
603
|
poweredByHyperSync: true,
|
|
598
604
|
getBlockHashes,
|
|
599
605
|
getHeightOrThrow: async () => {
|
|
600
|
-
let timer =
|
|
606
|
+
let timer = Performance.now()
|
|
601
607
|
let h = await client.getHeight()
|
|
602
|
-
let seconds = timer->
|
|
608
|
+
let seconds = timer->Performance.secondsSince
|
|
603
609
|
Prometheus.SourceRequestCount.increment(~sourceName=name, ~chainId, ~method="getHeight")
|
|
604
610
|
Prometheus.SourceRequestCount.addSeconds(
|
|
605
611
|
~sourceName=name,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Hrtime from "../bindings/Hrtime.res.mjs";
|
|
4
3
|
import * as Source from "./Source.res.mjs";
|
|
5
4
|
import * as Prometheus from "../Prometheus.res.mjs";
|
|
6
5
|
import * as EventRouter from "./EventRouter.res.mjs";
|
|
6
|
+
import * as Performance from "../bindings/Performance.res.mjs";
|
|
7
7
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
9
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
@@ -185,10 +185,10 @@ function toSvmInstruction(instr, programName, instructionName, logs, block) {
|
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress,
|
|
188
|
+
function probeRouter(router, programId, instr, byteLengthsDesc, contractAddress, contractNameByAddress) {
|
|
189
189
|
let probe = dN => {
|
|
190
190
|
let tag = EventRouter.getSvmEventId(programId, dN);
|
|
191
|
-
return EventRouter.get(router, tag, contractAddress,
|
|
191
|
+
return EventRouter.get(router, tag, contractAddress, contractNameByAddress);
|
|
192
192
|
};
|
|
193
193
|
let result = Stdlib_Array.reduce(byteLengthsDesc, undefined, (acc, len) => {
|
|
194
194
|
if (acc !== undefined) {
|
|
@@ -279,11 +279,18 @@ function make(param) {
|
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
281
|
});
|
|
282
|
-
let
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
let needsTransactions = Array.from(selectedTxFields).some(field => {
|
|
283
|
+
switch (field) {
|
|
284
|
+
case "transactionIndex" :
|
|
285
|
+
case "tokenBalances" :
|
|
286
|
+
return false;
|
|
287
|
+
default:
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
let getItemsOrThrow = async (fromBlock, toBlock, param, contractNameByAddress, knownHeight, param$1, param$2, retry, logger) => {
|
|
292
|
+
let totalTimeRef = Performance.now();
|
|
293
|
+
let pageFetchRef = Performance.now();
|
|
287
294
|
let instructionSelections = buildInstructionSelections(eventConfigs);
|
|
288
295
|
let fields_block = [
|
|
289
296
|
"slot",
|
|
@@ -344,8 +351,8 @@ function make(param) {
|
|
|
344
351
|
};
|
|
345
352
|
}
|
|
346
353
|
let resp = match[0];
|
|
347
|
-
let pageFetchTime =
|
|
348
|
-
let parsingRef =
|
|
354
|
+
let pageFetchTime = Performance.secondsSince(pageFetchRef);
|
|
355
|
+
let parsingRef = Performance.now();
|
|
349
356
|
let blockTimeBySlot = {};
|
|
350
357
|
resp.data.blocks.forEach(b => {
|
|
351
358
|
let t = b.blockTime;
|
|
@@ -377,7 +384,7 @@ function make(param) {
|
|
|
377
384
|
let programId = instr.programId;
|
|
378
385
|
let byteLengths = Stdlib_Option.getOr(orderingByProgram[instr.programId], []);
|
|
379
386
|
let contractAddress = instr.programId;
|
|
380
|
-
let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress,
|
|
387
|
+
let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, contractNameByAddress);
|
|
381
388
|
if (maybeConfig !== undefined) {
|
|
382
389
|
let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
|
|
383
390
|
let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
|
|
@@ -404,14 +411,14 @@ function make(param) {
|
|
|
404
411
|
});
|
|
405
412
|
}
|
|
406
413
|
});
|
|
407
|
-
let parsingTimeElapsed =
|
|
414
|
+
let parsingTimeElapsed = Performance.secondsSince(parsingRef);
|
|
408
415
|
let highestSlot = resp.nextSlot - 1 | 0;
|
|
409
416
|
let latestBlockTime = Stdlib_Option.getOr(blockTimeBySlot[highestSlot.toString()], 0);
|
|
410
417
|
let blockHashes = resp.data.blocks.map(b => ({
|
|
411
418
|
blockHash: b.blockhash,
|
|
412
419
|
blockNumber: b.slot
|
|
413
420
|
}));
|
|
414
|
-
let totalTimeElapsed =
|
|
421
|
+
let totalTimeElapsed = Performance.secondsSince(totalTimeRef);
|
|
415
422
|
return {
|
|
416
423
|
knownHeight: knownHeight,
|
|
417
424
|
blockHashes: blockHashes,
|
|
@@ -513,9 +520,9 @@ function make(param) {
|
|
|
513
520
|
pollingInterval: 1000,
|
|
514
521
|
getBlockHashes: getBlockHashes,
|
|
515
522
|
getHeightOrThrow: async () => {
|
|
516
|
-
let timer =
|
|
523
|
+
let timer = Performance.now();
|
|
517
524
|
let h = await client.getHeight();
|
|
518
|
-
let seconds =
|
|
525
|
+
let seconds = Performance.secondsSince(timer);
|
|
519
526
|
Prometheus.SourceRequestCount.increment(name, chain, "getHeight");
|
|
520
527
|
Prometheus.SourceRequestCount.addSeconds(name, chain, "getHeight", seconds);
|
|
521
528
|
return h;
|
|
@@ -19,32 +19,45 @@ let fieldCodes = (fields: array<string>): dict<int> => {
|
|
|
19
19
|
|
|
20
20
|
let pow2: int => float = %raw(`c => Math.pow(2, c)`)
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
// One event's selected transaction fields as a bitmask float (bit `code` set ⇔
|
|
23
|
+
// selected). Each field appears once, so summing `pow2(code)` sets distinct bits
|
|
24
|
+
// with no overlap; the result stays exact in f64 (codes span 0..31, so a mask is
|
|
25
|
+
// at most 2^32-1).
|
|
26
|
+
let maskFromFields = (selectedTransactionFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
|
|
27
|
+
let mask = ref(0.)
|
|
28
|
+
selectedTransactionFields->Utils.Set.forEach(name =>
|
|
29
|
+
switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
30
|
+
| Some(code) => mask := mask.contents +. pow2(code)
|
|
31
|
+
| None => ()
|
|
32
|
+
}
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
mask.contents
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Build an ecosystem's per-event mask function from its ordered field-name
|
|
38
|
+
// array. The field codes are derived once and closed over.
|
|
39
|
+
let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
|
|
40
|
+
let codes = fieldCodes(fields)
|
|
41
|
+
selectedTransactionFields => selectedTransactionFields->maskFromFields(~codes)
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
// Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 transaction
|
|
45
|
+
// fields), so `>>> 0` recovers the unsigned value that a plain `|` renders
|
|
46
|
+
// negative once bit 31 is set.
|
|
47
|
+
let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
|
|
48
|
+
|
|
37
49
|
// Drain another store (a fetch-response page) into this one.
|
|
38
50
|
@send external merge: (t, t) => unit = "merge"
|
|
39
51
|
|
|
40
|
-
// Bulk-materialise
|
|
41
|
-
//
|
|
52
|
+
// Bulk-materialise transactions off the JS thread, one row per
|
|
53
|
+
// (blockNumbers[i], transactionIndices[i]) key, decoding only the fields set in
|
|
54
|
+
// that row's own masks[i]. Result is aligned with the input.
|
|
42
55
|
@send
|
|
43
56
|
external materialize: (
|
|
44
57
|
t,
|
|
45
58
|
~blockNumbers: array<int>,
|
|
46
59
|
~transactionIndices: array<int>,
|
|
47
|
-
~
|
|
60
|
+
~masks: array<float>,
|
|
48
61
|
) => promise<array<Internal.eventTransaction>> = "materialize"
|
|
49
62
|
|
|
50
63
|
// Drop transactions for blocks at or below the given block (already processed).
|
|
@@ -53,21 +66,28 @@ external materialize: (
|
|
|
53
66
|
// Drop transactions for blocks above the given block (rolled back).
|
|
54
67
|
@send external rollback: (t, int) => unit = "rollback"
|
|
55
68
|
|
|
56
|
-
// Materialise
|
|
57
|
-
// resulting transaction onto
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
69
|
+
// Materialise each store-backed item's selected transaction fields and write the
|
|
70
|
+
// resulting transaction onto its payload. Every event's mask comes from its own
|
|
71
|
+
// `eventConfig.transactionFieldMask`, so a transaction decodes only the fields
|
|
72
|
+
// the events on it selected — a large field (e.g. `input`) never materialises
|
|
73
|
+
// for events that didn't ask for it. Items that already carry an inline
|
|
74
|
+
// transaction (RPC/simulate/Fuel) are skipped. Store-backed items always get a
|
|
75
|
+
// transaction object — the selected fields, or `{}` when nothing was selected —
|
|
76
|
+
// so `event.transaction` is never `undefined` (matching the inline sources).
|
|
77
|
+
// Deduped per (blockNumber, transactionIndex); each row's mask is the OR of the
|
|
78
|
+
// masks of the events sharing that transaction.
|
|
79
|
+
let materializeItems = async (store: t, ~items: array<Internal.item>) => {
|
|
63
80
|
// Store-backed items arrive in (block, logIndex) order, and a transaction's
|
|
64
81
|
// logs are contiguous within a block, so events sharing a (blockNumber,
|
|
65
|
-
// transactionIndex) are adjacent. Group them by extending
|
|
66
|
-
// rather than hashing a string key per item. A key recurring
|
|
67
|
-
// just splits into two groups (one redundant decode) — never
|
|
82
|
+
// transactionIndex) are adjacent regardless of event. Group them by extending
|
|
83
|
+
// the current run rather than hashing a string key per item. A key recurring
|
|
84
|
+
// non-adjacently just splits into two groups (one redundant decode) — never
|
|
85
|
+
// incorrect.
|
|
68
86
|
let blockNumbers = []
|
|
69
87
|
let transactionIndices = []
|
|
88
|
+
let masks = []
|
|
70
89
|
let payloadGroups = []
|
|
90
|
+
let anySelected = ref(false)
|
|
71
91
|
|
|
72
92
|
items->Array.forEach(item =>
|
|
73
93
|
switch item {
|
|
@@ -77,6 +97,10 @@ let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: flo
|
|
|
77
97
|
| Some(_) => () // RPC/simulate/Fuel carry the transaction inline.
|
|
78
98
|
| None =>
|
|
79
99
|
let {blockNumber, transactionIndex} = eventItem
|
|
100
|
+
let mask = eventItem.eventConfig.transactionFieldMask
|
|
101
|
+
if mask != 0. {
|
|
102
|
+
anySelected := true
|
|
103
|
+
}
|
|
80
104
|
let last = payloadGroups->Array.length - 1
|
|
81
105
|
if (
|
|
82
106
|
last >= 0 &&
|
|
@@ -84,9 +108,11 @@ let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: flo
|
|
|
84
108
|
transactionIndices->Array.getUnsafe(last) == transactionIndex
|
|
85
109
|
) {
|
|
86
110
|
payloadGroups->Array.getUnsafe(last)->Array.push(eventItem.payload)
|
|
111
|
+
masks->Array.setUnsafe(last, orMask(masks->Array.getUnsafe(last), mask))
|
|
87
112
|
} else {
|
|
88
113
|
blockNumbers->Array.push(blockNumber)
|
|
89
114
|
transactionIndices->Array.push(transactionIndex)
|
|
115
|
+
masks->Array.push(mask)
|
|
90
116
|
payloadGroups->Array.push([eventItem.payload])
|
|
91
117
|
}
|
|
92
118
|
}
|
|
@@ -95,17 +121,18 @@ let materializeItems = async (store: t, ~items: array<Internal.item>, ~mask: flo
|
|
|
95
121
|
)
|
|
96
122
|
|
|
97
123
|
if payloadGroups->Utils.Array.notEmpty {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
if anySelected.contents {
|
|
125
|
+
let txs = await store->materialize(~blockNumbers, ~transactionIndices, ~masks)
|
|
126
|
+
payloadGroups->Array.forEachWithIndex((payloads, i) => {
|
|
127
|
+
let tx = txs->Array.getUnsafe(i)
|
|
128
|
+
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
|
|
129
|
+
})
|
|
103
130
|
} else {
|
|
104
|
-
|
|
131
|
+
// No event selected any field: stamp an empty transaction object so
|
|
132
|
+
// `event.transaction` is never undefined, without a materialize call.
|
|
133
|
+
payloadGroups->Array.forEach(payloads =>
|
|
134
|
+
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(%raw(`{}`)))
|
|
135
|
+
)
|
|
105
136
|
}
|
|
106
|
-
payloadGroups->Array.forEachWithIndex((payloads, i) => {
|
|
107
|
-
let tx = txs->Array.getUnsafe(i)
|
|
108
|
-
payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
|
|
109
|
-
})
|
|
110
137
|
}
|
|
111
138
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Core from "../Core.res.mjs";
|
|
4
4
|
import * as Utils from "../Utils.res.mjs";
|
|
5
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
5
|
|
|
7
6
|
function make() {
|
|
8
7
|
return Core.getAddon().TransactionStore.new();
|
|
@@ -18,24 +17,35 @@ function fieldCodes(fields) {
|
|
|
18
17
|
|
|
19
18
|
let pow2 = (c => Math.pow(2, c));
|
|
20
19
|
|
|
21
|
-
function
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
20
|
+
function maskFromFields(selectedTransactionFields, codes) {
|
|
21
|
+
let mask = {
|
|
22
|
+
contents: 0
|
|
23
|
+
};
|
|
24
|
+
selectedTransactionFields.forEach(name => {
|
|
25
|
+
let code = codes[name];
|
|
26
|
+
if (code !== undefined) {
|
|
27
|
+
mask.contents = mask.contents + pow2(code);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
31
30
|
});
|
|
32
|
-
return
|
|
31
|
+
return mask.contents;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeMaskFn(fields) {
|
|
35
|
+
let codes = fieldCodes(fields);
|
|
36
|
+
return selectedTransactionFields => maskFromFields(selectedTransactionFields, codes);
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
let orMask = ((a, b) => (a | b) >>> 0);
|
|
40
|
+
|
|
41
|
+
async function materializeItems(store, items) {
|
|
36
42
|
let blockNumbers = [];
|
|
37
43
|
let transactionIndices = [];
|
|
44
|
+
let masks = [];
|
|
38
45
|
let payloadGroups = [];
|
|
46
|
+
let anySelected = {
|
|
47
|
+
contents: false
|
|
48
|
+
};
|
|
39
49
|
items.forEach(item => {
|
|
40
50
|
if (item.kind !== 0) {
|
|
41
51
|
return;
|
|
@@ -46,23 +56,37 @@ async function materializeItems(store, items, mask) {
|
|
|
46
56
|
}
|
|
47
57
|
let transactionIndex = item.transactionIndex;
|
|
48
58
|
let blockNumber = item.blockNumber;
|
|
59
|
+
let mask = item.eventConfig.transactionFieldMask;
|
|
60
|
+
if (mask !== 0) {
|
|
61
|
+
anySelected.contents = true;
|
|
62
|
+
}
|
|
49
63
|
let last = payloadGroups.length - 1 | 0;
|
|
50
64
|
if (last >= 0 && blockNumbers[last] === blockNumber && transactionIndices[last] === transactionIndex) {
|
|
51
65
|
payloadGroups[last].push(item.payload);
|
|
66
|
+
masks[last] = orMask(masks[last], mask);
|
|
52
67
|
} else {
|
|
53
68
|
blockNumbers.push(blockNumber);
|
|
54
69
|
transactionIndices.push(transactionIndex);
|
|
70
|
+
masks.push(mask);
|
|
55
71
|
payloadGroups.push([item.payload]);
|
|
56
72
|
}
|
|
57
73
|
});
|
|
58
74
|
if (!Utils.$$Array.notEmpty(payloadGroups)) {
|
|
59
75
|
return;
|
|
60
76
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
if (anySelected.contents) {
|
|
78
|
+
let txs = await store.materialize(blockNumbers, transactionIndices, masks);
|
|
79
|
+
payloadGroups.forEach((payloads, i) => {
|
|
80
|
+
let tx = txs[i];
|
|
81
|
+
payloads.forEach(payload => {
|
|
82
|
+
payload.transaction = tx;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
payloadGroups.forEach(payloads => {
|
|
64
88
|
payloads.forEach(payload => {
|
|
65
|
-
payload.transaction =
|
|
89
|
+
payload.transaction = {};
|
|
66
90
|
});
|
|
67
91
|
});
|
|
68
92
|
}
|
|
@@ -71,7 +95,9 @@ export {
|
|
|
71
95
|
make,
|
|
72
96
|
fieldCodes,
|
|
73
97
|
pow2,
|
|
74
|
-
|
|
98
|
+
maskFromFields,
|
|
99
|
+
makeMaskFn,
|
|
100
|
+
orMask,
|
|
75
101
|
materializeItems,
|
|
76
102
|
}
|
|
77
103
|
/* Core Not a pure module */
|