@xyo-network/chain-services 1.15.1 → 1.15.3

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 (50) hide show
  1. package/README.md +5302 -2235
  2. package/dist/neutral/AccountBalance/BaseAccountBalanceService.d.ts +3 -5
  3. package/dist/neutral/AccountBalance/BaseAccountBalanceService.d.ts.map +1 -1
  4. package/dist/neutral/AccountBalance/accountBalanceServiceFromArchivist.d.ts +3 -2
  5. package/dist/neutral/AccountBalance/accountBalanceServiceFromArchivist.d.ts.map +1 -1
  6. package/dist/neutral/AccountTransfers/BaseAccountTransfersService.d.ts +3 -6
  7. package/dist/neutral/AccountTransfers/BaseAccountTransfersService.d.ts.map +1 -1
  8. package/dist/neutral/AccountTransfers/accountTransfersServiceFromArchivist.d.ts +3 -3
  9. package/dist/neutral/AccountTransfers/accountTransfersServiceFromArchivist.d.ts.map +1 -1
  10. package/dist/neutral/BlockProducer/spec/BaseBlockProducerService.spec.d.ts.map +1 -1
  11. package/dist/neutral/ChainBlockIteration/ChainBlockNumberIterationService.d.ts +20 -0
  12. package/dist/neutral/ChainBlockIteration/ChainBlockNumberIterationService.d.ts.map +1 -0
  13. package/dist/neutral/ChainBlockIteration/index.d.ts +3 -0
  14. package/dist/neutral/ChainBlockIteration/index.d.ts.map +1 -0
  15. package/dist/neutral/ChainBlockIteration/model/BlockNumberIteration.d.ts +7 -0
  16. package/dist/neutral/ChainBlockIteration/model/BlockNumberIteration.d.ts.map +1 -0
  17. package/dist/neutral/ChainBlockIteration/model/Params.d.ts +8 -0
  18. package/dist/neutral/ChainBlockIteration/model/Params.d.ts.map +1 -0
  19. package/dist/neutral/ChainBlockIteration/model/index.d.ts +3 -0
  20. package/dist/neutral/ChainBlockIteration/model/index.d.ts.map +1 -0
  21. package/dist/neutral/ChainBlockNumberIteration/ChainBlockNumberIterationService.d.ts +10 -7
  22. package/dist/neutral/ChainBlockNumberIteration/ChainBlockNumberIterationService.d.ts.map +1 -1
  23. package/dist/neutral/ChainBlockNumberIteration/model/Params.d.ts +3 -2
  24. package/dist/neutral/ChainBlockNumberIteration/model/Params.d.ts.map +1 -1
  25. package/dist/neutral/ChainValidator/XyoValidator.d.ts +2 -2
  26. package/dist/neutral/ChainValidator/XyoValidator.d.ts.map +1 -1
  27. package/dist/neutral/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.d.ts +15 -14
  28. package/dist/neutral/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.d.ts.map +1 -1
  29. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts +2 -2
  30. package/dist/neutral/PendingTransactions/BasePendingTransactions.d.ts.map +1 -1
  31. package/dist/neutral/StakeIntent/XyoStakeIntentService.d.ts.map +1 -1
  32. package/dist/neutral/index.mjs +131 -157
  33. package/dist/neutral/index.mjs.map +1 -1
  34. package/package.json +41 -41
  35. package/src/AccountBalance/BaseAccountBalanceService.ts +6 -8
  36. package/src/AccountBalance/accountBalanceServiceFromArchivist.ts +18 -33
  37. package/src/AccountTransfers/BaseAccountTransfersService.ts +4 -13
  38. package/src/AccountTransfers/accountTransfersServiceFromArchivist.ts +18 -35
  39. package/src/BlockProducer/spec/BaseBlockProducerService.spec.ts +62 -8
  40. package/src/ChainBlockIteration/ChainBlockNumberIterationService.ts +108 -0
  41. package/src/ChainBlockIteration/index.ts +2 -0
  42. package/src/ChainBlockIteration/model/BlockNumberIteration.ts +7 -0
  43. package/src/ChainBlockIteration/model/Params.ts +9 -0
  44. package/src/ChainBlockIteration/model/index.ts +2 -0
  45. package/src/ChainBlockNumberIteration/ChainBlockNumberIterationService.ts +44 -30
  46. package/src/ChainBlockNumberIteration/model/Params.ts +3 -2
  47. package/src/ChainValidator/XyoValidator.ts +2 -2
  48. package/src/NetworkStakeStepReward/BaseNetworkStakeStepRewardService.ts +20 -14
  49. package/src/PendingTransactions/BasePendingTransactions.ts +2 -2
  50. package/src/StakeIntent/XyoStakeIntentService.ts +3 -1
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.15.1",
4
+ "version": "1.15.3",
5
5
  "description": "XYO Layer One SDK Services",
