@xyo-network/chain-services 1.19.15 → 1.19.17

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 (30) hide show
  1. package/dist/neutral/Election/BaseElectionService.d.ts +1 -1
  2. package/dist/neutral/Election/BaseElectionService.d.ts.map +1 -1
  3. package/dist/neutral/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.d.ts +1 -1
  4. package/dist/neutral/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.d.ts.map +1 -1
  5. package/dist/neutral/StepStake/BaseStepStakeService.d.ts +1 -1
  6. package/dist/neutral/StepStake/BaseStepStakeService.d.ts.map +1 -1
  7. package/dist/neutral/index.d.ts +0 -1
  8. package/dist/neutral/index.d.ts.map +1 -1
  9. package/dist/neutral/index.mjs +25 -323
  10. package/dist/neutral/index.mjs.map +1 -1
  11. package/dist/neutral/simple/block/runner/SimpleBlockRunner.d.ts +5 -6
  12. package/dist/neutral/simple/block/runner/SimpleBlockRunner.d.ts.map +1 -1
  13. package/package.json +22 -24
  14. package/src/Election/BaseElectionService.ts +1 -1
  15. package/src/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.ts +1 -1
  16. package/src/StepStake/BaseStepStakeService.ts +1 -1
  17. package/src/index.ts +0 -1
  18. package/src/simple/block/runner/SimpleBlockRunner.ts +3 -3
  19. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts +0 -64
  20. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts.map +0 -1
  21. package/dist/neutral/PendingTransactions/bundledPayloadToHydratedTransaction.d.ts +0 -4
  22. package/dist/neutral/PendingTransactions/bundledPayloadToHydratedTransaction.d.ts.map +0 -1
  23. package/dist/neutral/PendingTransactions/hydratedTransactionToPayloadBundle.d.ts +0 -4
  24. package/dist/neutral/PendingTransactions/hydratedTransactionToPayloadBundle.d.ts.map +0 -1
  25. package/dist/neutral/PendingTransactions/index.d.ts +0 -2
  26. package/dist/neutral/PendingTransactions/index.d.ts.map +0 -1
  27. package/src/PendingTransactions/BasePendingTransactions.ts +0 -382
  28. package/src/PendingTransactions/bundledPayloadToHydratedTransaction.ts +0 -14
  29. package/src/PendingTransactions/hydratedTransactionToPayloadBundle.ts +0 -18
  30. package/src/PendingTransactions/index.ts +0 -1
