@xyo-network/chain-services 1.16.15 → 1.16.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 (19) hide show
  1. package/package.json +13 -10
  2. package/dist/neutral/BlockProducer/spec/BaseBlockProducerService.spec.d.ts +0 -2
  3. package/dist/neutral/BlockProducer/spec/BaseBlockProducerService.spec.d.ts.map +0 -1
  4. package/dist/neutral/BlockProducer/spec/generateTransactionTransfer.spec.d.ts +0 -2
  5. package/dist/neutral/BlockProducer/spec/generateTransactionTransfer.spec.d.ts.map +0 -1
  6. package/dist/neutral/BlockReward/spec/MemoryBlockRewardService.spec.d.ts +0 -2
  7. package/dist/neutral/BlockReward/spec/MemoryBlockRewardService.spec.d.ts.map +0 -1
  8. package/dist/neutral/PendingTransactions/spec/BasePendingTransactions.spec.d.ts +0 -2
  9. package/dist/neutral/PendingTransactions/spec/BasePendingTransactions.spec.d.ts.map +0 -1
  10. package/dist/neutral/PendingTransactions/spec/bundledPayloadToHydratedTransaction.spec.d.ts +0 -2
  11. package/dist/neutral/PendingTransactions/spec/bundledPayloadToHydratedTransaction.spec.d.ts.map +0 -1
  12. package/dist/neutral/PendingTransactions/spec/hydratedTransactionToPayloadBundle.spec.d.ts +0 -2
  13. package/dist/neutral/PendingTransactions/spec/hydratedTransactionToPayloadBundle.spec.d.ts.map +0 -1
  14. package/src/BlockProducer/spec/BaseBlockProducerService.spec.ts +0 -391
  15. package/src/BlockProducer/spec/generateTransactionTransfer.spec.ts +0 -75
  16. package/src/BlockReward/spec/MemoryBlockRewardService.spec.ts +0 -64
  17. package/src/PendingTransactions/spec/BasePendingTransactions.spec.ts +0 -276
  18. package/src/PendingTransactions/spec/bundledPayloadToHydratedTransaction.spec.ts +0 -35
  19. package/src/PendingTransactions/spec/hydratedTransactionToPayloadBundle.spec.ts +0 -28
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/package.json",
3
3
  "name": "@xyo-network/chain-services",
4
- "version": "1.16.15",
4
+ "version": "1.16.17",
5
5
  "description": "XYO Layer One SDK Services",
6
6
  "homepage": "https://xylabs.com",