6
6
  "homepage": "https://xylabs.com",
7
7
  "bugs": {
@@ -37,51 +37,51 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@opentelemetry/api": "~1.9.0",
40
- "@xylabs/array": "~5.0.11",
41
- "@xylabs/assert": "~5.0.11",
42
- "@xylabs/creatable": "~5.0.11",
43
- "@xylabs/decimal-precision": "~5.0.11",
44
- "@xylabs/events": "~5.0.11",
45
- "@xylabs/exists": "~5.0.11",
46
- "@xylabs/forget": "~5.0.11",
47
- "@xylabs/hex": "~5.0.11",
48
- "@xylabs/promise": "~5.0.11",
49
- "@xylabs/telemetry": "~5.0.11",
50
- "@xylabs/typeof": "~5.0.11",
51
- "@xyo-network/account-model": "~5.1.2",
52
- "@xyo-network/archivist-memory": "~5.1.2",
53
- "@xyo-network/archivist-model": "~5.1.2",
54
- "@xyo-network/boundwitness-model": "~5.1.2",
55
- "@xyo-network/boundwitness-validator": "~5.1.2",
56
- "@xyo-network/boundwitness-wrapper": "~5.1.2",
57
- "@xyo-network/chain-analyze": "~1.15.1",
58
- "@xyo-network/chain-modules": "~1.15.1",
59
- "@xyo-network/chain-protocol": "~1.15.1",
60
- "@xyo-network/chain-utils": "~1.15.1",
61
- "@xyo-network/payload-builder": "~5.1.2",
62
- "@xyo-network/payload-model": "~5.1.2",
63
- "@xyo-network/typechain": "~4.0.8",
64
- "@xyo-network/xl1-protocol": "~1.12.33",
65
- "@xyo-network/xl1-protocol-sdk": "~1.15.1",
66
- "@xyo-network/xl1-validation": "~1.15.1",
67
- "@xyo-network/xl1-wrappers": "~1.15.1",
40
+ "@xylabs/array": "~5.0.12",
41
+ "@xylabs/assert": "~5.0.12",
42
+ "@xylabs/creatable": "~5.0.12",
43
+ "@xylabs/decimal-precision": "~5.0.12",
44
+ "@xylabs/events": "~5.0.12",
45
+ "@xylabs/exists": "~5.0.12",
46
+ "@xylabs/forget": "~5.0.12",
47
+ "@xylabs/hex": "~5.0.12",
48
+ "@xylabs/promise": "~5.0.12",
49
+ "@xylabs/telemetry": "~5.0.12",
50
+ "@xylabs/typeof": "~5.0.12",
51
+ "@xyo-network/account-model": "~5.1.6",
52
+ "@xyo-network/archivist-memory": "~5.1.6",
53
+ "@xyo-network/archivist-model": "~5.1.6",
54
+ "@xyo-network/boundwitness-model": "~5.1.6",
55
+ "@xyo-network/boundwitness-validator": "~5.1.6",
56
+ "@xyo-network/boundwitness-wrapper": "~5.1.6",
57
+ "@xyo-network/chain-analyze": "~1.15.3",
58
+ "@xyo-network/chain-modules": "~1.15.3",
59
+ "@xyo-network/chain-protocol": "~1.15.3",
60
+ "@xyo-network/chain-utils": "~1.15.3",
61
+ "@xyo-network/payload-builder": "~5.1.6",
62
+ "@xyo-network/payload-model": "~5.1.6",
63
+ "@xyo-network/typechain": "~4.0.10",
64
+ "@xyo-network/xl1-protocol": "~1.12.66",
65
+ "@xyo-network/xl1-protocol-sdk": "~1.15.3",
66
+ "@xyo-network/xl1-validation": "~1.15.3",
67
+ "@xyo-network/xl1-wrappers": "~1.15.3",
68
68
  "async-mutex": "~0.5.0",
69
69
  "ethers": "6.15.0",
70
- "lru-cache": "~11.2.1"
70
+ "lru-cache": "~11.2.2"
71
71
  },