@@ -1,4 +0,0 @@
1
- import type { PayloadBundle } from '@xyo-network/payload-model';
2
- import type { SignedHydratedTransactionWithHashMeta } from '@xyo-network/xl1-sdk';
3
- export declare const hydratedTransactionToPayloadBundle: (transaction: SignedHydratedTransactionWithHashMeta) => PayloadBundle;
4
- //# sourceMappingURL=hydratedTransactionToPayloadBundle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydratedTransactionToPayloadBundle.d.ts","sourceRoot":"","sources":["../../../src/PendingTransactions/hydratedTransactionToPayloadBundle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAG/D,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,sBAAsB,CAAA;AAGjF,eAAO,MAAM,kCAAkC,GAAI,aAAa,qCAAqC,KAAG,aAGvG,CAAA"}
@@ -1,2 +0,0 @@
1
- export * from './BasePendingTransactions.ts';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/PendingTransactions/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA"}
@@ -1,382 +0,0 @@
1
- import { ValueType } from '@opentelemetry/api'
2
- import {
3
- assertEx,
4
- exists,
5
- filterAs, filterAsync, forget, Hash,
6
- isDefined, isUndefined,
7
- } from '@xylabs/sdk-js'
8
- import { MemoryArchivist } from '@xyo-network/archivist-memory'
9
- import { ArchivistInstance } from '@xyo-network/archivist-model'
10
- import {
11
- Payload, PayloadBundle, Sequence, WithStorageMeta,
12
- } from '@xyo-network/payload-model'
13
- import {
14
- AbstractCreatableProvider,
15
- asBlockBoundWitnessWithHashMeta, asXL1BlockNumber, ChainId, creatableProvider,
16
- findMostRecentBlock, HydratedTransactionValidationFunction, isTransactionBoundWitnessWithStorageMeta,
17
- MempoolViewer, MempoolViewerMoniker, PendingBlocksOptions, PendingTransactionsOptions,
18
- SignedHydratedBlockWithHashMeta,
19
- SignedHydratedTransactionWithHashMeta,
20
- SignedHydratedTransactionWithStorageMeta,
21
- TransactionJsonSchemaValidator, validateTransaction,
22
- XL1BlockNumber,
23
- } from '@xyo-network/xl1-sdk'
24
- import { Mutex } from 'async-mutex'
25
-
26
- import { BaseServiceParams } from '../model/index.ts'
27
- import { bundledPayloadToHydratedTransaction } from './bundledPayloadToHydratedTransaction.ts'
28
- import { hydratedTransactionToPayloadBundle } from './hydratedTransactionToPayloadBundle.ts'
29
-
30
- export interface BasePendingTransactionsServiceParams extends BaseServiceParams {
31
- additionalPendingTransactionValidators?: HydratedTransactionValidationFunction[]
32
- chainArchivist: ArchivistInstance
33
- chainId: ChainId
34
- pendingBundledTransactionsArchivist: ArchivistInstance
35
- rejectedTransactionsArchivist: ArchivistInstance
36
- }
37
-
38
- @creatableProvider()
39
- export class BasePendingTransactionsService extends AbstractCreatableProvider<BasePendingTransactionsServiceParams> implements MempoolViewer {
40
- static readonly defaultMoniker = MempoolViewerMoniker
41
- static readonly dependencies = []
42
- static readonly monikers = [MempoolViewerMoniker]
43
-
44
- private static readonly MutexPriority = {
45
- /**
46
- * Priority for inserting new transactions
47
- */
48
- InsertNewTransactions: 5,
49
- /**
50
- * Priority for reading pending transactions
51
- */
52
- ReadTransactions: 3,
53
- /**
54
- * Priority for removing finalized/expired/rejected transactions
55
- */
56
- PurgeTransactions: 1,
57
- } as const
58
-
59
- override moniker = BasePendingTransactionsService.defaultMoniker
60
-
61
- /**
62
- * A mutex to ensure that the counting the number of pending transactions is
63
- * not called concurrently
64
- */
65
- private _countPendingTransactionsMutex = new Mutex()
66
-
67
- /**
68
- * A local Archivist optimized for fast retrieval that stores only validated
69
- * pending transactions
70
- */
71
- private _curatedPendingBundledTransactionsArchivist: MemoryArchivist | undefined
72
-
73
- /**
74
- * The last count of total pending transactions
75
- */
76
- private _pendingTransactionsCount: number = 0
77
-
78
- /**
79
- * A set of transaction hashes that are pending removal from the
80
- * curated pending transactions archivist. This is used to track
81
- * which transactions need to be removed from the archivist.
82
- */
83
- private _removablePendingTransactionHashes: Set<Hash> = new Set()
84
-
85
- /**
86
- * A mutex to ensure that the curated pending transactions archivist is
87
- * updated in a thread-safe manner
88
- */
89
- private _updateCuratedPendingTransactionsArchivistMutex = new Mutex()
90
-
91
- private get additionalPendingTransactionValidators() {
92
- return this.params.additionalPendingTransactionValidators ?? []
93
- }
94
-
95
- private get chainArchivist() {
96
- return assertEx(this.params.chainArchivist, () => 'No completed blocks with data archivist')
97
- }
98
-
99
- private get pendingBundledTransactionsArchivist() {
100
- return assertEx(this.params.pendingBundledTransactionsArchivist, () => 'No pending bundled transactions archivist')
101
- }
102
-
103
- private get pendingBundledTransactionsLocalArchivist() {
104
- return assertEx(this._curatedPendingBundledTransactionsArchivist, () => 'No pending bundled transactions curated archivist')
105
- }
106
-
107
- private get pendingTransactionsCount() {
108
- forget(this.countPendingTransactions())
109
- return this._pendingTransactionsCount
110
- }
111
-
112
- private get rejectedTransactionsArchivist() {
113
- return assertEx(this.params.rejectedTransactionsArchivist, () => 'No rejected transactions archivist')
114
- }
115
-
116
- override async createHandler() {
117
- await super.createHandler()
118
- this._curatedPendingBundledTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
119
-
120
- // On new pending transactions, insert them into the curated archivist
121
- this.pendingBundledTransactionsArchivist.on('inserted', ({ payloads }) => {
122
- forget(this.insertNewTransactions(payloads as WithStorageMeta<PayloadBundle>[]))
123
- })
124
-
125
- // On new finalized blocks, remove the transactions from the curated archivist
126
- this.chainArchivist.on('inserted', ({ payloads }) => {
127
- this.markAnyIncludedTransactionsForRemoval(payloads)
128
- forget(this.cleanupWorker())
129
- })
130
-
131
- // On new rejected transactions, remove the transactions from the curated archivist
132
- this.rejectedTransactionsArchivist.on('inserted', ({ payloads }) => {
133
- this.markAnyIncludedTransactionsForRemoval(payloads)
134
- forget(this.cleanupWorker())
135
- })
136
-
137
- const pendingTransactionsCounter = this.meter?.createObservableUpDownCounter(
138
- 'xyo_pending_transactions_counter',
139
- {
140
- description: 'The current number of pending transactions', valueType: ValueType.INT, unit: '1',
141
- },
142
- )
143
- pendingTransactionsCounter?.addCallback((observer) => {
144
- observer.observe(this.pendingTransactionsCount)
145
- })
146
- }
147
-
148
- pendingBlocks(_options?: PendingBlocksOptions): Promise<SignedHydratedBlockWithHashMeta[]> {
149
- throw new Error('Method [pendingBlocks] not implemented.')
150
- }
151
-
152
- async pendingTransactions({ limit = 100 }: PendingTransactionsOptions = {}): Promise<SignedHydratedTransactionWithStorageMeta[]> {
153
- return await this.spanAsync('getPendingTransactions', async () => {
154
- // Acquires an exclusive mutex to ensure no race conditions while accessing pending transactions.
155
- return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
156
- // Find the supplied head
157
- let lastHead = (filterAs(await this.chainArchivist.next({ limit: 100 }), x => asBlockBoundWitnessWithHashMeta(x))).at(-1)
158
- if (isUndefined(lastHead)) return []
159
-
160
- await this.pruneCuratedPendingTransactionsArchivist(lastHead._hash)
161
-
162
- const foundPendingTransactions: SignedHydratedTransactionWithStorageMeta[] = []
163
- let cursor: Sequence | undefined
164
-
165
- // Continue fetching until the desired number of transactions is reached.
166
- while (foundPendingTransactions.length < limit) {
167
- // Fetch the next batch of payloads
168
- const pendingBundledTransactions = await this.pendingBundledTransactionsLocalArchivist.next({
169
- limit: 100,
170
- order: 'asc',
171
- cursor,
172
- }) as WithStorageMeta<PayloadBundle>[]
173
-
174
- // Exit if no more payloads are available.
175
- if (pendingBundledTransactions.length === 0) break
176
-
177
- // Update the cursor for the next iteration to fetch subsequent payloads.
178
- cursor = pendingBundledTransactions.at(-1)?._sequence
179
-
180
- // Keep only those payloads that are not marked for deletion.
181
- const undeletedTransactionBundles = pendingBundledTransactions.filter(tx =>
182
- !this._removablePendingTransactionHashes.has(tx.root))
183
-
184
- // Convert each undeleted payload bundle into a hydrated transaction.
185
- const transactions = (await Promise.all(
186
- undeletedTransactionBundles.map(p => bundledPayloadToHydratedTransaction(p)),
187
- )).filter(exists)
188
-
189
- // Filter transactions to only include those that are active for the next
190
- // potential block based on the last supplied head.
191
- const activeTransactions = transactions.filter(isTransactionActive(asXL1BlockNumber(lastHead.block + 1, true)))
192
-
193
- const txValidationResults = await Promise.all(activeTransactions.map(async tx => ([tx, await validateTransaction(
194
- { ...this.context, chainId: this.params.chainId },
195
- tx,
196
- this.additionalPendingTransactionValidators,
197
- )])))
198
-
199
- const validTransactions = txValidationResults.filter((
200
- [, errors],
201
- ) => errors.length === 0).map(([tx]) => tx) as SignedHydratedTransactionWithStorageMeta[]
202
-
203
- const invalidTransactions = txValidationResults.filter((
204
- [, errors],
205
- ) => errors.length > 0).map(([tx]) => tx) as SignedHydratedTransactionWithStorageMeta[]
206
-
207
- if (invalidTransactions.length > 0) {
208
- this.logger?.warn(`getPendingTransactions: Found ${invalidTransactions.length} invalid pending transactions`)
209
- for (const tx of invalidTransactions) {
210
- this.logger?.warn(tx[0]._hash)
211
- }
212
- }
213
-
214
- // Add the valid hydrated transactions to the result set.
215
- foundPendingTransactions.push(...validTransactions)
216
- }
217
-
218
- if (foundPendingTransactions.length > 0) {
219
- this.logger?.log(`getPendingTransactions: Found ${foundPendingTransactions.length} pending transactions`)
220
- for (const tx of foundPendingTransactions) {
221
- this.logger?.log(tx[0]._hash)
222
- }
223
- }
224
-
225
- return foundPendingTransactions
226
- }, BasePendingTransactionsService.MutexPriority.ReadTransactions)
227
- }, this.context)
228
- }
229
-
230
- private async cleanupWorker() {
231
- return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
232
- const lastHead = await findMostRecentBlock(this.chainArchivist)
233
- if (isDefined(lastHead)) await this.pruneCuratedPendingTransactionsArchivist(lastHead._hash)
234
- }, BasePendingTransactionsService.MutexPriority.PurgeTransactions)
235
- }
236
-
237
- private async countPendingTransactions() {
238
- if (this._countPendingTransactionsMutex.isLocked()) return
239
- await this._countPendingTransactionsMutex.runExclusive(async () => {
240
- const payloads = (await this._curatedPendingBundledTransactionsArchivist?.all()) ?? []
241
- this._pendingTransactionsCount = payloads.length
242
- })
243
- }
244
-
245
- private async filterAlreadyFinalizedTransactions(
246
- incomingTransactions: WithStorageMeta<PayloadBundle>[],
247
- ): Promise<WithStorageMeta<PayloadBundle>[]> {
248
- const incomingTransactionHashes = incomingTransactions.map(payload => payload.root)
249
- const finalizedTransactions = await this.chainArchivist.get(incomingTransactionHashes)
250
- const finalizedTransactionHashes = new Set(finalizedTransactions.map(item => item._hash))
251
- const nonFinalizedTransactions = incomingTransactions.filter(item => !finalizedTransactionHashes.has(item._hash))
252
- return nonFinalizedTransactions
253
- }
254
-
255
- private async insertNewTransactions(payloads: WithStorageMeta<PayloadBundle>[]) {
256
- if (payloads.length === 0) return
257
- return await this.spanAsync('InsertNewTransactions', async () => {
258
- return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
259
- // Check incoming transactions against finalized transactions
260
- const unprocessedTransactions = await this.filterAlreadyFinalizedTransactions(payloads)
261
- // Hydrate all unprocessed transactions
262
- const hydratedUnprocessedTransactions = (await Promise.all(unprocessedTransactions.map(async (tx) => {
263
- return await bundledPayloadToHydratedTransaction(tx)
264
- }))).filter(exists)
265
- // Filter to only valid transactions
266
- const validTransactions = await filterAsync(hydratedUnprocessedTransactions, async (tx) => {
267
- const errors = await validateTransaction({ ...this.context, chainId: this.params.chainId }, tx, [TransactionJsonSchemaValidator])
268
- if (errors.length > 0) {
269
- this.logger?.warn('validateTransaction', errors)
270
- }
271
- return errors.length > 0 ? false : true
272
- })
273
- if (validTransactions.length > 0) {
274
- const bundledTransactions = validTransactions.map(tx => hydratedTransactionToPayloadBundle(tx))
275
- await this.pendingBundledTransactionsLocalArchivist.insert(bundledTransactions)
276
- }
277
- }, BasePendingTransactionsService.MutexPriority.InsertNewTransactions)
278
- }, this.context)
279
- }
280
-
281
- /**
282
- * Marks any included transactions in the provided payloads for removal preventing them
283
- * from being included in the curated pending transactions archivist and from being offered
284
- * during the next retrieval of pending transactions.
285
- * @param payloads An array of payloads that may contain transactions.
286
- */
287
- private markAnyIncludedTransactionsForRemoval(payloads: WithStorageMeta<Payload>[]) {
288
- const hashes = payloads.filter(isTransactionBoundWitnessWithStorageMeta).map(p => p._hash)
289
- for (const hash of hashes) {
290
- this._removablePendingTransactionHashes.add(hash)
291
- }
292
- }
293
-
294
- private async pruneCuratedPendingTransactionsArchivist(head: Hash) {
295
- return await this.spanAsync('pruneCuratedPendingTransactionsArchivist', async () => {
296
- const foundPendingTransactionsToDeleteHashes: Hash[] = []
297
-
298
- let cursor: Sequence | undefined
299
- let [lastHead] = filterAs(await this.chainArchivist.get([head]), x => asBlockBoundWitnessWithHashMeta(x))
300
-
301
- // Continue fetching until the desired number of transactions is reached.
302
- while (isDefined(lastHead)) {
303
- // Fetch the next batch of payloads
304
- const pendingBundledTransactions = await this.pendingBundledTransactionsLocalArchivist.next({
305
- limit: 100,
306
- order: 'asc',
307
- cursor,
308
- }) as WithStorageMeta<PayloadBundle>[]
309
-
310
- // Exit if no more payloads are available.
311
- if (pendingBundledTransactions.length === 0) {
312
- break
313
- }
314
-
315
- // Update the cursor for the next iteration to fetch subsequent payloads.
316
- cursor = pendingBundledTransactions.at(-1)?._sequence
317
-
318
- // Filter out bundles that are marked as removable.
319
- const deletedTransactionBundles = pendingBundledTransactions.filter(tx =>
320
- this._removablePendingTransactionHashes.has(tx.root))
321
-
322
- // Queue the hashes of deletable transactions for deletion and cleanup.
323
- foundPendingTransactionsToDeleteHashes.push(
324
- ...deletedTransactionBundles.map(tx => tx._hash).filter(exists),
325
- )
326
-
327
- // Keep only those payloads that are not marked for deletion.
328
- const undeletedTransactionBundles = pendingBundledTransactions.filter(tx =>
329
- !this._removablePendingTransactionHashes.has(tx.root))
330
-
331
- // Convert each undeleted payload bundle into a hydrated transaction.
332
- const transactions = (await Promise.all(
333
- undeletedTransactionBundles.map(p => bundledPayloadToHydratedTransaction(p)),
334
- )).filter(exists)
335
-
336
- // Find expired transactions based on the last supplied head
337
- const expiredTransactions = transactions.filter(isTransactionExpired(asXL1BlockNumber(lastHead.block + 1, true)))
338
- // Find the corresponding bundle hashes for the expired transactions
339
- const expiredBundleHashes = expiredTransactions
340
- .map(expiredHydratedTx =>
341
- // Find the corresponding payload bundle hash for the expired transaction
342
- pendingBundledTransactions.find(bundledTx => bundledTx.root === expiredHydratedTx[0]._hash)?._hash)
343
- .filter(exists)
344
- // Mark all expired bundled transactions for deletion.
345
- foundPendingTransactionsToDeleteHashes.push(...expiredBundleHashes)
346
- }
347
-
348
- // Actually delete the marked payload bundles from the archivist
349
- const deletedHashes = await this.pendingBundledTransactionsLocalArchivist.delete(foundPendingTransactionsToDeleteHashes)
350
-
351
- // Remove all deleted hashes from the "pending delete" set now that they are deleted
352
- for (const payload of deletedHashes) {
353
- this._removablePendingTransactionHashes.delete(payload._hash)
354
- }
355
-
356
- if (deletedHashes.length > 0) {
357
- this.logger?.log(`foundPendingTransactionsToDeleteHashes: Found ${deletedHashes.length} deletable transactions`)
358
- for (const payload of deletedHashes) {
359
- this.logger?.log(payload._hash)
360
- }
361
- }
362
- }, this.context)
363
- }
364
- }
365
-
366
- /**
367
- * Checks if a transaction is expired for a given block.
368
- * @param block The block number to check against the transaction's validity period.
369
- * @returns True if the transaction is expired for the given block, false otherwise.
370
- */
371
- const isTransactionExpired = (block: XL1BlockNumber) =>
372
- ([txBw]: SignedHydratedTransactionWithHashMeta): boolean =>
373
- txBw.exp < block
374
-
375
- /**
376
- * Checks if a transaction is active for a given block.
377
- * @param block The block number to check against the transaction's validity period.
378
- * @returns True if the transaction is active for the given block, false otherwise.
379
- */
380
- const isTransactionActive = (block: XL1BlockNumber) =>
381
- ([txBw]: SignedHydratedTransactionWithHashMeta): boolean =>
382
- txBw.nbf <= block && txBw.exp >= block
@@ -1,14 +0,0 @@
1
- import type { PayloadBundle, WithStorageMeta } from '@xyo-network/payload-model'
2
- import { PayloadBuilder } from '@xyo-network/sdk-js'
3
- import type { SignedHydratedTransactionWithHashMeta } from '@xyo-network/xl1-sdk'
4
- import { asSignedTransactionBoundWitnessWithHashMeta } from '@xyo-network/xl1-sdk'
5
-
6
- export const bundledPayloadToHydratedTransaction = async (
7
- payload: WithStorageMeta<PayloadBundle>,
8
- ): Promise<SignedHydratedTransactionWithHashMeta | undefined> => {
9
- const withHashMeta = await PayloadBuilder.addHashMeta(payload.payloads)
10
- const tx = asSignedTransactionBoundWitnessWithHashMeta(withHashMeta.find(p => p._hash === payload.root))
11
- if (tx) {
12
- return [tx, withHashMeta.filter(p => p._hash !== payload.root)]
13
- }
14
- }
@@ -1,18 +0,0 @@
1
- import type { Hash } from '@xylabs/sdk-js'
2
- import type { PayloadBundle } from '@xyo-network/payload-model'
3
- import { PayloadBundleSchema } from '@xyo-network/payload-model'
4
- import { PayloadBuilder } from '@xyo-network/sdk-js'
5
- import type { SignedHydratedTransactionWithHashMeta } from '@xyo-network/xl1-sdk'
6
- import { flattenHydratedTransaction } from '@xyo-network/xl1-sdk'
7
-
8
- export const hydratedTransactionToPayloadBundle = (transaction: SignedHydratedTransactionWithHashMeta): PayloadBundle => {
9
- const root = transaction[0]._hash
10
- return bundle(root, transaction)
11
- }
12
-
13
- const bundle = (root: Hash, transaction: SignedHydratedTransactionWithHashMeta) => {
14
- const payloads = flattenHydratedTransaction(transaction).flatMap(p => PayloadBuilder.omitStorageMeta(p))
15
- return new PayloadBuilder<PayloadBundle>({ schema: PayloadBundleSchema })
16
- .fields({ payloads, root })
17
- .build()
18
- }
@@ -1 +0,0 @@
1
- export * from './BasePendingTransactions.ts'