envio 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/index.d.ts +158 -104
  3. package/package.json +6 -6
  4. package/src/AddressRows.res +121 -0
  5. package/src/AddressRows.res.mjs +143 -0
  6. package/src/Batch.res +2 -0
  7. package/src/Batch.res.mjs +2 -1
  8. package/src/ChainState.res +76 -70
  9. package/src/ChainState.res.mjs +76 -65
  10. package/src/ChainState.resi +14 -9
  11. package/src/Config.res +23 -82
  12. package/src/Config.res.mjs +19 -63
  13. package/src/ContractMapping.res +66 -0
  14. package/src/ContractMapping.res.mjs +73 -0
  15. package/src/Core.res +20 -0
  16. package/src/Core.res.mjs +4 -0
  17. package/src/EventConfigBuilder.res +0 -2
  18. package/src/EventConfigBuilder.res.mjs +0 -2
  19. package/src/FetchState.res +59 -67
  20. package/src/FetchState.res.mjs +34 -44
  21. package/src/InMemoryStore.res +17 -30
  22. package/src/InMemoryStore.res.mjs +11 -23
  23. package/src/IndexerState.res +21 -1
  24. package/src/IndexerState.res.mjs +9 -3
  25. package/src/IndexerState.resi +1 -0
  26. package/src/Main.res +25 -15
  27. package/src/Main.res.mjs +19 -14
  28. package/src/MemoryStorage.res +39 -66
  29. package/src/MemoryStorage.res.mjs +28 -65
  30. package/src/Metrics.res +15 -1
  31. package/src/Metrics.res.mjs +5 -1
  32. package/src/Persistence.res +30 -16
  33. package/src/Persistence.res.mjs +8 -5
  34. package/src/PgStorage.res +183 -65
  35. package/src/PgStorage.res.mjs +92 -58
  36. package/src/Rollback.res +14 -10
  37. package/src/Rollback.res.mjs +4 -2
  38. package/src/SimulateItems.res +5 -12
  39. package/src/SimulateItems.res.mjs +6 -10
  40. package/src/TestIndexer.res +93 -111
  41. package/src/TestIndexer.res.mjs +61 -88
  42. package/src/Utils.res +7 -0
  43. package/src/Utils.res.mjs +9 -2
  44. package/src/Writing.res +2 -0
  45. package/src/Writing.res.mjs +2 -1
  46. package/src/bindings/ClickHouse.res +6 -0
  47. package/src/bindings/ClickHouse.res.mjs +4 -0
  48. package/src/bindings/NodeJs.res +9 -0
  49. package/src/bindings/NodeJs.res.mjs +8 -1
  50. package/src/bindings/Postgres.res +9 -0
  51. package/src/bindings/Postgres.res.mjs +3 -0
  52. package/src/db/EntityHistory.res +12 -18
  53. package/src/db/EntityHistory.res.mjs +3 -14
  54. package/src/db/InternalTable.res +171 -65
  55. package/src/db/InternalTable.res.mjs +176 -73
  56. package/src/db/Table.res +24 -0
  57. package/src/db/Table.res.mjs +20 -1
  58. package/src/sources/AddressSet.res +0 -22
  59. package/src/sources/AddressStore.res +48 -88
  60. package/src/sources/AddressStore.res.mjs +11 -22
  61. package/src/sources/SvmHyperSyncClient.res +17 -21
  62. package/src/sources/SvmHyperSyncSource.res +4 -9
  63. package/src/sources/SvmHyperSyncSource.res.mjs +5 -13
  64. package/svm.schema.json +10 -4
