@xyo-network/chain-services 1.19.14 → 1.19.16

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 (43) 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 -2
  8. package/dist/neutral/index.d.ts.map +1 -1
  9. package/dist/neutral/index.mjs +32 -578
  10. package/dist/neutral/index.mjs.map +1 -1
  11. package/dist/neutral/simple/block/runner/SimpleBlockRunner.d.ts +8 -6
  12. package/dist/neutral/simple/block/runner/SimpleBlockRunner.d.ts.map +1 -1
  13. package/package.json +31 -28
  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 -2
  18. package/src/simple/block/runner/SimpleBlockRunner.ts +12 -13
  19. package/src/simple/block/runner/generateTransactionFeeTransfers.ts +1 -1
  20. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts +0 -64
  21. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts.map +0 -1
  22. package/dist/neutral/PendingTransactions/bundledPayloadToHydratedTransaction.d.ts +0 -4
  23. package/dist/neutral/PendingTransactions/bundledPayloadToHydratedTransaction.d.ts.map +0 -1
  24. package/dist/neutral/PendingTransactions/hydratedTransactionToPayloadBundle.d.ts +0 -4
  25. package/dist/neutral/PendingTransactions/hydratedTransactionToPayloadBundle.d.ts.map +0 -1
  26. package/dist/neutral/PendingTransactions/index.d.ts +0 -2
  27. package/dist/neutral/PendingTransactions/index.d.ts.map +0 -1
  28. package/dist/neutral/StakeIntent/XyoStakeIntentService.d.ts +0 -47
  29. package/dist/neutral/StakeIntent/XyoStakeIntentService.d.ts.map +0 -1
  30. package/dist/neutral/StakeIntent/index.d.ts +0 -3
  31. package/dist/neutral/StakeIntent/index.d.ts.map +0 -1
  32. package/dist/neutral/StakeIntent/lib/getBlockSignedStakeDeclarations.d.ts +0 -4
  33. package/dist/neutral/StakeIntent/lib/getBlockSignedStakeDeclarations.d.ts.map +0 -1
  34. package/dist/neutral/StakeIntent/lib/index.d.ts +0 -2
  35. package/dist/neutral/StakeIntent/lib/index.d.ts.map +0 -1
  36. package/src/PendingTransactions/BasePendingTransactions.ts +0 -382
  37. package/src/PendingTransactions/bundledPayloadToHydratedTransaction.ts +0 -14
  38. package/src/PendingTransactions/hydratedTransactionToPayloadBundle.ts +0 -18
  39. package/src/PendingTransactions/index.ts +0 -1
  40. package/src/StakeIntent/XyoStakeIntentService.ts +0 -259
  41. package/src/StakeIntent/index.ts +0 -2
  42. package/src/StakeIntent/lib/getBlockSignedStakeDeclarations.ts +0 -47
  43. package/src/StakeIntent/lib/index.ts +0 -1