72
72
  "devDependencies": {
73
- "@types/node": "~24.5.2",
74
- "@xylabs/delay": "~5.0.11",
75
- "@xylabs/ts-scripts-yarn3": "~7.1.7",
76
- "@xylabs/tsconfig": "~7.1.7",
77
- "@xylabs/vitest-extended": "~5.0.11",
78
- "@xyo-network/account": "~5.1.2",
79
- "@xyo-network/account-model": "~5.1.2",
80
- "@xyo-network/chain-validation": "~1.15.1",
81
- "@xyo-network/wallet": "~5.1.2",
82
- "eslint": "^9.36.0",
73
+ "@types/node": "~24.7.2",
74
+ "@xylabs/delay": "~5.0.12",
75
+ "@xylabs/ts-scripts-yarn3": "~7.1.8",
76
+ "@xylabs/tsconfig": "~7.1.8",
77
+ "@xylabs/vitest-extended": "~5.0.12",
78
+ "@xyo-network/account": "~5.1.6",
79
+ "@xyo-network/account-model": "~5.1.6",
80
+ "@xyo-network/chain-validation": "~1.15.3",
81
+ "@xyo-network/wallet": "~5.1.6",
82
+ "eslint": "^9.37.0",
83
83
  "tslib": "~2.8.1",
84
- "typescript": "~5.9.2",
84
+ "typescript": "~5.9.3",
85
85
  "vitest": "~3.2.4",
86
86
  "vitest-mock-extended": "~3.1.0",
87
87
  "web3-types": "~1.10.0"
@@ -1,31 +1,29 @@
1
1
  import { creatable } from '@xylabs/creatable'
2
2
  import { Address, Hash } from '@xylabs/hex'
3
3
  import { spanRootAsync } from '@xylabs/telemetry'
4
- import { ReadArchivist } from '@xyo-network/archivist-model'
5
4
  import {
6
5
  AccountBalanceServiceV2,
7
- AttoXL1, PayloadRepository,
6
+ AttoXL1,
8
7
  } from '@xyo-network/xl1-protocol'
9
8
  import {
10
- BalancesStepSummary,
11
9
  balancesSummary,
10
+ BalanceStepSummaryContext,
12
11
  } from '@xyo-network/xl1-protocol-sdk'
13
12
 
14
13
  import { BaseService } from '../BaseService.ts'
15
14
  import { BaseServiceParams } from '../model/index.ts'
16
15
 
17
16
  export interface BaseAccountBalanceServiceParams extends BaseServiceParams {
18
- chainArchivist: ReadArchivist
19
- summaryRepository: PayloadRepository<Hash, BalancesStepSummary>
17
+ context: BalanceStepSummaryContext
20
18
  }
21
19
 
22
20
  @creatable()
23
21
  export class BaseAccountBalanceService extends BaseService<BaseAccountBalanceServiceParams> implements AccountBalanceServiceV2 {
24
22
  async balances(head: Hash, address: Address[]): Promise<Partial<Record<Address, AttoXL1>>> {
25
23
  return await spanRootAsync('balances', async () => {
26
- const summary = await balancesSummary({
27
- chainArchivist: this.params.chainArchivist, summaryRepository: this.params.summaryRepository, head,
28
- })
24
+ const summary = await balancesSummary(
25
+ this.params.context,
26
+ )
29
27
  const result: Record<Address, AttoXL1> = {}
30
28
  for (const addr of address) {
31
29
  const summaryBalance = summary[addr] ?? 0n
@@ -1,45 +1,30 @@
1
- import { exists } from '@xylabs/exists'
2
- import {
3
- asHash, type Hash,
4
- isHash,
5
- } from '@xylabs/hex'
1
+ import { ZERO_HASH } from '@xylabs/hex'
6
2
  import type { ReadArchivist } from '@xyo-network/archivist-model'
7
- import { PayloadBuilder } from '@xyo-network/payload-builder'
8
- import type { WithStorageMeta } from '@xyo-network/payload-model'
9
- import type { AccountBalanceServiceV2 } from '@xyo-network/xl1-protocol'
10
- import type { BalancesStepSummary, StepSummary } from '@xyo-network/xl1-protocol-sdk'
11
- import { LRUCache } from 'lru-cache'
3
+ import { LruCacheMap } from '@xyo-network/chain-protocol'
4
+ import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
5
+ import type { AccountBalanceServiceV2, ChainId } from '@xyo-network/xl1-protocol'
6
+ import { type BalancesStepSummary, readPayloadMapFromStore } from '@xyo-network/xl1-protocol-sdk'
12
7
 
13
8
  import { BaseAccountBalanceService } from './BaseAccountBalanceService.ts'
14
9
 
15
- export const accountBalanceServiceFromArchivist = async (archivist: ReadArchivist): Promise<AccountBalanceServiceV2> => {
16
- const summaryArchivistCache = new LRUCache<Hash, WithStorageMeta<BalancesStepSummary>>({
10
+ export const accountBalancesServiceFromArchivist = async (
11
+ chainId: ChainId,
12
+ archivist: ReadArchivist<WithStorageMeta<Payload>>,
13
+ ): Promise<AccountBalanceServiceV2> => {
14
+ const summaryMap = new LruCacheMap<string, BalancesStepSummary>({
17
15
  max: 100_000,
18
16
  allowStale: true,
19
17
  noDisposeOnSet: false,
20
18
  updateAgeOnGet: true,
21
19
  })
22
- const summaryRepository = {
23
- get: (hashes: Hash[]) => {
24
- const results = hashes.map((hash) => {
25
- return summaryArchivistCache.get(hash)
26
- }).filter(exists)
27
- return results
20
+ const chainMap = readPayloadMapFromStore<WithStorageMeta<Payload>>(archivist)
21
+ const service = await BaseAccountBalanceService.create({
22
+ context: {
23
+ chainId,
24
+ store: { chainMap },
25
+ summaryMap,
26
+ head: () => ZERO_HASH,
28
27
  },
29
- insert: async (payloads: BalancesStepSummary[]) => {
30
- const results = (await PayloadBuilder.addStorageMeta(payloads)).map((payload) => {
31
- const hash = asHash((payload as WithStorageMeta<StepSummary>).hash)
32
- if (isHash(hash)) {
33
- summaryArchivistCache.set(hash, payload)
34
- return payload
35
- }
36
- }).filter(exists)
37
- return results
38
- },
39
- next: () => {
40
- throw new Error('Not implemented')
41
- },
42
- }
43
- const service = await BaseAccountBalanceService.create({ chainArchivist: archivist, summaryRepository })
28
+ })
44
29
  return service
45
30
  }
@@ -1,14 +1,12 @@
1
1
  import { creatable } from '@xylabs/creatable'
2
2
  import { Address, Hash } from '@xylabs/hex'
3
- import { Promisable } from '@xylabs/promise'
4
3
  import { spanRootAsync } from '@xylabs/telemetry'
5
- import { ReadArchivist } from '@xyo-network/archivist-model'
6
4
  import {
7
5
  AccountTransfersService,
8
- AttoXL1, PayloadRepository,
6
+ AttoXL1,
9
7
  } from '@xyo-network/xl1-protocol'
10
8
  import {
11
- TransfersStepSummary,
9
+ TransfersStepSummaryContext,
12
10
  transfersSummary,
13
11
  } from '@xyo-network/xl1-protocol-sdk'
14
12
 
@@ -16,9 +14,7 @@ import { BaseService } from '../BaseService.ts'
16
14
  import { BaseServiceParams } from '../model/index.ts'
17
15
 
18
16
  export interface BaseAccountTransferServiceParams extends BaseServiceParams {
19
- account: Address
20
- chainArchivist: ReadArchivist
21
- summaryRepository: PayloadRepository<Hash, TransfersStepSummary>
17
+ context: TransfersStepSummaryContext
22
18
  }
23
19
 
24
20
  @creatable()
@@ -29,12 +25,7 @@ export class BaseAccountTransfersService extends BaseService<BaseAccountTransfer
29
25
 
30
26
  async transfers(head: Hash, addresses: Address[]): Promise<Partial<Record<Address, AttoXL1>>> {
31
27
  return await spanRootAsync('transfers', async () => {
32
- const {
33
- chainArchivist, summaryRepository, account,
34
- } = this.params
35
- const summary = await transfersSummary({
36
- chainArchivist, summaryRepository, head, account,
37
- })
28
+ const summary = await transfersSummary(this.params.context)
38
29
  const result: Record<Address, AttoXL1> = {}
39
30
  for (const addr of addresses) {
40
31
  const summaryBalance = summary[addr] ?? 0n
@@ -1,48 +1,31 @@
1
- import { exists } from '@xylabs/exists'
2
- import type { Address, Hash } from '@xylabs/hex'
3
- import {
4
- asHash,
5
- isHash,
6
- } from '@xylabs/hex'
1
+ import { ZERO_HASH } from '@xylabs/hex'
7
2
  import type { ReadArchivist } from '@xyo-network/archivist-model'
8
- import { PayloadBuilder } from '@xyo-network/payload-builder'
9
- import type { WithStorageMeta } from '@xyo-network/payload-model'
10
- import type { AccountTransfersService } from '@xyo-network/xl1-protocol'
11
- import type { StepSummary, TransfersStepSummary } from '@xyo-network/xl1-protocol-sdk'
12
- import { LRUCache } from 'lru-cache'
3
+ import { LruCacheMap } from '@xyo-network/chain-protocol'
4
+ import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
5
+ import type { AccountTransfersService, ChainId } from '@xyo-network/xl1-protocol'
6
+ import type { TransfersStepSummary } from '@xyo-network/xl1-protocol-sdk'
7
+ import { readPayloadMapFromStore } from '@xyo-network/xl1-protocol-sdk'
13
8
 
14
9
  import { BaseAccountTransfersService } from './BaseAccountTransfersService.ts'
15
10
 
16
- export const accountTransferServiceFromArchivist = async (archivist: ReadArchivist, account: Address): Promise<AccountTransfersService> => {
17
- const summaryArchivistCache = new LRUCache<Hash, WithStorageMeta<TransfersStepSummary>>({
11
+ export const accountTransfersServiceFromArchivist = async (
12
+ chainId: ChainId,
13
+ archivist: ReadArchivist<WithStorageMeta<Payload>>,
14
+ ): Promise<AccountTransfersService> => {
15
+ const summaryMap = new LruCacheMap<string, TransfersStepSummary>({
18
16
  max: 100_000,
19
17
  allowStale: true,
20
18
  noDisposeOnSet: false,
21
19
  updateAgeOnGet: true,
22
20
  })
23
- const summaryRepository = {
24
- get: (hashes: Hash[]) => {
25
- const results = hashes.map((hash) => {
26
- return summaryArchivistCache.get(hash)
27
- }).filter(exists)
28
- return results
29
- },
30
- insert: async (payloads: TransfersStepSummary[]) => {
31
- const results = (await PayloadBuilder.addStorageMeta(payloads)).map((payload) => {
32
- const hash = asHash((payload as WithStorageMeta<StepSummary>).hash)
33
- if (isHash(hash)) {
34
- summaryArchivistCache.set(hash, payload)
35
- return payload
36
- }
37
- }).filter(exists)
38
- return results
39
- },
40
- next: () => {
41
- throw new Error('Not implemented')
42
- },
43
- }
21
+ const chainMap = readPayloadMapFromStore<WithStorageMeta<Payload>>(archivist)
44
22
  const service = await BaseAccountTransfersService.create({
45
- chainArchivist: archivist, account, summaryRepository,
23
+ context: {
24
+ chainId,
25
+ store: { chainMap },
26
+ summaryMap,
27
+ head: () => ZERO_HASH,
28
+ },
46
29
  })
47
30
  return service
48
31
  }
@@ -1,13 +1,15 @@
1
+ /* eslint-disable max-statements */
1
2
  import '@xylabs/vitest-extended'
2
3
 
3
4
  import { filterAs } from '@xylabs/array'
4
5
  import { assertEx } from '@xylabs/assert'
5
6
  import type { CreatableName } from '@xylabs/creatable'
6
7
  import { delay } from '@xylabs/delay'
7
- import type { Address } from '@xylabs/hex'
8
+ import type { Address, Hash } from '@xylabs/hex'
8
9
  import {
9
10
  asAddress, hexToBigInt, ZERO_HASH,
10
11
  } from '@xylabs/hex'
12
+ import type { Promisable } from '@xylabs/promise'
11
13
  import { Account } from '@xyo-network/account'
12
14
  import type { AccountInstance } from '@xyo-network/account-model'
13
15
  import { MemoryArchivist } from '@xyo-network/archivist-memory'
@@ -19,10 +21,11 @@ import {
19
21
  } from '@xyo-network/payload-model'
20
22
  import { HDWallet } from '@xyo-network/wallet'
21
23
  import type {
22
- BlockBoundWitness, BlockRewardService, Chain, ElectionService,
24
+ BlockBoundWitness, BlockRewardService, ChainId, ElectionService,
23
25
  HydratedBlock,
24
26
  HydratedBlockStateValidationFunctionV2,
25
27
  StakeIntentService,
28
+ TimeDomain, TimePayload,
26
29
  TimeSyncViewInterfaceV2,
27
30
  } from '@xyo-network/xl1-protocol'
28
31
  import {
@@ -31,6 +34,7 @@ import {
31
34
  asTransactionBoundWitness,
32
35
  asTransfer,
33
36
  HydratedBlockStateValidationError,
37
+ TimeSchema,
34
38
  XYO_ZERO_ADDRESS,
35
39
  } from '@xyo-network/xl1-protocol'
36
40
  import type { Config } from '@xyo-network/xl1-protocol-sdk'
@@ -42,7 +46,7 @@ import {
42
46
  } from 'vitest'
43
47
  import { mock } from 'vitest-mock-extended'
44
48
 
45
- import { accountBalanceServiceFromArchivist } from '../../AccountBalance/index.ts'
49
+ import { accountBalancesServiceFromArchivist } from '../../AccountBalance/index.ts'
46
50
  import { MemoryBlockRewardService } from '../../BlockReward/index.ts'
47
51
  import type { BasePendingTransactionsServiceParams } from '../../PendingTransactions/index.ts'
48
52
  import { BasePendingTransactionsService } from '../../PendingTransactions/index.ts'
@@ -95,9 +99,56 @@ describe('XyoBlockProducer', () => {
95
99
 
96
100
  stakeIntentService = mock<StakeIntentService>()
97
101
  stakeIntentService.getDeclaredCandidateRanges.mockResolvedValue([])
98
- time = mock<TimeSyncViewInterfaceV2>()
102
+ time = {
103
+ currentTimeAndHash(domain: TimeDomain): Promisable<[number, Hash | null]> {
104
+ switch (domain) {
105
+ case 'epoch': {
106
+ return [Date.now(), null]
107
+ }
108
+ case 'xl1': {
109
+ return [1, '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff' as Hash]
110
+ }
111
+ case 'ethereum': {
112
+ return [1, '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff' as Hash]
113
+ }
114
+ // No default
115
+ }
116
+ },
117
+ currentTime(domain: TimeDomain): Promisable<[string, number]> {
118
+ switch (domain) {
119
+ case 'epoch': {
120
+ return ['epoch', Date.now()]
121
+ }
122
+ case 'xl1': {
123
+ return ['xl1', 1]
124
+ }
125
+ case 'ethereum': {
126
+ return ['ethereum', 1]
127
+ }
128
+ // No default
129
+ }
130
+ },
131
+ currentTimePayload(): Promisable<TimePayload> {
132
+ return {
133
+ schema: TimeSchema, epoch: Date.now(), xl1: 1,
134
+ }
135
+ },
136
+ /** Convert time between different domains */
137
+ convertTime(fromDomain: TimeDomain, toDomain: TimeDomain, from: number): Promisable<number> {
138
+ if (fromDomain === toDomain) {
139
+ return from
140
+ }
141
+ if (fromDomain === 'epoch' && toDomain === 'xl1') {
142
+ return 1
143
+ }
144
+ if (fromDomain === 'xl1' && toDomain === 'epoch') {
145
+ return Date.now()
146
+ }
147
+ return from
148
+ },
149
+ }
99
150
  rewardService = await MemoryBlockRewardService.create()
100
- const balanceService = await accountBalanceServiceFromArchivist(chainArchivist)
151
+ const balanceService = await accountBalancesServiceFromArchivist(chainId, chainArchivist)
101
152
  const params: BaseBlockProducerServiceParams = {
102
153
  name: 'TestXyoBlockProducerParams' as CreatableName,
103
154
  account,
@@ -179,11 +230,11 @@ describe('XyoBlockProducer', () => {
179
230
  let rejectBlock = true
180
231
  const validateHydratedBlockState: HydratedBlockStateValidationFunctionV2 = async (
181
232
  hydratedBlock: HydratedBlock,
182
- chainId: Chain,
233
+ chainId: ChainId,
183
234
  ) => {
184
235
  return rejectBlock ? [await Promise.resolve(new HydratedBlockStateValidationError(ZERO_HASH, chainId, hydratedBlock, 'Invalid block'))] : []
185
236
  }
186
- const balanceService = await accountBalanceServiceFromArchivist(chainArchivist)
237
+ const balanceService = await accountBalancesServiceFromArchivist(chainId, chainArchivist)
187
238
  const params: BaseBlockProducerServiceParams = {
188
239
  name: 'TestXyoBlockProducerParams' as CreatableName,
189
240
  account,
@@ -234,11 +285,14 @@ describe('XyoBlockProducer', () => {
234
285
  const transfers = filterAs(assertEx(transactionsAndData), asTransfer)
235
286
  const blockRewardTransfer = transfers.find(transfer => transfer.from === XYO_ZERO_ADDRESS)
236
287
  expect(blockRewardTransfer).toBeDefined()
288
+ let totalTransfer = 0n
237
289
  for (const value of Object.values(blockRewardTransfer?.transfers ?? {})) {
238
290
  if (value) {
239
- expect(hexToBigInt(value)).toEqual(1_500_000_000_000_000_000_000n)
291
+ const bigIntValue = hexToBigInt(value)
292
+ totalTransfer += bigIntValue
240
293
  }
241
294
  }
295
+ expect(totalTransfer).toEqual(3_000_000_000_000_000_000_000n)
242
296
  }
243
297
  })
244
298
  })
@@ -0,0 +1,108 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import type { Hex } from '@xylabs/hex'
3
+ import {
4
+ isDefined, isNull, isUndefined,
5
+ } from '@xylabs/typeof'
6
+ import type { ArchivistInstance } from '@xyo-network/archivist-model'
7
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
8
+ import type { WithStorageMeta } from '@xyo-network/payload-model'
9
+ import type {
10
+ BlockBoundWitness,
11
+ ChainIteratorServiceEventData,
12
+ EventingChainBlockNumberIteratorService,
13
+ } from '@xyo-network/xl1-protocol'
14
+ import {
15
+ asBlockBoundWitness,
16
+ asBlockBoundWitnessWithStorageMeta,
17
+ isBlockBoundWitness,
18
+ } from '@xyo-network/xl1-protocol'
19
+ import { LRUCache } from 'lru-cache'
20
+
21
+ import { BaseService } from '../BaseService.ts'
22
+ import type { XyoChainIteratorParams } from './model/index.ts'
23
+
24
+ export class ChainBlockNumberIterationService extends BaseService<XyoChainIteratorParams, ChainIteratorServiceEventData>
25
+ implements EventingChainBlockNumberIteratorService {
26
+ protected _blocksByBlockNumber = new LRUCache<number, WithStorageMeta<BlockBoundWitness>>({ max: 10_000 })
27
+ protected _currentHead: WithStorageMeta<BlockBoundWitness> | undefined
28
+
29
+ get chainArchivist(): ArchivistInstance { return assertEx(this.params.chainArchivist) }
30
+
31
+ get chainId(): Hex { return assertEx(this._currentHead?.chain ?? this.params?.head?.chain, () => 'Current head is not set') }
32
+
33
+ async get(block: number): Promise<WithStorageMeta<BlockBoundWitness>> {
34
+ const head = await this.head()
35
+ // if(isUndefined(head)) return undefined
36
+ // Bail early if the block requested is newer than the current head
37
+ assertEx(head.block >= block, () => `Block requested is newer than the current head [${block}]`)
38
+ const cached = this._blocksByBlockNumber.get(block)
39
+ if (cached) return cached
40
+ // Start at the current head and traverse backwards until the requested block is found
41
+ const startingBlock = head
42
+ const currentBlockHash = await PayloadBuilder.hash(startingBlock)
43
+ let currentBlock = (await this.chainArchivist.get([currentBlockHash])).at(0)
44
+ while (isDefined(currentBlock)) {
45
+ assertEx(asBlockBoundWitness(currentBlock), () => `Expected hash to be a block bound witness [${currentBlock?._hash}]`)
46
+ if (isBlockBoundWitness(currentBlock)) {
47
+ this._blocksByBlockNumber.set(currentBlock.block, currentBlock)
48
+ if (currentBlock.block === block) {
49
+ return currentBlock
50
+ }
51
+ const { previous } = currentBlock
52
+ if (isNull(previous)) break
53
+ currentBlock = (await this.chainArchivist.get([previous])).at(0)
54
+ }
55
+ }
56
+ throw new Error(`Block not found: ${block}`)
57
+ }
58
+
59
+ async head(): Promise<WithStorageMeta<BlockBoundWitness>> {
60
+ if (isDefined(this._currentHead)) return this._currentHead
61
+ if (isDefined(this.params.head)) {
62
+ const newHead = await this.getBoundWitnessAsBlockBoundWitnessWithStorageMeta(this.params.head)
63
+ this._currentHead = newHead
64
+ return newHead
65
+ }
66
+ throw new Error('Head is not set')
67
+ }
68
+
69
+ async next(block: number): Promise<WithStorageMeta<BlockBoundWitness> | undefined> {
70
+ const currentBlock = block
71
+ const nextBlockNumber = currentBlock + 1
72
+ return await this.get(nextBlockNumber)
73
+ }
74
+
75
+ // TODO: Decide on inclusive/exclusive (probably need inclusive to account for chain head)
76
+ // and then communicate via method name and documentation
77
+ async previous(block: number | undefined = undefined, count: number = 1): Promise<WithStorageMeta<BlockBoundWitness>[]> {
78
+ const results: WithStorageMeta<BlockBoundWitness>[] = []
79
+ let currentBlock: WithStorageMeta<BlockBoundWitness> | undefined = isDefined(block) ? (await this.get(block)) : await this.head()
80
+ while (currentBlock && results.length < count) {
81
+ if (isBlockBoundWitness(currentBlock)) {
82
+ results.push(currentBlock)
83
+ const { previous } = currentBlock
84
+ if (isNull(previous)) break
85
+ const nextBlock = await this.chainArchivist.get([previous])
86
+ currentBlock = asBlockBoundWitnessWithStorageMeta(nextBlock[0])
87
+ } else {
88
+ const hash = PayloadBuilder.hash(currentBlock)
89
+ assertEx(asBlockBoundWitnessWithStorageMeta(currentBlock), () => `Expected hash to be a block bound witness [${hash}]`)
90
+ }
91
+ }
92
+ return results
93
+ }
94
+
95
+ async updateHead(head: BlockBoundWitness): Promise<void> {
96
+ const newHead = await this.getBoundWitnessAsBlockBoundWitnessWithStorageMeta(head)
97
+ this._currentHead = newHead
98
+ void this.emit('headUpdated', { blocks: [newHead] })
99
+ }
100
+
101
+ private async getBoundWitnessAsBlockBoundWitnessWithStorageMeta(head: BlockBoundWitness): Promise<WithStorageMeta<BlockBoundWitness>> {
102
+ const hash = await PayloadBuilder.hash(head)
103
+ const stored = (await this.chainArchivist.get([hash])).at(-1)
104
+ const newHead = asBlockBoundWitnessWithStorageMeta(stored)
105
+ if (isUndefined(newHead)) throw new Error(`Head block not found in archivist [${hash}]`)
106
+ return newHead
107
+ }
108
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ChainBlockNumberIterationService.ts'
2
+ export * from './model/index.ts'
@@ -0,0 +1,7 @@
1
+ import type { Promisable } from '@xylabs/promise'
2
+ import type { BlockBoundWitness, SignedHydratedTransactionWithStorageMeta } from '@xyo-network/xl1-protocol'
3
+
4
+ export interface BlockNumberIteration {
5
+ validatePendingBlock(block: BlockBoundWitness): Promisable<Error[]>
6
+ validatePendingTransaction(tx: SignedHydratedTransactionWithStorageMeta): Promise<boolean>
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { ArchivistInstance } from '@xyo-network/archivist-model'
2
+ import type { BlockBoundWitness } from '@xyo-network/xl1-protocol'
3
+
4
+ import type { BaseServiceParams } from '../../model/index.ts'
5
+
6
+ export interface XyoChainIteratorParams extends BaseServiceParams {
7
+ chainArchivist: ArchivistInstance
8
+ head: BlockBoundWitness
9
+ }
@@ -0,0 +1,2 @@
1
+ export * from './BlockNumberIteration.ts'
2
+ export * from './Params.ts'