@@ -0,0 +1,143 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Core from "./Core.res.mjs";
4
+ import * as Utils from "./Utils.res.mjs";
5
+ import * as ChainId from "./ChainId.res.mjs";
6
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
7
+
8
+ function emptySeedRows() {
9
+ return {
10
+ addresses: [],
11
+ contractIds: [],
12
+ registrationBlocks: []
13
+ };
14
+ }
15
+
16
+ function keyOf(row) {
17
+ return {
18
+ chainId: row.chainId,
19
+ address: row.address,
20
+ contractId: row.contractId
21
+ };
22
+ }
23
+
24
+ function packBuffers(addresses) {
25
+ return [
26
+ Buffer.concat(addresses),
27
+ addresses.map(address => address.length)
28
+ ];
29
+ }
30
+
31
+ function packKeys(rows) {
32
+ return packBuffers(rows.map(row => row.address));
33
+ }
34
+
35
+ function seedRowsOf(rows) {
36
+ let addresses = [];
37
+ let contractIds = [];
38
+ let registrationBlocks = [];
39
+ rows.forEach(row => {
40
+ addresses.push(row.address);
41
+ contractIds.push(row.contractId);
42
+ registrationBlocks.push(row.registrationBlock);
43
+ });
44
+ return {
45
+ addresses: addresses,
46
+ contractIds: contractIds,
47
+ registrationBlocks: registrationBlocks
48
+ };
49
+ }
50
+
51
+ function group(rows) {
52
+ let rowsByChain = {};
53
+ let runChainId = {
54
+ contents: undefined
55
+ };
56
+ let runKey = {
57
+ contents: ""
58
+ };
59
+ rows.forEach(row => {
60
+ if (runChainId.contents !== Primitive_option.some(row.chainId)) {
61
+ runChainId.contents = Primitive_option.some(row.chainId);
62
+ runKey.contents = ChainId.toString(ChainId.normalizeOrThrow(row.chainId));
63
+ }
64
+ Utils.Dict.push(rowsByChain, runKey.contents, row);
65
+ });
66
+ return Utils.Dict.mapValues(rowsByChain, seedRowsOf);
67
+ }
68
+
69
+ function dictKey(key) {
70
+ return ChainId.toString(key.chainId) + `|` + key.contractId.toString() + `|` + key.address.toString("base64");
71
+ }
72
+
73
+ function make() {
74
+ return {};
75
+ }
76
+
77
+ function clear(table) {
78
+ Utils.Dict.clearInPlace(table);
79
+ }
80
+
81
+ function insert(table, row) {
82
+ let key = dictKey(keyOf(row));
83
+ let match = table[key];
84
+ if (match !== undefined) {
85
+ return;
86
+ } else {
87
+ table[key] = row;
88
+ return;
89
+ }
90
+ }
91
+
92
+ function insertMany(table, rows) {
93
+ rows.forEach(row => insert(table, row));
94
+ }
95
+
96
+ function $$delete(table, key) {
97
+ Utils.Dict.deleteInPlace(table, dictKey(key));
98
+ }
99
+
100
+ function rows(table) {
101
+ return Object.values(table);
102
+ }
103
+
104
+ function groupByChain(table) {
105
+ return group(Object.values(table));
106
+ }
107
+
108
+ let Table = {
109
+ dictKey: dictKey,
110
+ make: make,
111
+ clear: clear,
112
+ insert: insert,
113
+ insertMany: insertMany,
114
+ $$delete: $$delete,
115
+ rows: rows,
116
+ groupByChain: groupByChain
117
+ };
118
+
119
+ function render(rows, ecosystem, shouldChecksum) {
120
+ let match = packKeys(rows);
121
+ return Core.getAddon().renderAddresses(ecosystem, shouldChecksum, match[0], match[1]);
122
+ }
123
+
124
+ function renderOfContract(rows, ecosystem, shouldChecksum, contractId) {
125
+ let match = packBuffers(rows.addresses);
126
+ return Core.getAddon().renderContractAddresses(ecosystem, shouldChecksum, match[0], match[1], rows.contractIds, contractId);
127
+ }
128
+
129
+ let configRegistrationBlock = -1;
130
+
131
+ export {
132
+ emptySeedRows,
133
+ configRegistrationBlock,
134
+ keyOf,
135
+ packBuffers,
136
+ packKeys,
137
+ seedRowsOf,
138
+ group,
139
+ Table,
140
+ render,
141
+ renderOfContract,
142
+ }
143
+ /* Core Not a pure module */
package/src/Batch.res CHANGED
@@ -43,6 +43,7 @@ type t = {
43
43
  checkpointBlockNumbers: array<int>,
44
44
  checkpointBlockHashes: array<Null.t<string>>,
45
45
  checkpointEventsProcessed: array<int>,
46
+ registeredAddresses: array<AddressRows.staged>,
46
47
  }
47
48
 
48
49
  let getProgressedChainsById = {
@@ -326,6 +327,7 @@ let prepareBatch = (
326
327
  checkpointBlockNumbers,
327
328
  checkpointBlockHashes,
328
329
  checkpointEventsProcessed,
330
+ registeredAddresses: [],
329
331
  }
330
332
  }