@@ -1,18 +0,0 @@
1
- import type { Hash } from '@xylabs/sdk-js'
2
- import { PayloadBuilder } from '@xyo-network/payload-builder'
3
- import type { PayloadBundle } from '@xyo-network/payload-model'
4
- import { PayloadBundleSchema } from '@xyo-network/payload-model'
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'
@@ -1,259 +0,0 @@
1
- import {
2
- Address, asAddress, assertEx, filterAs, Hash,
3
- isUndefined,
4
- } from '@xylabs/sdk-js'
5
- import { ArchivistInstance, ArchivistNextOptions } from '@xyo-network/archivist-model'
6
- import {
7
- analyzeChain, ChainStakeIntentAnalyzer,
8
- isChainSummaryStakeIntent,
9
- } from '@xyo-network/chain-analyze'
10
- import {
11
- DEFAULT_FIND_FIRST_MATCHING_NEXT_OPTIONS,
12
- findFirstMatching, IntervalMap,
13
- SerializedIntervalMap,
14
- } from '@xyo-network/chain-utils'
15
- import { PayloadBuilder } from '@xyo-network/payload-builder'
16
- import { Payload, WithStorageMeta } from '@xyo-network/payload-model'
17
- import {
18
- AbstractCreatableProvider,
19
- asChainIndexingServiceStateWithStorageMeta,
20
- BlockViewer,
21
- BlockViewerMoniker,
22
- ChainIndexingServiceState, ChainIndexingServiceStateSchema, ChainStakeViewer,
23
- ChainStakeViewerMoniker,
24
- creatableProvider,
25
- isChainIndexingServiceState,
26
- readPayloadMapFromStore,
27
- StakeIntentService,
28
- timeBudget,
29
- } from '@xyo-network/xl1-sdk'
30
- import {
31
- asBlockBoundWitness, asBlockBoundWitnessWithStorageMeta, asChainStakeIntent, type Intent,
32
- } from '@xyo-network/xl1-sdk'
33
- import { Mutex } from 'async-mutex'
34
- import { LRUCache } from 'lru-cache'
35
-
36
- import { BaseServiceParams } from '../model/index.ts'
37
-
38
- export interface XyoStakeIntentServiceParams extends BaseServiceParams {
39
- blockViewer: BlockViewer
40
- chainArchivist: ArchivistInstance
41
- chainStakeViewer: ChainStakeViewer
42
- stakeIntentStateArchivist: ArchivistInstance
43
- }
44
-
45
- /**
46
- * The number of blocks to periodically persist state
47
- */
48
- // const STATE_PERSISTENCE_INTERVAL = 60n
49
-
50
- const ACTIVE_STAKE_TTL = 1000 * 60 * 60 * 2 // 2 hours in milliseconds
51
- const NO_ACTIVE_STAKE_TTL = 1000 * 2 // 2 seconds in milliseconds
52
- const STAKE_CACHE_MAX_ENTRIES = 10_000
53
-
54
- @creatableProvider()
55
- export class XyoStakeIntentService extends AbstractCreatableProvider<XyoStakeIntentServiceParams> implements StakeIntentService {
56
- static readonly defaultMoniker = 'StakeIntent'
57
- static readonly dependencies = []
58
- static readonly monikers = [XyoStakeIntentService.defaultMoniker]
59
- moniker = 'StakeIntent'
60
- // TODO: Use hash instead of block number to handle chain reorgs
61
- protected _lastIndexedBlockHash: Hash | undefined = undefined
62
- // TODO: Interval tree per declaration (bank, validator, etc.)
63
-
64
- // Ideally move to DataIntervalTree to handle declared
65
- // ranges as it enables range queries in O(min(n, k * log n)) time,
66
- // where k is the number of intervals in the output list time
67
- // Currently using set based because it's simpler, equivalent
68
- // in performance for small sets, and (most importantly) easily
69
- // persisted so we can recover state on restart.
70
- protected _producers: IntervalMap<Address> = new IntervalMap()
71
- protected _stakeCache = new LRUCache<Address, bigint>({ max: STAKE_CACHE_MAX_ENTRIES })
72
- protected _updateMutex = new Mutex()
73
-
74
- private _blockViewer?: BlockViewer
75
- private _chainStakeViewer?: ChainStakeViewer
76
-
77
- protected get blockViewer() {
78
- return this._blockViewer!
79
- }
80
-
81
- protected get chainArchivist() {
82
- return assertEx(this.params.chainArchivist!, () => 'chainArchivist not set')
83
- }
84
-
85
- protected get chainStakeViewer() {
86
- return this._chainStakeViewer!
87
- }
88
-
89
- protected get stakeIntentStateArchivist() {
90
- return assertEx(this.params.stakeIntentStateArchivist!, () => 'stakeIntentStateArchivist not set')
91
- }
92
-
93
- static override async paramsHandler(params: Partial<XyoStakeIntentServiceParams>): Promise<XyoStakeIntentServiceParams> {
94
- return {
95
- ...await super.paramsHandler(params),
96
- chainArchivist: assertEx(params?.chainArchivist, () => 'chainArchivist is required'),
97
- stakeIntentStateArchivist: assertEx(params?.stakeIntentStateArchivist, () => 'stakeIntentStateArchivist is required'),
98
- } as XyoStakeIntentServiceParams
99
- }
100
-
101
- override async createHandler() {
102
- this._blockViewer = await this.locator.getInstance(BlockViewerMoniker)
103
- this._chainStakeViewer = await this.locator.getInstance(ChainStakeViewerMoniker)
104
- const head = await this.blockViewer.currentBlock()
105
- if (isUndefined(head)) return
106
- await this.recoverState(head[0]._hash)
107
- }
108
-
109
- async getDeclaredCandidateRanges(address: Address, intent: Intent): Promise<Readonly<Readonly<[number, number]>[]>> {
110
- await Promise.resolve()
111
- assertEx(intent === 'producer', () => `Support not yet added for intent ${intent}`)
112
- const results = this._producers.get(address)
113
- return results ?? []
114
- }
115
-
116
- async getDeclaredCandidatesForBlock(block: number, intent: Intent): Promise<Address[]> {
117
- return await this.spanAsync('getDeclaredCandidatesForBlock', async () => {
118
- assertEx(intent === 'producer', () => `Support not yet added for intent ${intent}`)
119
- const results = this._producers.findAllContaining(block)
120
- const candidates = [...results]
121
- const requiredMinimumStake = this.getRequiredMinimumStakeForIntent(intent)
122
- const validCandidates = await this.filterToValidStake(candidates, this.chainStakeViewer, requiredMinimumStake)
123
- return validCandidates
124
- }, this.context)
125
- }
126
-
127
- getRequiredMinimumStakeForIntent(intent: Intent): bigint {
128
- switch (intent) {
129
- case 'producer': {
130
- const { minStake } = this.params.context.config.actors.producer
131
- return BigInt(minStake)
132
- }
133
- }
134
- }
135
-
136
- async isStakedForBlock(block: number, intent: Intent, address: Address): Promise<boolean> {
137
- const candidates = await this.getDeclaredCandidatesForBlock(block, intent)
138
- return candidates.includes(address)
139
- }
140
-
141
- override async startHandler(): Promise<void> {
142
- await this.updateIndex(true)
143
- }
144
-
145
- private async filterToValidStake(
146
- candidates: Address[],
147
- chainStakeViewer: ChainStakeViewer,
148
- requiredMinimumStake: bigint,
149
- ): Promise<Address[]> {
150
- type CandidateStake = { candidate: Address; stake: bigint }
151
-
152
- // Find the stake for each candidate
153
- const candidatesWithStake: CandidateStake[] = await Promise.all(
154
- candidates.map(async (candidate) => {
155
- // Check if the stake is already cached
156
- const stake = this._stakeCache.get(candidate)
157
- if (stake === undefined) {
158
- // Fetch from chainStakeViewer if not cached
159
- const activeStake = await chainStakeViewer.activeByStaked(candidate)
160
- if (activeStake > 0n) {
161
- // Store result in cache
162
- this._stakeCache.set(candidate, activeStake, { ttl: ACTIVE_STAKE_TTL })
163
- } else {
164
- this._stakeCache.set(candidate, activeStake, { ttl: NO_ACTIVE_STAKE_TTL })
165
- }
166
- return { candidate, stake: activeStake }
167
- } else {
168
- return { candidate, stake }
169
- }
170
- }),
171
- )
172
-
173
- // Filter out candidates whose stake is greater than or equal to the required minimum
174
- return candidatesWithStake
175
- .filter(({ stake }) => stake >= requiredMinimumStake)
176
- .map(({ candidate }) => candidate)
177
- }
178
-
179
- private async persistState(current: Hash): Promise<void> {
180
- const state = this._producers.serialize()
181
- const payload = new PayloadBuilder<ChainIndexingServiceState<SerializedIntervalMap>>({ schema: ChainIndexingServiceStateSchema })
182
- .fields({ endBlockHash: current, state })
183
- .build()
184
- await this.stakeIntentStateArchivist.insert([payload])
185
- }
186
-
187
- private async recoverState(current: Hash): Promise<void> {
188
- return await timeBudget('XyoStakeIntentService.recoverState', console, async () => {
189
- const currentBlock = assertEx(asBlockBoundWitness((await this.chainArchivist.get([current]))?.[0]), () => `Block ${current} not found`)
190
- const currentBlockNum = currentBlock.block
191
- // Find last state before current head (in case of rollback, we indexed past it on an uncle chain, etc.)
192
- const opts: ArchivistNextOptions = { ...DEFAULT_FIND_FIRST_MATCHING_NEXT_OPTIONS }
193
- while (true) {
194
- const predicate = (p: WithStorageMeta<Payload>) => {
195
- const state = asChainIndexingServiceStateWithStorageMeta(p)
196
- return state ? true : false
197
- }
198
- const state = await findFirstMatching(this.stakeIntentStateArchivist, predicate, opts)
199
- if (isChainIndexingServiceState<SerializedIntervalMap>(state)) {
200
- const indexed = (await this.chainArchivist.get([state.endBlockHash]))?.[0]
201
- const indexedBlock = asBlockBoundWitnessWithStorageMeta(indexed)
202
- if (indexedBlock) {
203
- const indexedBlockNum = indexedBlock.block
204
- if (indexedBlockNum <= currentBlockNum) {
205
- const data = state.state as SerializedIntervalMap
206
- this._producers = new IntervalMap(data)
207
- this._lastIndexedBlockHash = indexedBlock._hash
208
- break
209
- }
210
- }
211
- } else {
212
- // No state found, start from genesis
213
- break
214
- }
215
- opts.open = true
216
- }
217
- }, 2000, true)
218
- }
219
-
220
- private async updateIndex(displayProgress = false): Promise<void> {
221
- if (this._updateMutex.isLocked()) {
222
- return
223
- }
224
- await this._updateMutex.runExclusive(async () => {
225
- return await this.spanAsync('updateIndex', async () => {
226
- const currentHead = (await this.blockViewer.currentBlock())[0]
227
- if (isUndefined(currentHead)) return
228
- const currentHeadHash = currentHead._hash
229
- const chainMap = readPayloadMapFromStore<WithStorageMeta<Payload>>(this.chainArchivist)
230
- const result = await analyzeChain(
231
- { ...this.context, chainMap },
232
- [new ChainStakeIntentAnalyzer('producer')],
233
- currentHeadHash,
234
- this._lastIndexedBlockHash,
235
- )
236
- const signedDeclarations = filterAs(result.find(isChainSummaryStakeIntent)?.intents ?? [], asChainStakeIntent)
237
- if (currentHead.block === undefined) return
238
- const currentHeadBlockNum = currentHead.block
239
- if (displayProgress) this.logger?.log(`Updating index through 0x${currentHeadBlockNum}`)
240
- for (const signedDeclaration of signedDeclarations) {
241
- const { exp, nbf } = signedDeclaration
242
- const start = nbf
243
- const stop = exp
244
- const address = asAddress(signedDeclaration?.from)
245
- if (start !== undefined && stop !== undefined && address !== undefined) {
246
- this._producers.insert(address, start, stop)
247
- }
248
- }
249
- /*
250
- if (index % STATE_PERSISTENCE_INTERVAL === 0n) {
251
- if (displayProgress) this.logger?.info(`Persisting state at block ${index}`)
252
- await this.persistState(await PayloadBuilder.hash(block))
253
- }
254
- */
255
- this._lastIndexedBlockHash = currentHeadHash
256
- }, this.context)
257
- })
258
- }
259
- }
@@ -1,2 +0,0 @@
1
- export * from './lib/index.ts'
2
- export * from './XyoStakeIntentService.ts'
@@ -1,47 +0,0 @@
1
- import type { Hash } from '@xylabs/sdk-js'
2
- import { exists, filterAs } from '@xylabs/sdk-js'
3
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
4
- import type { BoundWitness } from '@xyo-network/boundwitness-model'
5
- import { isBoundWitness } from '@xyo-network/boundwitness-model'
6
- import { payloadSchemasContains } from '@xyo-network/boundwitness-validator'
7
- import { BoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
8
- import type {
9
- BlockBoundWitness, ChainStakeIntent, Intent,
10
- } from '@xyo-network/xl1-sdk'
11
- import { asChainStakeIntent, ChainStakeIntentSchema } from '@xyo-network/xl1-sdk'
12
-
13
- export const getBlockSignedStakeDeclarations = async (block: BlockBoundWitness, archivist: ArchivistInstance, intent: Intent): Promise<ChainStakeIntent[]> => {
14
- // Get payloads in block
15
- const blockData = await archivist.get(block.payload_hashes)
16
- // Filter Payloads in block to BoundWitnesses
17
- const bwsFromBlock = blockData.filter(x => isBoundWitness(x))
18
- // Filter to BoundWitnesses with StakeIntent payloads
19
- const bwsFromBlockWithDeclarations = bwsFromBlock.filter(bw => payloadSchemasContains(bw, ChainStakeIntentSchema))
20
- // Filter to only valid signed BWs
21
- const validBlockBwsWithDeclarations = await filterToValidSignedBoundWitnesses(bwsFromBlockWithDeclarations)
22
- return (await Promise.all(validBlockBwsWithDeclarations.map(async (bw) => {
23
- // Get staked intent hashes from signed declarations
24
- const stakeIntentHashes = validBlockBwsWithDeclarations
25
- .flatMap(mapBoundWitnessToStakeIntentHashes)
26
- .filter(exists)
27
- // Get staked intent payloads
28
- const payloads = await archivist.get(stakeIntentHashes)
29
- // Filter payloads to staked intents
30
- const stakeIntents = filterAs(payloads, asChainStakeIntent)
31
- // that are producers
32
- .filter(p => p.intent === intent)
33
- // where the issuer of the intent is also the signer of this BW
34
- .filter(p => bw.addresses.includes(p.from))
35
-
36
- return stakeIntents
37
- }))).flat()
38
- }
39
-
40
- const filterToValidSignedBoundWitnesses = async (bws: BoundWitness[]): Promise<BoundWitness[]> => {
41
- const validBwIndexes = await Promise.all(bws.map(bw => BoundWitnessWrapper.parse(bw).getValid()))
42
- return bws.filter((_, index) => validBwIndexes[index])
43
- }
44
-
45
- const mapBoundWitnessToStakeIntentHashes = (bw: BoundWitness): (Hash | undefined)[] => {
46
- return bw.payload_schemas.map((schema, index) => schema === ChainStakeIntentSchema ? bw.payload_hashes[index] : undefined)
47
- }
@@ -1 +0,0 @@
1
- export * from './getBlockSignedStakeDeclarations.ts'