7
7
  "bugs": {
@@ -33,7 +33,10 @@
33
33
  "types": "./dist/neutral/index.d.ts",
34
34
  "files": [
35
35
  "dist",
36
- "src"
36
+ "src",
37
+ "!**/*.bench.*",
38
+ "!**/*.spec.*",
39
+ "!**/*.test.*"
37
40
  ],
38
41
  "dependencies": {
39
42
  "@opentelemetry/api": "~1.9.0",
@@ -45,17 +48,17 @@
45
48
  "@xyo-network/boundwitness-model": "~5.1.23",
46
49
  "@xyo-network/boundwitness-validator": "~5.1.23",
47
50
  "@xyo-network/boundwitness-wrapper": "~5.1.23",
48
- "@xyo-network/chain-analyze": "~1.16.15",
49
- "@xyo-network/chain-modules": "~1.16.15",
50
- "@xyo-network/chain-protocol": "~1.16.15",
51
- "@xyo-network/chain-utils": "~1.16.15",
51
+ "@xyo-network/chain-analyze": "~1.16.17",
52
+ "@xyo-network/chain-modules": "~1.16.17",
53
+ "@xyo-network/chain-protocol": "~1.16.17",
54
+ "@xyo-network/chain-utils": "~1.16.17",
52
55
  "@xyo-network/payload-builder": "~5.1.23",
53
56
  "@xyo-network/payload-model": "~5.1.23",
54
57
  "@xyo-network/typechain": "~4.0.10",
55
58
  "@xyo-network/xl1-protocol": "~1.13.11",
56
- "@xyo-network/xl1-protocol-sdk": "~1.16.15",
57
- "@xyo-network/xl1-validation": "~1.16.15",
58
- "@xyo-network/xl1-wrappers": "~1.16.15",
59
+ "@xyo-network/xl1-protocol-sdk": "~1.16.17",
60
+ "@xyo-network/xl1-validation": "~1.16.17",
61
+ "@xyo-network/xl1-wrappers": "~1.16.17",
59
62
  "async-mutex": "~0.5.0",
60
63
  "ethers": "6.15.0",
61
64
  "lru-cache": "~11.2.2"
@@ -68,7 +71,7 @@
68
71
  "@xylabs/vitest-extended": "~5.0.34",
69
72
  "@xyo-network/account": "~5.1.23",
70
73
  "@xyo-network/account-model": "~5.1.23",
71
- "@xyo-network/chain-validation": "~1.16.15",
74
+ "@xyo-network/chain-validation": "~1.16.17",
72
75
  "@xyo-network/wallet": "~5.1.23",
73
76
  "eslint": "^9.39.1",
74
77
  "tslib": "~2.8.1",
@@ -1,2 +0,0 @@
1
- import '@xylabs/vitest-extended';
2
- //# sourceMappingURL=BaseBlockProducerService.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BaseBlockProducerService.spec.d.ts","sourceRoot":"","sources":["../../../../src/BlockProducer/spec/BaseBlockProducerService.spec.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generateTransactionTransfer.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateTransactionTransfer.spec.d.ts","sourceRoot":"","sources":["../../../../src/BlockProducer/spec/generateTransactionTransfer.spec.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- import '@xylabs/vitest-extended';
2
- //# sourceMappingURL=MemoryBlockRewardService.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MemoryBlockRewardService.spec.d.ts","sourceRoot":"","sources":["../../../../src/BlockReward/spec/MemoryBlockRewardService.spec.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=BasePendingTransactions.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BasePendingTransactions.spec.d.ts","sourceRoot":"","sources":["../../../../src/PendingTransactions/spec/BasePendingTransactions.spec.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=bundledPayloadToHydratedTransaction.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundledPayloadToHydratedTransaction.spec.d.ts","sourceRoot":"","sources":["../../../../src/PendingTransactions/spec/bundledPayloadToHydratedTransaction.spec.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=hydratedTransactionToPayloadBundle.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydratedTransactionToPayloadBundle.spec.d.ts","sourceRoot":"","sources":["../../../../src/PendingTransactions/spec/hydratedTransactionToPayloadBundle.spec.ts"],"names":[],"mappings":""}
@@ -1,391 +0,0 @@
1
- import '@xylabs/vitest-extended'
2
-
3
- import type {
4
- Address, CreatableName, Hash, Promisable,
5
- } from '@xylabs/sdk-js'
6
- import {
7
- asAddress, assertEx,
8
- delay,
9
- filterAs,
10
- hexToBigInt, ZERO_HASH,
11
- } from '@xylabs/sdk-js'
12
- import { Account } from '@xyo-network/account'
13
- import type { AccountInstance } from '@xyo-network/account-model'
14
- import { MemoryArchivist } from '@xyo-network/archivist-memory'
15
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
16
- import { buildRandomChain, buildRandomTransaction } from '@xyo-network/chain-protocol'
17
- import { validateHydratedBlockState } from '@xyo-network/chain-validation'
18
- import {
19
- type PayloadBundle, PayloadBundleSchema, type WithStorageMeta,
20
- } from '@xyo-network/payload-model'
21
- import { HDWallet } from '@xyo-network/wallet'
22
- import type {
23
- BlockBoundWitness, ChainId,
24
- HydratedBlock,
25
- TimeDomain, TimePayload,
26
- } from '@xyo-network/xl1-protocol'
27
- import {
28
- asBlockBoundWitness,
29
- asChainStakeIntent,
30
- asTransactionBoundWitness,
31
- asTransfer,
32
- TimeSchema,
33
- XYO_ZERO_ADDRESS,
34
- } from '@xyo-network/xl1-protocol'
35
- import type {
36
- BlockRewardService, Config, ElectionService, HydratedBlockStateValidationFunction, StakeIntentService,
37
- TimeSyncViewer,
38
- } from '@xyo-network/xl1-protocol-sdk'
39
- import {
40
- flattenHydratedBlock, flattenHydratedTransaction, getDefaultConfig,
41
- HydratedBlockStateValidationError,
42
- } from '@xyo-network/xl1-protocol-sdk'
43
- import {
44
- beforeAll, beforeEach, describe, expect, it,
45
- } from 'vitest'
46
- import { mock } from 'vitest-mock-extended'
47
-
48
- import { accountBalancesServiceFromArchivist } from '../../AccountBalance/index.ts'
49
- import { MemoryBlockRewardService } from '../../BlockReward/index.ts'
50
- import type { BasePendingTransactionsServiceParams } from '../../PendingTransactions/index.ts'
51
- import { BasePendingTransactionsService } from '../../PendingTransactions/index.ts'
52
- import type { BaseBlockProducerServiceParams } from '../BaseBlockProducerService.ts'
53
- import { BaseBlockProducerService } from '../BaseBlockProducerService.ts'
54
-
55
- describe('XyoBlockProducer', () => {
56
- const leaderCount = 3
57
- let account: AccountInstance
58
- let blockProducer: BaseBlockProducerService
59
- let chainArchivist: ArchivistInstance
60
- let electionService: ReturnType<typeof mock<ElectionService>>
61
- let pendingBundledTransactionsArchivist: ArchivistInstance
62
- let pendingTransactionsService: BasePendingTransactionsService
63
- let rejectedTransactionsArchivist: ArchivistInstance
64
- let rewardAddress = '1111111111111111111111111111111111111111' as Address
65
- let rewardService: BlockRewardService
66
- let stakeIntentService: ReturnType<typeof mock<StakeIntentService>>
67
- let time: TimeSyncViewer
68
- let transactionAccount: AccountInstance
69
- const validPendingTransactions = Math.ceil(BaseBlockProducerService.DefaultBlockSize)
70
- let config: Config
71
-
72
- let currentBlock: WithStorageMeta<BlockBoundWitness>
73
-
74
- const chainId = assertEx(asAddress('Be17531fec6fEc55f3EAc3f1c187c87e4C47F81E'))
75
-
76
- beforeAll(async () => {
77
- account = await Account.random()
78
- transactionAccount = await HDWallet.fromPhrase('room maximum palace fragile man pyramid school indoor base business want bronze assume marble report')
79
- })
80
-
81
- beforeEach(async () => {
82
- config = getDefaultConfig()
83
- config.producer.disableIntentRedeclaration = false
84
- chainArchivist = await MemoryArchivist.create({ account: 'random' })
85
- rejectedTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
86
- electionService = mock<ElectionService>()
87
- pendingBundledTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
88
- const BasePendingTransactionsServiceParams: BasePendingTransactionsServiceParams = {
89
- name: 'TestBasePendingTransactionsServiceParams' as CreatableName,
90
- chainArchivist: chainArchivist,
91
- chainId,
92
- config,
93
- pendingBundledTransactionsArchivist,
94
- rejectedTransactionsArchivist,
95
- }
96
- pendingTransactionsService = await BasePendingTransactionsService.create(BasePendingTransactionsServiceParams)
97
- await addPendingTransactions(validPendingTransactions)
98
-
99
- stakeIntentService = mock<StakeIntentService>()
100
- stakeIntentService.getDeclaredCandidateRanges.mockResolvedValue([])
101
- time = {
102
- currentTimeAndHash(domain: TimeDomain): Promisable<[number, Hash | null]> {
103
- switch (domain) {
104
- case 'epoch': {
105
- return [Date.now(), null]
106
- }
107
- case 'xl1': {
108
- return [1, '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff' as Hash]
109
- }
110
- case 'ethereum': {
111
- return [1, '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff' as Hash]
112
- }
113
- // No default
114
- }
115
- },
116
- currentTime(domain: TimeDomain): Promisable<[string, number]> {
117
- switch (domain) {
118
- case 'epoch': {
119
- return ['epoch', Date.now()]
120
- }
121
- case 'xl1': {
122
- return ['xl1', 1]
123
- }
124
- case 'ethereum': {
125
- return ['ethereum', 1]
126
- }
127
- // No default
128
- }
129
- },
130
- currentTimePayload(): Promisable<TimePayload> {
131
- return {
132
- schema: TimeSchema, epoch: Date.now(), xl1: 1,
133
- }
134
- },
135
- /** Convert time between different domains */
136
- convertTime(fromDomain: TimeDomain, toDomain: TimeDomain, from: number): Promisable<number> {
137
- if (fromDomain === toDomain) {
138
- return from
139
- }
140
- if (fromDomain === 'epoch' && toDomain === 'xl1') {
141
- return 1
142
- }
143
- if (fromDomain === 'xl1' && toDomain === 'epoch') {
144
- return Date.now()
145
- }
146
- return from
147
- },
148
- }
149
- rewardService = await MemoryBlockRewardService.create()
150
- const balanceService = await accountBalancesServiceFromArchivist(chainId, chainArchivist)
151
- const params: BaseBlockProducerServiceParams = {
152
- name: 'TestXyoBlockProducerParams' as CreatableName,
153
- account,
154
- balanceService,
155
- chainArchivist,
156
- chainId,
157
- config,
158
- electionService,
159
- pendingTransactionsService,
160
- pendingBundledTransactionsArchivist,
161
- rejectedTransactionsArchivist,
162
- rewardAddress,
163
- rewardService,
164
- stakeIntentService,
165
- time,
166
- validateHydratedBlockState,
167
- }
168
- blockProducer = await BaseBlockProducerService.create(params)
169
- })
170
-
171
- const addPendingTransactions = async (count: number = validPendingTransactions) => {
172
- for (let i = 0; i < count; i++) {
173
- const transaction = await buildRandomTransaction(chainId, [], transactionAccount)
174
- const bundledPayload: PayloadBundle = {
175
- schema: PayloadBundleSchema, payloads: flattenHydratedTransaction(transaction), root: transaction[0]._hash,
176
- }
177
- await pendingBundledTransactionsArchivist.insert([bundledPayload])
178
- }
179
- }
180
-
181
- describe('next', () => {
182
- describe('block production', () => {
183
- describe('when not current block leader', () => {
184
- beforeEach(async () => {
185
- const [hydratedBlock] = await buildRandomChain(account, 1, undefined, chainId, transactionAccount)
186
- currentBlock = hydratedBlock[0]
187
- await chainArchivist.insert(flattenHydratedBlock(hydratedBlock))
188
- const leaders = await Promise.all(Array.from({ length: leaderCount }, async () => (await Account.random()).address))
189
- electionService.getCreatorCommitteeForNextBlock.mockResolvedValue(leaders)
190
- stakeIntentService.getDeclaredCandidateRanges.mockResolvedValue([])
191
- })
192
- it('should return undefined', async () => {
193
- // Arrange
194
-
195
- // Act
196
- const result = await blockProducer.next(currentBlock)
197
-
198
- // Assert
199
- expect(result).toBeUndefined()
200
- })
201
- })
202
- describe('when current block leader', () => {
203
- beforeEach(async () => {
204
- const [hydratedBlock] = await buildRandomChain(account, 1, undefined, chainId, transactionAccount)
205
- currentBlock = hydratedBlock[0]
206
- await chainArchivist.insert(flattenHydratedBlock(hydratedBlock))
207
- await delay(1000)
208
- electionService.getCreatorCommitteeForNextBlock.mockResolvedValue([account.address])
209
- })
210
- it('should return a valid block', async () => {
211
- // Arrange
212
-
213
- // Act
214
- const result = await blockProducer.next(currentBlock)
215
-
216
- // Assert
217
- expect(result).toBeDefined()
218
- expect(result).toBeArrayOfSize(2)
219
- const [block, transactionsAndData] = result ?? []
220
- expect(block).toBeDefined()
221
- expect(asBlockBoundWitness(block)).toBeDefined()
222
- expect(transactionsAndData).toBeDefined()
223
- expect(transactionsAndData).toBeArray()
224
- const transactions = filterAs(assertEx(transactionsAndData), asTransactionBoundWitness)
225
- expect(transactions).toBeArrayOfSize(validPendingTransactions)
226
- })
227
- it('should remove invalid transactions', async () => {
228
- // Arrange
229
- let rejectBlock = true
230
- const validateHydratedBlockState: HydratedBlockStateValidationFunction = async (
231
- hydratedBlock: HydratedBlock,
232
- chainId: ChainId,
233
- ) => {
234
- return rejectBlock ? [await Promise.resolve(new HydratedBlockStateValidationError(ZERO_HASH, chainId, hydratedBlock, 'Invalid block'))] : []
235
- }
236
- const balanceService = await accountBalancesServiceFromArchivist(chainId, chainArchivist)
237
- const params: BaseBlockProducerServiceParams = {
238
- name: 'TestXyoBlockProducerParams' as CreatableName,
239
- account,
240
- balanceService,
241
- chainArchivist,
242
- chainId,
243
- config,
244
- electionService,
245
- pendingBundledTransactionsArchivist,
246
- pendingTransactionsService,
247
- rejectedTransactionsArchivist,
248
- rewardAddress,
249
- rewardService,
250
- stakeIntentService,
251
- time,
252
- validateHydratedBlockState,
253
- }
254
- blockProducer = await BaseBlockProducerService.create(params)
255
-
256
- // Act
257
- // Force producer to reject first block
258
- rejectBlock = true
259
- const invalidTransactionCount = Math.floor(validPendingTransactions / 2)
260
- await addPendingTransactions(invalidTransactionCount)
261
-
262
- // Ensure bad block is not produced
263
- const firstResult = await blockProducer.next(currentBlock)
264
- expect(firstResult).toBeUndefined()
265
- // Do not reject second block
266
- rejectBlock = false
267
- // Add more pending transactions
268
- const newTransactionCount = Math.floor(validPendingTransactions / 2)
269
- await addPendingTransactions(newTransactionCount)
270
- // Ensure producer recovers from bad block
271
- const result = await blockProducer.next(currentBlock)
272
-
273
- // Assert
274
- expect(result).toBeDefined()
275
- expect(result).toBeArrayOfSize(2)
276
- const [block, transactionsAndData] = result ?? []
277
- expect(block).toBeDefined()
278
- if (block) {
279
- expect(asBlockBoundWitness(block)).toBeDefined()
280
- expect(transactionsAndData).toBeDefined()
281
- expect(transactionsAndData).toBeArray()
282
- const transactions = filterAs(assertEx(transactionsAndData), asTransactionBoundWitness)
283
- expect(transactions).toBeArrayOfSize(newTransactionCount)
284
- const transfers = filterAs(assertEx(transactionsAndData), asTransfer)
285
- const blockRewardTransfer = transfers.find(transfer => transfer.from === XYO_ZERO_ADDRESS)
286
- expect(blockRewardTransfer).toBeDefined()
287
- let totalTransfer = 0n
288
- for (const value of Object.values(blockRewardTransfer?.transfers ?? {})) {
289
- const bigIntValue = hexToBigInt(value)
290
- totalTransfer += bigIntValue
291
- }
292
- expect(totalTransfer).toEqual(3_000_000_000_000_000_000_000n)
293
- }
294
- })
295
- })
296
- })
297
- describe('producer re-declare intent', () => {
298
- describe('when within re-declare intent window', () => {
299
- beforeEach(async () => {
300
- const [hydratedBlock] = await buildRandomChain(account, 1, undefined, chainId, transactionAccount)
301
- currentBlock = hydratedBlock[0]
302
- await chainArchivist.insert(flattenHydratedBlock(hydratedBlock))
303
- electionService.getCreatorCommitteeForNextBlock.mockResolvedValue([account.address])
304
- stakeIntentService.getDeclaredCandidateRanges.mockResolvedValue([[0, 10]])
305
- })
306
- it('should re-declare intent if configured for re-declaration', async () => {
307
- // Arrange
308
- config.producer.disableIntentRedeclaration = false
309
-
310
- // Act
311
- const result = await blockProducer.next(currentBlock)
312
-
313
- // Assert
314
- expect(result).toBeDefined()
315
- expect(result).toBeArrayOfSize(2)
316
- const [block, payloads] = result ?? []
317
- expect(block).toBeDefined()
318
- expect(asBlockBoundWitness(block)).toBeDefined()
319
- expect(payloads).toBeArray()
320
- const allData = flattenHydratedBlock(assertEx(result))
321
- const declaration = filterAs(allData, asChainStakeIntent).at(0)
322
- expect(declaration).toBeDefined()
323
- expect(declaration?.from).toEqual(blockProducer.address)
324
- })
325
- it('should not re-declare intent if not configured for re-declaration', async () => {
326
- // Arrange
327
- config.producer.disableIntentRedeclaration = true
328
-
329
- // Act
330
- const result = await blockProducer.next(currentBlock)
331
-
332
- // Assert
333
- expect(result).toBeDefined()
334
- expect(result).toBeArrayOfSize(2)
335
- const [block, payloads] = result ?? []
336
- expect(block).toBeDefined()
337
- expect(asBlockBoundWitness(block)).toBeDefined()
338
- expect(payloads).toBeArray()
339
- const allData = flattenHydratedBlock(assertEx(result))
340
- const declaration = filterAs(allData, asChainStakeIntent).at(0)
341
- expect(declaration).toBeUndefined()
342
- })
343
- })
344
- describe('when not within re-declare intent window', () => {
345
- beforeEach(async () => {
346
- const [hydratedBlock] = await buildRandomChain(account, 1, undefined, chainId, transactionAccount)
347
- currentBlock = hydratedBlock[0]
348
- await chainArchivist.insert(flattenHydratedBlock(hydratedBlock))
349
- electionService.getCreatorCommitteeForNextBlock.mockResolvedValue([account.address])
350
- stakeIntentService.getDeclaredCandidateRanges.mockResolvedValue([[0, 1_000_000_000]])
351
- })
352
- it('should not re-declare intent if configured for re-declaration', async () => {
353
- // Arrange
354
- config.producer.disableIntentRedeclaration = false
355
-
356
- // Act
357
- const result = await blockProducer.next(currentBlock)
358
-
359
- // Assert
360
- expect(result).toBeDefined()
361
- expect(result).toBeArrayOfSize(2)
362
- const [block, payloads] = result ?? []
363
- expect(block).toBeDefined()
364
- expect(asBlockBoundWitness(block)).toBeDefined()
365
- expect(payloads).toBeArray()
366
- const allData = flattenHydratedBlock(assertEx(result))
367
- const declaration = filterAs(allData, asChainStakeIntent).at(0)
368
- expect(declaration).toBeUndefined()
369
- })
370
- it('should not re-declare intent if not configured for re-declaration', async () => {
371
- // Arrange
372
- config.producer.disableIntentRedeclaration = true
373
-
374
- // Act
375
- const result = await blockProducer.next(currentBlock)
376
-
377
- // Assert
378
- expect(result).toBeDefined()
379
- expect(result).toBeArrayOfSize(2)
380
- const [block, payloads] = result ?? []
381
- expect(block).toBeDefined()
382
- expect(asBlockBoundWitness(block)).toBeDefined()
383
- expect(payloads).toBeArray()
384
- const allData = flattenHydratedBlock(assertEx(result))
385
- const declaration = filterAs(allData, asChainStakeIntent).at(0)
386
- expect(declaration).toBeUndefined()
387
- })
388
- })
389
- })
390
- })
391
- })
@@ -1,75 +0,0 @@
1
- import type {
2
- Address, Hash, Hex,
3
- } from '@xylabs/sdk-js'
4
- import {
5
- asAddress, assertEx,
6
- hexToBigInt, isDefined,
7
- toHex,
8
- } from '@xylabs/sdk-js'
9
- import type { Sequence, WithStorageMeta } from '@xyo-network/payload-model'
10
- import { XYO_ZERO_ADDRESS } from '@xyo-network/xl1-protocol'
11
- import {
12
- defaultTransactionFees, type SignedHydratedTransactionWithStorageMeta, type Transfer,
13
- } from '@xyo-network/xl1-protocol'
14
- import {
15
- describe, expect, it,
16
- } from 'vitest'
17
-
18
- import { generateTransactionFeeTransfers } from '../generateTransactionFeeTransfers.ts'
19
-
20
- describe('generateTransactionTransfers', () => {
21
- const testCases: SignedHydratedTransactionWithStorageMeta[][] = [
22
- [
23
- [
24
- {
25
- schema: 'network.xyo.boundwitness',
26
- addresses: ['39d22bed37f77bee0c056f648bde6efa489981c3' as Address],
27
- payload_hashes: ['65c64a5b2a2bf5aebc71255b64b36cc570dcd0d0539238757469d3772aef69c8' as Hash],
28
- payload_schemas: ['network.xyo.transfer'],
29
- previous_hashes: [null],
30
- $signatures: [
31
- '0605878a92045bb752cc12066b585589b513cee652bf4bc36c19289495ccd3d3751a1202b5bfb334325c4c6a59a4a7dad7468739a8b4ffe07b6091317b16dc09' as Hex,
32
- ],
33
- nbf: 2835,
34
- exp: 3835,
35
- fees: {
36
- base: toHex(defaultTransactionFees.base),
37
- gasLimit: toHex(defaultTransactionFees.gasLimit),
38
- gasPrice: toHex(defaultTransactionFees.gasPrice),
39
- priority: '00' as Hex,
40
- },
41
- chain: 'a82920051db4fcbb804463440dd45e03f72442fd' as Hex,
42
- script: ['elevate|65c64a5b2a2bf5aebc71255b64b36cc570dcd0d0539238757469d3772aef69c8'],
43
- from: '39d22bed37f77bee0c056f648bde6efa489981c3' as Address,
44
- _dataHash: '47be77303edd6073f82e9fff119107f1e173a08e7285e8e5f6e0c979d43e5b7d' as Hash,
45
- _hash: '1419062ddb266ff58515c4ffbc435ec482b6129d9e0c3c707fb7610c67215281' as Hash,
46
- _sequence: '00000196f46014770000000167215281' as Sequence,
47
- },
48
- [
49
- {
50
- schema: 'network.xyo.transfer',
51
- epoch: 1_747_856_659_420,
52
- from: '39d22bed37f77bee0c056f648bde6efa489981c3' as Address,
53
- transfers: { db5df9c99e661500c95f2c933023ba693e5c1ae9: '056bc75e2d63100000' as Hex },
54
- _dataHash: '65c64a5b2a2bf5aebc71255b64b36cc570dcd0d0539238757469d3772aef69c8' as Hash,
55
- _hash: '65c64a5b2a2bf5aebc71255b64b36cc570dcd0d0539238757469d3772aef69c8' as Hash,
56
- _sequence: '00000196f4601477000000002aef69c8' as Sequence,
57
- } as WithStorageMeta<Transfer>,
58
- ],
59
- ],
60
- ],
61
- ]
62
- it.each(testCases)('should generate correct Transfer payloads from hydrated transactions', async (...transactions) => {
63
- const producerAddress = assertEx(asAddress('3e86dac7546202156c4620d3cf36fb0ac30404a3'))
64
-
65
- // Patch the transactionRequiredGas function to return our test gas amount
66
-
67
- const result = await generateTransactionFeeTransfers(producerAddress, transactions)
68
-
69
- const burnValueHex = result.at(0)?.transfers[XYO_ZERO_ADDRESS]
70
- expect(burnValueHex).toBeDefined()
71
- const burnValue = isDefined(burnValueHex) ? hexToBigInt(burnValueHex) : 0n
72
- expect(burnValue).toBeGreaterThan(0n)
73
- expect(burnValue).toEqual(1_000_000_000_000n)
74
- })
75
- })
@@ -1,64 +0,0 @@
1
- import '@xylabs/vitest-extended'
2
-
3
- import { toFixedPoint } from '@xylabs/sdk-js'
4
- import {
5
- beforeAll, describe, expect, it,
6
- } from 'vitest'
7
-
8
- import { MemoryBlockRewardService } from '../index.ts'
9
-
10
- describe('MemoryBlockRewardService', () => {
11
- let sut: MemoryBlockRewardService
12
- // const genesisReward = toFixedPoint(18_000_000_000n)
13
- // const initialReward = toFixedPoint(500n)
14
- // const minRewardPerBlock = toFixedPoint(10n)
15
- const genesisReward = 18_000_000_000_000_000_000_000_000_000n
16
- const initialReward = 500_000_000_000_000_000_000n
17
- const minRewardPerBlock = 10_000_000_000_000_000_000n
18
- const stepFactorDenominator = 100n
19
- const stepFactorNumerator = 95n
20
- const stepSize = 1_000_000n
21
- beforeAll(async () => {
22
- sut = await MemoryBlockRewardService.create({
23
- stepFactorNumerator,
24
- stepFactorDenominator,
25
- stepSize,
26
- initialStepReward: initialReward,
27
- minRewardPerBlock,
28
- creatorReward: genesisReward,
29
- })
30
- })
31
-
32
- describe('rewards', () => {
33
- it('for block 0', async () => {
34
- const reward = await sut.getRewardForBlock(0n)
35
- expect(reward).toEqual(18_000_000_000_000_000_000_000_000_000n)
36
- expect(reward).toEqual(toFixedPoint(18_000_000_000n))
37
- })
38
- it('for block 1', async () => {
39
- const reward = await sut.getRewardForBlock(1n)
40
- expect(reward).toEqual(500_000_000_000_000_000_000n)
41
- expect(reward).toEqual(toFixedPoint(500n))
42
- })
43
- it('for block 2', async () => {
44
- const reward = await sut.getRewardForBlock(2n)
45
- expect(reward).toEqual(500_000_000_000_000_000_000n)
46
- expect(reward).toEqual(toFixedPoint(500n))
47
- })
48
- it('for block 3', async () => {
49
- const reward = await sut.getRewardForBlock(3n)
50
- expect(reward).toEqual(500_000_000_000_000_000_000n)
51
- expect(reward).toEqual(toFixedPoint(500n))
52
- })
53
- it('for block 4', async () => {
54
- const reward = await sut.getRewardForBlock(4n)
55
- expect(reward).toEqual(500_000_000_000_000_000_000n)
56
- expect(reward).toEqual(toFixedPoint(500n))
57
- })
58
- it('for block 1000000', async () => {
59
- const reward = await sut.getRewardForBlock(1_000_000n)
60
- expect(reward).toEqual(475_000_000_000_000_000_000n)
61
- expect(reward).toEqual(toFixedPoint(475n))
62
- })
63
- })
64
- })
@@ -1,276 +0,0 @@
1
- import type { CreatableName } from '@xylabs/sdk-js'
2
- import {
3
- asAddress, assertEx, delay,
4
- } from '@xylabs/sdk-js'
5
- import { Account } from '@xyo-network/account'
6
- import type { AccountInstance } from '@xyo-network/account-model'
7
- import { MemoryArchivist } from '@xyo-network/archivist-memory'
8
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
9
- import {
10
- buildRandomChain, buildRandomTransaction,
11
- findMostRecentBlock, TestChainId,
12
- } from '@xyo-network/chain-protocol'
13
- import { PayloadBuilder } from '@xyo-network/payload-builder'
14
- import type { WithStorageMeta } from '@xyo-network/payload-model'
15
- import type {
16
- BlockBoundWitness, HashPayload,
17
- HydratedBlock,
18
- SignedHydratedTransactionWithStorageMeta,
19
- } from '@xyo-network/xl1-protocol'
20
- import { HashSchema } from '@xyo-network/xl1-protocol'
21
- import {
22
- buildTransaction, flattenHydratedBlock, flattenHydratedTransactions,
23
- getDefaultConfig,
24
- } from '@xyo-network/xl1-protocol-sdk'
25
- import {
26
- beforeAll,
27
- beforeEach, describe, expect, it,
28
- } from 'vitest'
29
-
30
- import type { BasePendingTransactionsServiceParams } from '../BasePendingTransactions.ts'
31
- import { BasePendingTransactionsService } from '../BasePendingTransactions.ts'
32
- import { hydratedTransactionToPayloadBundle } from '../hydratedTransactionToPayloadBundle.ts'
33
-
34
- describe('BasePendingTransactionsService', () => {
35
- let sut: BasePendingTransactionsService
36
- let chainArchivist: ArchivistInstance
37
- let pendingBundledTransactionsArchivist: ArchivistInstance
38
- let rejectedTransactionsArchivist: ArchivistInstance
39
- let producer: AccountInstance
40
- let chain: HydratedBlock[]
41
- let head: WithStorageMeta<BlockBoundWitness>
42
- const chainId = TestChainId
43
- const config = getDefaultConfig()
44
-
45
- beforeAll(async () => {
46
- producer = await Account.random()
47
- })
48
-
49
- beforeEach(async () => {
50
- chain = await buildRandomChain(producer, 10)
51
- chainArchivist = await MemoryArchivist.create({ account: 'random' })
52
- await chainArchivist.insert(chain.flatMap(block => flattenHydratedBlock(block)))
53
- pendingBundledTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
54
- rejectedTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
55
- head = assertEx(await findMostRecentBlock(chainArchivist))
56
- const params: BasePendingTransactionsServiceParams = {
57
- name: 'TestBasePendingTransactionsServiceParams' as CreatableName,
58
- chainArchivist,
59
- chainId,
60
- config,
61
- pendingBundledTransactionsArchivist,
62
- rejectedTransactionsArchivist,
63
- }
64
- sut = await BasePendingTransactionsService.create(params)
65
- })
66
-
67
- describe('with pending transactions', () => {
68
- let newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
69
- beforeEach(async () => {
70
- for (let index = 0; index < 5; index++) {
71
- const tx = await buildRandomTransaction(TestChainId)
72
- newTransactions.push(tx)
73
- await delay(2)
74
- }
75
- const payloads = newTransactions.map(hydratedTransactionToPayloadBundle)
76
- await pendingBundledTransactionsArchivist.insert(payloads)
77
- })
78
- it('should return the requested amount of hydrated transactions', async () => {
79
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
80
- expect(result.length).toEqual(newTransactions.length)
81
- })
82
- })
83
-
84
- describe('with no pending transactions', () => {
85
- it('should return an empty array', async () => {
86
- const result = await sut.getPendingTransactions(head._dataHash, 10)
87
- expect(result).toEqual([])
88
- })
89
- })
90
-
91
- describe('with pending and finalized transactions', () => {
92
- let finalizedTransactions: SignedHydratedTransactionWithStorageMeta[] = []
93
- let newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
94
- beforeEach(async () => {
95
- for (let index = 0; index < 5; index++) {
96
- const tx = await buildRandomTransaction(TestChainId)
97
- finalizedTransactions.push(tx)
98
- await delay(2)
99
- }
100
- await pendingBundledTransactionsArchivist.insert(finalizedTransactions.map(hydratedTransactionToPayloadBundle))
101
- await chainArchivist.insert(flattenHydratedTransactions(finalizedTransactions))
102
- for (let index = 0; index < 5; index++) {
103
- const tx = await buildRandomTransaction(TestChainId)
104
- newTransactions.push(tx)
105
- await delay(2)
106
- }
107
- const pending = newTransactions.map(hydratedTransactionToPayloadBundle)
108
- await pendingBundledTransactionsArchivist.insert(pending)
109
- })
110
- it('should remove finalized transactions from pending', async () => {
111
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length + finalizedTransactions.length)
112
- expect(result.length).toEqual(newTransactions.length)
113
- })
114
- })
115
-
116
- describe('with repeated transactions', () => {
117
- const hashPayload = new PayloadBuilder<HashPayload>({ schema: HashSchema })
118
- .fields({ hash: '3fc6fb2edd79e0d3f27c668a706cb48473d5f9e432e5462ae647702221a2c97a' })
119
- .build()
120
- beforeEach(async () => {})
121
- it('when unique should allow transactions', async () => {
122
- let newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
123
- // NOTE: Cast to Account to allow loadPreviousHash to be called
124
- const signer = await Account.random()
125
- const transaction1 = await buildTransaction(TestChainId, [hashPayload], [], signer, 0, 100)
126
- const transaction2 = await buildTransaction(TestChainId, [hashPayload], [], signer, 0, 100)
127
- expect(transaction1[0]._hash).not.toEqual(transaction2[0]._hash)
128
- for (const tx of [transaction1, transaction2]) {
129
- const bundled = hydratedTransactionToPayloadBundle(tx)
130
- await pendingBundledTransactionsArchivist.insert([bundled])
131
- newTransactions.push(tx)
132
- }
133
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
134
- expect(result.length).toEqual(newTransactions.length)
135
- })
136
- it('when non-unique should filter duplicate transactions', async () => {
137
- let newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
138
- const previousHash = 'bb27dd470ccf91665d573a16b4f652a47b8a24686d1b44c189c1fa826cd3fef5'
139
- // NOTE: Cast to Account to allow loadPreviousHash to be called
140
- const signer = await Account.random() as Account
141
- await signer.loadPreviousHash(previousHash)
142
- const transaction1 = await buildTransaction(TestChainId, [hashPayload], [], signer, 0, 100)
143
- await signer.loadPreviousHash(previousHash)
144
- const transaction2 = await buildTransaction(TestChainId, [hashPayload], [], signer, 0, 100)
145
- expect(transaction1[0]._hash).toEqual(transaction2[0]._hash)
146
- for (const tx of [transaction1, transaction2]) {
147
- const bundled = hydratedTransactionToPayloadBundle(tx)
148
- await pendingBundledTransactionsArchivist.insert([bundled])
149
- newTransactions.push(tx)
150
- }
151
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
152
- expect(result.length).toEqual(newTransactions.length / 2)
153
- })
154
- })
155
-
156
- describe('with expiring transactions', () => {
157
- const insertTransaction = async (nbf: number, exp: number) => {
158
- const newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
159
- const signer = await Account.random()
160
- const hashPayload = new PayloadBuilder<HashPayload>({ schema: HashSchema })
161
- .fields({ hash: head._hash })
162
- .build()
163
- const tx = await buildRandomTransaction(TestChainId, [hashPayload], signer, nbf, exp)
164
- const bundled = hydratedTransactionToPayloadBundle(tx)
165
- await pendingBundledTransactionsArchivist.insert([bundled])
166
- newTransactions.push(tx)
167
- await delay(2)
168
- return newTransactions
169
- }
170
- describe('transaction nbf', () => {
171
- describe('is included', () => {
172
- it('when equal to previous block', async () => {
173
- const nbf = head.block - 1
174
- const exp = head.block + 3
175
- const newTransactions = await insertTransaction(nbf, exp)
176
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
177
- expect(result.length).toEqual(newTransactions.length)
178
- })
179
- it('when equal to current block', async () => {
180
- const nbf = head.block
181
- const exp = head.block + 3
182
- const newTransactions = await insertTransaction(nbf, exp)
183
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
184
- expect(result.length).toEqual(newTransactions.length)
185
- })
186
- it('when equal to next block', async () => {
187
- const nbf = head.block + 1
188
- const exp = head.block + 3
189
- const newTransactions = await insertTransaction(nbf, exp)
190
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
191
- expect(result.length).toEqual(newTransactions.length)
192
- })
193
- })
194
- describe('is excluded', () => {
195
- it('when beyond next block', async () => {
196
- const nbf = head.block + 2
197
- const exp = head.block + 3
198
- const newTransactions = await insertTransaction(nbf, exp)
199
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
200
- expect(result.length).toEqual(0)
201
- })
202
- })
203
- })
204
- describe('transaction exp', () => {
205
- describe('is excluded', () => {
206
- it('when equal to previous block', async () => {
207
- const nbf = head.block - 3
208
- const exp = head.block - 1
209
- const newTransactions = await insertTransaction(nbf, exp)
210
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
211
- expect(result.length).toEqual(0)
212
- })
213
- it('when equal to current block', async () => {
214
- const nbf = head.block - 3
215
- const exp = head.block
216
- const newTransactions = await insertTransaction(nbf, exp)
217
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
218
- expect(result.length).toEqual(0)
219
- })
220
- })
221
- describe('is included', () => {
222
- it('when equal to next block', async () => {
223
- const nbf = head.block - 3
224
- const exp = head.block + 1
225
- const newTransactions = await insertTransaction(nbf, exp)
226
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
227
- expect(result.length).toEqual(newTransactions.length)
228
- })
229
- it('when beyond next block', async () => {
230
- const nbf = head.block - 3
231
- const exp = head.block + 3
232
- const newTransactions = await insertTransaction(nbf, exp)
233
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length)
234
- expect(result.length).toEqual(newTransactions.length)
235
- })
236
- })
237
- })
238
- })
239
-
240
- describe('with pending and rejected transactions', () => {
241
- let rejectedTransactions: SignedHydratedTransactionWithStorageMeta[] = []
242
- let newTransactions: SignedHydratedTransactionWithStorageMeta[] = []
243
- beforeEach(async () => {
244
- for (let index = 0; index < 5; index++) {
245
- const tx = await buildRandomTransaction(TestChainId)
246
- rejectedTransactions.push(tx)
247
- await delay(2)
248
- }
249
- await pendingBundledTransactionsArchivist.insert(rejectedTransactions.map(hydratedTransactionToPayloadBundle))
250
- for (let index = 0; index < 5; index++) {
251
- const tx = await buildRandomTransaction(TestChainId)
252
- newTransactions.push(tx)
253
- await delay(2)
254
- }
255
- await pendingBundledTransactionsArchivist.insert(newTransactions.map(hydratedTransactionToPayloadBundle))
256
- await rejectedTransactionsArchivist.insert(flattenHydratedTransactions(rejectedTransactions))
257
- })
258
- it('should remove rejected transactions from pending', async () => {
259
- const result = await sut.getPendingTransactions(head._hash, newTransactions.length + rejectedTransactions.length)
260
- expect(result.length).toEqual(newTransactions.length)
261
- })
262
- })
263
-
264
- describe('with invalid transactions in mempool', () => {
265
- beforeEach(async () => {
266
- const incorrectChainId = assertEx(asAddress('1234567890abcdef1234567890abcdef12345678'))
267
- const invalidTransactions: SignedHydratedTransactionWithStorageMeta[] = [await buildRandomTransaction(incorrectChainId)]
268
- const pending = invalidTransactions.map(hydratedTransactionToPayloadBundle)
269
- await pendingBundledTransactionsArchivist.insert(pending)
270
- })
271
- it('should remove invalid transactions from pending', async () => {
272
- const result = await sut.getPendingTransactions(head._hash, 20)
273
- expect(result.length).toEqual(0)
274
- })
275
- })
276
- })
@@ -1,35 +0,0 @@
1
- import { buildRandomTransaction, TestChainId } from '@xyo-network/chain-protocol'
2
- import { PayloadBuilder } from '@xyo-network/payload-builder'
3
- import type { HashPayload } from '@xyo-network/xl1-protocol'
4
- import { HashSchema } from '@xyo-network/xl1-protocol'
5
- import {
6
- describe, expect, it,
7
- } from 'vitest'
8
-
9
- import { bundledPayloadToHydratedTransaction } from '../bundledPayloadToHydratedTransaction.ts'
10
- import { hydratedTransactionToPayloadBundle } from '../hydratedTransactionToPayloadBundle.ts'
11
-
12
- describe('bundledPayloadToHydratedTransaction', () => {
13
- it('creates a hydrated transaction from valid payload bundle', async () => {
14
- // Create a minimal Transaction
15
- const hashPayload = new PayloadBuilder<HashPayload>({ schema: HashSchema })
16
- .fields({ hash: '3fc6fb2edd79e0d3f27c668a706cb48473d5f9e432e5462ae647702221a2c97a' })
17
- .build()
18
- const payloads = [hashPayload]
19
- const tx = await buildRandomTransaction(TestChainId, payloads)
20
-
21
- // Create bundle
22
- const bundle = await PayloadBuilder.addStorageMeta(hydratedTransactionToPayloadBundle(tx))
23
-
24
- // Act
25
- const result = await bundledPayloadToHydratedTransaction(bundle)
26
-
27
- // Assert
28
- expect(result).toBeDefined()
29
- expect(result?.[0]._hash).toEqual(tx[0]._hash)
30
- expect(result?.[1].length).toEqual(payloads.length)
31
- for (const payload of result?.[1] ?? []) {
32
- expect(payload._hash).toEqual(await PayloadBuilder.hash(payload))
33
- }
34
- })
35
- })
@@ -1,28 +0,0 @@
1
- import { buildRandomTransaction, TestChainId } from '@xyo-network/chain-protocol'
2
- import { PayloadBuilder } from '@xyo-network/payload-builder'
3
- import { asOptionalPayloadBundle } from '@xyo-network/payload-model'
4
- import type { HashPayload } from '@xyo-network/xl1-protocol'
5
- import { HashSchema } from '@xyo-network/xl1-protocol'
6
- import {
7
- describe, expect, it,
8
- } from 'vitest'
9
-
10
- import { hydratedTransactionToPayloadBundle } from '../hydratedTransactionToPayloadBundle.ts'
11
-
12
- describe('hydratedTransactionToPayloadBundle', () => {
13
- it('creates a payload bundle from a hydrated transaction', async () => {
14
- // Create a minimal Transaction
15
- const hashPayload = new PayloadBuilder<HashPayload>({ schema: HashSchema })
16
- .fields({ hash: '3fc6fb2edd79e0d3f27c668a706cb48473d5f9e432e5462ae647702221a2c97a' })
17
- .build()
18
- const payloads = [hashPayload]
19
- const tx = await buildRandomTransaction(TestChainId, payloads)
20
-
21
- // Act
22
- const bundle = hydratedTransactionToPayloadBundle(tx)
23
-
24
- // Assert
25
- expect(bundle).toBeDefined()
26
- expect(asOptionalPayloadBundle(bundle)).toBeDefined()
27
- })
28
- })