331
333
 
package/src/Batch.res.mjs CHANGED
@@ -185,7 +185,8 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
185
185
  checkpointChainIds: checkpointChainIds,
186
186
  checkpointBlockNumbers: checkpointBlockNumbers,
187
187
  checkpointBlockHashes: checkpointBlockHashes,
188
- checkpointEventsProcessed: checkpointEventsProcessed
188
+ checkpointEventsProcessed: checkpointEventsProcessed,
189
+ registeredAddresses: []
189
190
  };
190
191
  }
191
192
 
@@ -62,20 +62,48 @@ type t = {
62
62
  mutable progressLatencyMs: option<int>,
63
63
  }
64
64
 
65
- let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
65
+ // The chain's config-declared addresses as storage rows: what a clean run
66
+ // writes into `envio_addresses`. The keys are encoded by the Rust codec, so the
67
+ // bytes a clean run writes are exactly the bytes a resume seeds from.
68
+ let configStorageRows = (
69
+ chainConfig: Config.chain,
70
+ ~ecosystem: Ecosystem.name,
71
+ ~contractMapping: ContractMapping.t,
72
+ ): array<AddressRows.row> => {
66
73
  let addresses = []
74
+ let contractIds = []
67
75
  chainConfig.contracts->Array.forEach(contract => {
76
+ let contractId = contractMapping->ContractMapping.idOfOrThrow(contract.name)
68
77
  contract.addresses->Array.forEach(address => {
69
- addresses->Array.push({
70
- Internal.address,
71
- contractName: contract.name,
72
- registrationBlock: -1,
73
- })
78
+ addresses->Array.push(address)->ignore
79
+ contractIds->Array.push(contractId)->ignore
74
80
  })
75
81
  })
76
- addresses
82
+ Core.getAddon().encodeAddresses(
83
+ ~ecosystem=(ecosystem :> string),
84
+ ~addresses,
85
+ )->Array.mapWithIndex((address, idx) => {
86
+ AddressRows.chainId: chainConfig.id,
87
+ address,
88
+ contractId: contractIds->Array.getUnsafe(idx),
89
+ registrationBlock: AddressRows.configRegistrationBlock,
90
+ })
77
91
  }
78
92
 
93
+ // Seeds an in-memory address table with every chain's config-declared
94
+ // addresses, the twin of what `PgStorage.initialize` writes in one insert.
95
+ let seedConfigAddresses = (
96
+ table: AddressRows.Table.t,
97
+ ~chainConfigs: array<Config.chain>,
98
+ ~ecosystem: Ecosystem.name,
99
+ ~contractMapping: ContractMapping.t,
100
+ ) =>
101
+ chainConfigs->Array.forEach(chainConfig =>
102
+ table->AddressRows.Table.insertMany(
103
+ chainConfig->configStorageRows(~ecosystem, ~contractMapping),
104
+ )
105
+ )
106
+
79
107
  let validateOnEventRegistrations = (
80
108
  ~chainId: ChainId.t,
81
109
  registrations: array<Internal.onEventRegistration>,
@@ -142,22 +170,12 @@ let make = (
142
170
  }
143
171
  }
144
172
 
145
- // Every chain's contracts, not just one chain's: `context.chain.X.add` accepts
146
- // any config contract, whichever chain declared it, so every chain's address
147
- // store has to hold the whole set.
148
- let configContractNames = (config: Config.t) => {
149
- let names = Utils.Set.make()
150
- config.chainMap
151
- ->ChainMap.values
152
- ->Array.forEach(chain =>
153
- chain.contracts->Array.forEach(contract => names->Utils.Set.add(contract.name)->ignore)
154
- )
155
- names->Utils.Set.toArray
156
- }
157
-
158
173
  let makeInternal = (
159
174
  ~chainConfig: Config.chain,
160
- ~indexingAddresses: array<Internal.indexingAddress>,
175
+ ~addressRows: AddressRows.seedRows,
176
+ // Passed rather than read off `config`, because a resume must use the mapping
177
+ // storage holds: the ids stored rows carry, not the ids this config derives.
178
+ ~contractMapping: ContractMapping.t,
161
179
  ~startBlock,
162
180
  ~endBlock,
163
181
  ~firstEventBlock=None,
@@ -199,16 +217,13 @@ let makeInternal = (
199
217
  let addressStore = AddressStore.make(
200
218
  ~ecosystem=config.ecosystem.name,
201
219
  ~shouldChecksum=!lowercaseAddresses,
202
- ~contracts=AddressStore.contractsOf(
203
- ~onEventRegistrations,
204
- ~configContractNames=config->configContractNames,
205
- ),
220
+ ~contracts=AddressStore.contractsOf(~onEventRegistrations, ~contractMapping),
206
221
  )
207
222
 
208
223
  let fetchState = FetchState.make(
209
224
  ~maxAddrInPartition=config.maxAddrInPartition,
210
225
  ~addressStore,
211
- ~addresses=indexingAddresses,
226
+ ~addressRows,
212
227
  ~progressBlockNumber,
213
228
  ~startBlock,
214
229
  ~endBlock,
@@ -359,7 +374,7 @@ let makeInternal = (
359
374
  ~chainReorgCheckpoints,
360
375
  ),
361
376
  ~committedProgressBlockNumber=progressBlockNumber,
362
- ~perChainEntities=config.allEntities->EntityTables.perChain,
377
+ ~perChainEntities=config.userEntities->EntityTables.perChain,
363
378
  ~timestampCaughtUpToHeadOrEndblock,
364
379
  ~numEventsProcessed,
365
380
  ~transactionStore=TransactionStore.make(
@@ -373,33 +388,6 @@ let makeInternal = (
373
388
  )
374
389
  }
375
390
 
376
- let makeFromConfig = (
377
- chainConfig: Config.chain,
378
- ~config,
379
- ~registrationsByChainId,
380
- ~knownHeight,
381
- ) => {
382
- let logger = Logging.createChild(~params={"chainId": chainConfig.id})
383
-
384
- makeInternal(
385
- ~chainConfig,
386
- ~config,
387
- ~registrationsByChainId,
388
- ~startBlock=chainConfig.startBlock,
389
- ~endBlock=chainConfig.endBlock,
390
- ~reorgCheckpoints=[],
391
- ~maxReorgDepth=chainConfig.maxReorgDepth,
392
- ~progressBlockNumber=-1,
393
- ~timestampCaughtUpToHeadOrEndblock=None,
394
- ~numEventsProcessed=0.,
395
- ~logger,
396
- ~indexingAddresses=configAddresses(chainConfig),
397
- ~isInReorgThreshold=false,
398
- ~isRealtime=false,
399
- ~knownHeight,
400
- )
401
- }
402
-
403
391
  /**
404
392
  * This function allows a chain state to be created from metadata, in particular this is useful for restarting an indexer and making sure it fetches blocks from the same place.
405
393
  */
@@ -410,6 +398,7 @@ let makeFromDbState = (
410
398
  ~isInReorgThreshold,
411
399
  ~isRealtime,
412
400
  ~config,
401
+ ~contractMapping,
413
402
  ~registrationsByChainId,
414
403
  ~reducedPollingInterval=?,
415
404
  ) => {
@@ -423,7 +412,8 @@ let makeFromDbState = (
423
412
  : resumedChainState.startBlock - 1
424
413
 
425
414
  makeInternal(
426
- ~indexingAddresses=resumedChainState.indexingAddresses,
415
+ ~addressRows=resumedChainState.addressRows,
416
+ ~contractMapping,
427
417
  ~chainConfig,
428
418
  ~startBlock=resumedChainState.startBlock,
429
419
  ~endBlock=resumedChainState.endBlock,
@@ -507,7 +497,7 @@ let contractAddresses = (cs: t, ~contractName) =>
507
497
 
508
498
  // Hands over the dynamically registered addresses the database hasn't seen yet,
509
499
  // up to the block the caller is about to commit, each paired with the checkpoint
510
- // that owns its row. Destructive: the caller must persist them or take the
500
+ // that registered it. Destructive: the caller must persist them or take the
511
501
  // indexer down. Throws with nothing consumed when a registration's block has no
512
502
  // checkpoint in the batch.
513
503
  let drainAddressesForWrite = (cs: t, ~toBlockInclusive, ~checkpointBlockNumbers) =>
@@ -854,9 +844,9 @@ let applyTransactionGroups = async (store: TransactionStore.t, g: transactionGro
854
844
  g.payloadGroups->Array.forEachWithIndex((payloads, i) => {
855
845
  let tx = txs->Array.getUnsafe(i)
856
846
  payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
857
- switch (
858
- tx->(Utils.magic: Internal.eventTransaction => dict<unknown>)
859
- )->Dict.get("accountActivities") {
847
+ switch tx
848
+ ->(Utils.magic: Internal.eventTransaction => dict<unknown>)
849
+ ->Dict.get("accountActivities") {
860
850
  | Some(_) => payloads->Array.forEach(payload => Svm.attachAccountActivities(payload, tx))
861
851
  | None => ()
862
852
  }
@@ -1027,6 +1017,9 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
1027
1017
  startBlock: cs.fetchState.startBlock,
1028
1018
  endBlock: cs.fetchState.endBlock,
1029
1019
  numAddresses: cs.addressStore->AddressStore.size,
1020
+ addressesByContract: cs.addressStore
1021
+ ->AddressStore.contractCounts
1022
+ ->Array.map(({contractName, count}) => (contractName, count)),
1030
1023
  isReady: cs->isReady,
1031
1024
  sourceBlockNumber: cs.fetchState.knownHeight,
1032
1025
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -1217,13 +1210,30 @@ let markReady = (cs: t, ~readyAt) =>
1217
1210
  // with no diff entry still rewinds fetch state + stores to the target -
1218
1211
  // otherwise the stale block hash stays in the store and re-triggers the same
1219
1212
  // reorg.
1213
+ // Returns the address registrations the storage has to delete along with it —
1214
+ // the store is what decides which ones died, so the two halves of a rollback
1215
+ // can't disagree.
1220
1216
  let rollback = (
1221
1217
  cs: t,
1222
1218
  ~newProgressBlockNumber,
1223
1219
  ~eventsProcessedDiff,
1224
1220
  ~rollbackTargetBlockNumber,
1225
1221
  ~isReorgChain,
1226
- ) => {
1222
+ ): array<AddressRows.key> => {
1223
+ let rollbackTo = targetBlockNumber => {
1224
+ let {fetchState, rolledBackAddresses} =
1225
+ cs.fetchState->FetchState.rollback(
1226
+ ~rolledBackAddressStore=cs.addressStore,
1227
+ ~targetBlockNumber,
1228
+ )
1229
+ cs.fetchState = fetchState
1230
+ rolledBackAddresses->Array.map(({address, contractId}): AddressRows.key => {
1231
+ chainId: cs.chainConfig.id,
1232
+ address,
1233
+ contractId,
1234
+ })
1235
+ }
1236
+
1227
1237
  switch newProgressBlockNumber {
1228
1238
  | Some(newProgressBlockNumber) =>
1229
1239
  let newTotalEventsProcessed =
@@ -1243,23 +1253,16 @@ let rollback = (
1243
1253
  )
1244
1254
  | None => ()
1245
1255
  }
1246
- cs.fetchState =
1247
- cs.fetchState->FetchState.rollback(
1248
- ~addressStore=cs.addressStore,
1249
- ~targetBlockNumber=newProgressBlockNumber,
1250
- )
1256
+ let rolledBackAddresses = rollbackTo(newProgressBlockNumber)
1251
1257
  cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
1252
1258
  cs.blockStore->BlockStore.rollback(newProgressBlockNumber)
1253
1259
  cs.committedProgressBlockNumber = newProgressBlockNumber
1254
1260
  cs.processingBlockNumber = newProgressBlockNumber
1255
1261
  cs.numEventsProcessed = newTotalEventsProcessed
1262
+ rolledBackAddresses
1256
1263
  | None =>
1257
1264
  if isReorgChain {
1258
- cs.fetchState =
1259
- cs.fetchState->FetchState.rollback(
1260
- ~addressStore=cs.addressStore,
1261
- ~targetBlockNumber=rollbackTargetBlockNumber,
1262
- )
1265
+ let rolledBackAddresses = rollbackTo(rollbackTargetBlockNumber)
1263
1266
  cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
1264
1267
  cs.blockStore->BlockStore.rollback(rollbackTargetBlockNumber)
1265
1268
  cs.committedProgressBlockNumber = Pervasives.min(
@@ -1267,6 +1270,9 @@ let rollback = (
1267
1270
  rollbackTargetBlockNumber,
1268
1271
  )
1269
1272
  cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, rollbackTargetBlockNumber)
1273
+ rolledBackAddresses
1274
+ } else {
1275
+ []
1270
1276
  }
1271
1277
  }
1272
1278
  }