@xyo-network/xl1-cli-lib 1.14.2 → 1.14.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 (62) hide show
  1. package/dist/node/index.mjs +216 -65
  2. package/dist/node/index.mjs.map +1 -1
  3. package/dist/node/orchestration/archivists/lib/localPersistentArchivist.d.ts +1 -1
  4. package/dist/node/orchestration/archivists/lib/localPersistentArchivist.d.ts.map +1 -1
  5. package/dist/node/orchestration/services/implementation/head/createBootstrapHead.d.ts +4 -0
  6. package/dist/node/orchestration/services/implementation/head/createBootstrapHead.d.ts.map +1 -0
  7. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getBridgeDestinationDetails.d.ts +18 -0
  8. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getBridgeDestinationDetails.d.ts.map +1 -0
  9. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getBridgeSourceDetails.d.ts +14 -0
  10. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getBridgeSourceDetails.d.ts.map +1 -0
  11. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getForkDetails.d.ts +4 -0
  12. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/getForkDetails.d.ts.map +1 -0
  13. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/index.d.ts +4 -0
  14. package/dist/node/orchestration/services/implementation/head/createForkedHead/config/index.d.ts.map +1 -0
  15. package/dist/node/orchestration/services/implementation/head/createForkedHead/createForkedHead.d.ts +5 -0
  16. package/dist/node/orchestration/services/implementation/head/createForkedHead/createForkedHead.d.ts.map +1 -0
  17. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeDestinationObservation.d.ts +10 -0
  18. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeDestinationObservation.d.ts.map +1 -0
  19. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeIntent.d.ts +10 -0
  20. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeIntent.d.ts.map +1 -0
  21. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeSourceObservation.d.ts +10 -0
  22. package/dist/node/orchestration/services/implementation/head/createForkedHead/getBridgeSourceObservation.d.ts.map +1 -0
  23. package/dist/node/orchestration/services/implementation/head/createForkedHead/getFirstBlockForNewChain.d.ts +12 -0
  24. package/dist/node/orchestration/services/implementation/head/createForkedHead/getFirstBlockForNewChain.d.ts.map +1 -0
  25. package/dist/node/orchestration/services/implementation/head/createForkedHead/index.d.ts +2 -0
  26. package/dist/node/orchestration/services/implementation/head/createForkedHead/index.d.ts.map +1 -0
  27. package/dist/node/orchestration/services/implementation/head/getForkFromBlock.d.ts +12 -0
  28. package/dist/node/orchestration/services/implementation/head/getForkFromBlock.d.ts.map +1 -0
  29. package/dist/node/orchestration/services/implementation/head/head.d.ts.map +1 -0
  30. package/dist/node/orchestration/services/implementation/head/index.d.ts +2 -0
  31. package/dist/node/orchestration/services/implementation/head/index.d.ts.map +1 -0
  32. package/dist/node/orchestration/services/implementation/head/submitNewChain.d.ts +10 -0
  33. package/dist/node/orchestration/services/implementation/head/submitNewChain.d.ts.map +1 -0
  34. package/dist/node/orchestration/services/implementation/index.d.ts +1 -1
  35. package/dist/node/orchestration/services/implementation/index.d.ts.map +1 -1
  36. package/dist/node/xl1.mjs +216 -65
  37. package/dist/node/xl1.mjs.map +1 -1
  38. package/package.json +12 -12
  39. package/src/orchestration/actor/implementation/ChainHeadUpdateActor.ts +2 -2
  40. package/src/orchestration/actor/model/Actor.ts +2 -2
  41. package/src/orchestration/archivists/ChainFinalized/local.ts +1 -1
  42. package/src/orchestration/archivists/StakeIntentState/local.ts +1 -1
  43. package/src/orchestration/archivists/lib/localPersistentArchivist.ts +8 -1
  44. package/src/orchestration/services/implementation/head/createBootstrapHead.ts +8 -0
  45. package/src/orchestration/services/implementation/head/createForkedHead/config/getBridgeDestinationDetails.ts +27 -0
  46. package/src/orchestration/services/implementation/head/createForkedHead/config/getBridgeSourceDetails.ts +18 -0
  47. package/src/orchestration/services/implementation/head/createForkedHead/config/getForkDetails.ts +10 -0
  48. package/src/orchestration/services/implementation/head/createForkedHead/config/index.ts +3 -0
  49. package/src/orchestration/services/implementation/head/createForkedHead/createForkedHead.ts +31 -0
  50. package/src/orchestration/services/implementation/head/createForkedHead/getBridgeDestinationObservation.ts +38 -0
  51. package/src/orchestration/services/implementation/head/createForkedHead/getBridgeIntent.ts +50 -0
  52. package/src/orchestration/services/implementation/head/createForkedHead/getBridgeSourceObservation.ts +43 -0
  53. package/src/orchestration/services/implementation/head/createForkedHead/getFirstBlockForNewChain.ts +42 -0
  54. package/src/orchestration/services/implementation/head/createForkedHead/index.ts +1 -0
  55. package/src/orchestration/services/implementation/head/getForkFromBlock.ts +42 -0
  56. package/src/orchestration/services/implementation/head/head.ts +52 -0
  57. package/src/orchestration/services/implementation/head/index.ts +1 -0
  58. package/src/orchestration/services/implementation/head/submitNewChain.ts +27 -0
  59. package/src/orchestration/services/implementation/index.ts +1 -1
  60. package/dist/node/orchestration/services/implementation/head.d.ts.map +0 -1
  61. package/src/orchestration/services/implementation/head.ts +0 -111
  62. /package/dist/node/orchestration/services/implementation/{head.d.ts → head/head.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/xl1-cli-lib",
3
- "version": "1.14.2",
3
+ "version": "1.14.3",
4
4
  "description": "XYO Layer One CLI Library",
5
5
  "homepage": "https://xylabs.com",
6
6
  "bugs": {
@@ -52,17 +52,17 @@
52
52
  "@xylabs/promise": "~5.0.11",
53
53
  "@xylabs/telemetry": "~5.0.11",
54
54
  "@xylabs/typeof": "~5.0.11",
55
- "@xyo-network/archivist-lmdb": "~5.1.0",
56
- "@xyo-network/archivist-memory": "~5.1.0",
57
- "@xyo-network/archivist-model": "~5.1.0",
58
- "@xyo-network/chain-api": "~1.14.2",
59
- "@xyo-network/chain-sdk": "~1.14.2",
60
- "@xyo-network/payload-builder": "~5.1.0",
61
- "@xyo-network/payload-model": "~5.1.0",
62
- "@xyo-network/wallet": "~5.1.0",
63
- "@xyo-network/wallet-model": "~5.1.0",
64
- "@xyo-network/xl1-protocol": "~1.12.14",
65
- "@xyo-network/xl1-protocol-sdk": "~1.14.2",
55
+ "@xyo-network/archivist-lmdb": "~5.1.2",
56
+ "@xyo-network/archivist-memory": "~5.1.2",
57
+ "@xyo-network/archivist-model": "~5.1.2",
58
+ "@xyo-network/chain-api": "~1.14.3",
59
+ "@xyo-network/chain-sdk": "~1.14.3",
60
+ "@xyo-network/payload-builder": "~5.1.2",
61
+ "@xyo-network/payload-model": "~5.1.2",
62
+ "@xyo-network/wallet": "~5.1.2",
63
+ "@xyo-network/wallet-model": "~5.1.2",
64
+ "@xyo-network/xl1-protocol": "~1.12.22",
65
+ "@xyo-network/xl1-protocol-sdk": "~1.14.3",
66
66
  "async-mutex": "~0.5.0",
67
67
  "cosmiconfig": "~9.0.0",
68
68
  "dotenv": "~17.2.2",
@@ -57,9 +57,9 @@ export class ChainHeadUpdateActor extends Actor<ChainHeadUpdateActorParams> {
57
57
  const candidateBlockNumberDisplay = `0x${toHex(candidateBlockNumber)}`
58
58
  const currentBlockNumber = currentHead?.block ?? -1
59
59
  if (candidateBlockNumber > currentBlockNumber) {
60
- this.logger?.log('Found more recent head:', candidateBlockNumberDisplay)
60
+ this.logger?.log('Found more recent head:', candidateBlockNumber, candidateBlockNumberDisplay)
61
61
  await this.chainIterator.updateHead(candidateBlock)
62
- this.logger?.log('Updated head:', candidateBlockNumberDisplay)
62
+ this.logger?.log('Updated head:', candidateBlockNumber, candidateBlockNumberDisplay)
63
63
  }
64
64
  }
65
65
  }
@@ -1,7 +1,7 @@
1
1
  import type { TracerProvider } from '@opentelemetry/api'
2
2
  import { Base, type BaseParams } from '@xylabs/base'
3
3
  import { IdLogger } from '@xylabs/logger'
4
- import { span, spanAsync } from '@xylabs/telemetry'
4
+ import { span, spanRootAsync } from '@xylabs/telemetry'
5
5
  import type { Config } from '@xyo-network/xl1-protocol-sdk'
6
6
 
7
7
  export interface IActor {
@@ -87,7 +87,7 @@ export class Actor<TParams extends ActorParams = ActorParams> extends Base<TPara
87
87
  }
88
88
 
89
89
  async spanAsync<T>(name: string, fn: () => Promise<T>): Promise<T> {
90
- return await spanAsync(`${this.name}:${name}`, fn, this.tracer)
90
+ return await spanRootAsync(`${this.name}:${name}`, fn, this.tracer)
91
91
  }
92
92
 
93
93
  /**
@@ -12,7 +12,7 @@ export const initLocalChainFinalizedArchivist: Initializable<{ config: Config },
12
12
  return await mutex.runExclusive(async () => {
13
13
  if (singleton) return singleton
14
14
  const { root } = config.storage
15
- singleton = await getLocalPersistentArchivist('chain', 'finalized', root)
15
+ singleton = await getLocalPersistentArchivist('local-finalized-chain', 'chain', 'finalized', root)
16
16
  return singleton
17
17
  })
18
18
  }
@@ -13,7 +13,7 @@ export const initLocalStakeIntentStateArchivist: Initializable<{ config: Config
13
13
  return await mutex.runExclusive(async () => {
14
14
  if (singleton) return singleton
15
15
  const { root } = config.storage
16
- singleton = await getLocalPersistentArchivist('stakeIntent', 'state', root)
16
+ singleton = await getLocalPersistentArchivist('local-stake-intent-state', 'stakeIntent', 'state', root)
17
17
  return assertEx(singleton, () => new Error('Failed to initialize stake intent state archivist'))
18
18
  })
19
19
  }
@@ -17,13 +17,20 @@ const DEFAULT_STORAGE_ROOT = Path.join(process.cwd(), '.store')
17
17
  * @param kind The kind of the archivist
18
18
  * @returns an archivist instance
19
19
  */
20
- export const getLocalPersistentArchivist = (dbName: string, storeName: string, storageRoot?: string, kind: StoreKind = 'lmdb'): Promise<ArchivistInstance> => {
20
+ export const getLocalPersistentArchivist = (
21
+ name: string,
22
+ dbName: string,
23
+ storeName: string,
24
+ storageRoot?: string,
25
+ kind: StoreKind = 'lmdb',
26
+ ): Promise<ArchivistInstance> => {
21
27
  switch (kind) {
22
28
  case 'lmdb': {
23
29
  const root = storageRoot ?? DEFAULT_STORAGE_ROOT
24
30
  return LmdbArchivist.create({
25
31
  account: 'random',
26
32
  config: {
33
+ name,
27
34
  clearStoreOnStart: false,
28
35
  dbName,
29
36
  location: getStoreDirectory(dbName, root, 'lmdb'),
@@ -0,0 +1,8 @@
1
+ import { ZERO_ADDRESS } from '@xylabs/hex'
2
+ import { createGenesisBlock } from '@xyo-network/chain-sdk'
3
+ import type { WalletInstance } from '@xyo-network/wallet-model'
4
+ import type { HydratedBlock } from '@xyo-network/xl1-protocol'
5
+
6
+ export const createBootstrapHead = async (account: WalletInstance): Promise<HydratedBlock> => {
7
+ return await createGenesisBlock(account, ZERO_ADDRESS, 10_000_000n, account.address)
8
+ }
@@ -0,0 +1,27 @@
1
+ import { toHex } from '@xylabs/hex'
2
+ import type { BridgeDetailsDestinationFields } from '@xyo-network/xl1-protocol'
3
+
4
+ import { getForkBlockRewardHex } from './getForkDetails.ts'
5
+
6
+ // const bridgeableTokenContract = toHex('0x4865Cb10d55cfB0E60DD2B4F9b888f6a49B76733')
7
+ // const bridgeDestAddress = toHex('0xe53218d47913b5f9E58bb74F0a0eD790bbF21972')
8
+ // const destConfirmation = toHex('0x4fa05d7e799f36f1d45c441a4866eb4570a00a753a39377f404a8d113c01a657') // Eth TX for mint
9
+ const ethChainId = toHex('0x1')
10
+ const bridgeableTokenContract = toHex('0x7789e11BB83b398A8Cca8E8D582B33F91499D6f5')
11
+ const bridgeDestAddress = toHex('0xe53218d47913b5f9E58bb74F0a0eD790bbF21972')
12
+ const destConfirmation = toHex('0x772ee028f9ad291ec692912c1a33ecd4409e71383fe06f6ebf39f7cdbb779069') // Eth TX for mint
13
+
14
+ export const getBridgeDestChainId = () => ethChainId
15
+ export const getBridgeDestToken = () => bridgeableTokenContract
16
+ export const getBridgeDestAddress = () => bridgeDestAddress
17
+ export const getBridgeDestAmount = () => getForkBlockRewardHex()
18
+ export const getBridgeDestConfirmation = () => destConfirmation
19
+
20
+ export const getBridgeDestinationDetails = (): BridgeDetailsDestinationFields => {
21
+ return {
22
+ dest: getBridgeDestChainId(),
23
+ destToken: getBridgeDestToken(),
24
+ destAddress: getBridgeDestAddress(),
25
+ destAmount: getBridgeDestAmount(),
26
+ }
27
+ }
@@ -0,0 +1,18 @@
1
+ import type { AccountInstance } from '@xyo-network/account-model'
2
+ import type { BridgeDetailsSourceFields, ChainService } from '@xyo-network/xl1-protocol'
3
+
4
+ import { getForkBlockRewardHex } from './getForkDetails.ts'
5
+
6
+ export const getBridgeSrcChainId = (chainService: ChainService) => chainService.chainId
7
+ export const getBridgeSrcAddress = (account: AccountInstance) => account.address
8
+ export const getBridgeSrcToken = (chainService: ChainService) => chainService.chainId
9
+ export const getBridgeSrcAmount = () => getForkBlockRewardHex()
10
+
11
+ export const getBridgeSourceDetails = (account: AccountInstance, chainService: ChainService): BridgeDetailsSourceFields => {
12
+ return {
13
+ src: getBridgeSrcChainId(chainService),
14
+ srcAddress: getBridgeSrcAddress(account),
15
+ srcToken: getBridgeSrcToken(chainService),
16
+ srcAmount: getBridgeSrcAmount(),
17
+ }
18
+ }
@@ -0,0 +1,10 @@
1
+ import { type Hex, toHex } from '@xylabs/hex'
2
+ import { AttoXL1ConvertFactor } from '@xyo-network/xl1-protocol'
3
+
4
+ export const getForkBlockReward = (): bigint => {
5
+ return 18_000_000_000n * AttoXL1ConvertFactor.xl1 // 18b XL1 in AttoXL1
6
+ }
7
+
8
+ export const getForkBlockRewardHex = (): Hex => {
9
+ return toHex(getForkBlockReward())
10
+ }
@@ -0,0 +1,3 @@
1
+ export * from './getBridgeDestinationDetails.ts'
2
+ export * from './getBridgeSourceDetails.ts'
3
+ export * from './getForkDetails.ts'
@@ -0,0 +1,31 @@
1
+ import type { WithHashStorageMeta } from '@xyo-network/payload-model'
2
+ import type { WalletInstance } from '@xyo-network/wallet-model'
3
+ import type {
4
+ BlockBoundWitness, ChainService, HydratedBlock,
5
+ } from '@xyo-network/xl1-protocol'
6
+
7
+ import { getBridgeDestinationObservation } from './getBridgeDestinationObservation.ts'
8
+ import { getBridgeIntent } from './getBridgeIntent.ts'
9
+ import { getBridgeSourceObservation } from './getBridgeSourceObservation.ts'
10
+ import { getFirstBlockForNewChain } from './getFirstBlockForNewChain.ts'
11
+
12
+ export const createForkedHead = async (
13
+ forkFromBlock: WithHashStorageMeta<BlockBoundWitness>,
14
+ account: WalletInstance,
15
+ chainService: ChainService,
16
+ ): Promise<HydratedBlock[]> => {
17
+ const chain: HydratedBlock[] = []
18
+ // Build the first block for the new chain
19
+ const firstBlockForNewChain = await getFirstBlockForNewChain(forkFromBlock, account, chainService)
20
+ chain.push(firstBlockForNewChain)
21
+ // Build the bridge intent
22
+ const bridgeIntent = await getBridgeIntent(firstBlockForNewChain, account, chainService)
23
+ chain.push(bridgeIntent)
24
+ // Build the bridge source observation
25
+ const bridgeSourceObservation = await getBridgeSourceObservation(bridgeIntent, account, chainService)
26
+ chain.push(bridgeSourceObservation)
27
+ // Build the bridge destination observation
28
+ const bridgeDestinationObservation = await getBridgeDestinationObservation(bridgeSourceObservation, account, chainService)
29
+ chain.push(bridgeDestinationObservation)
30
+ return chain
31
+ }
@@ -0,0 +1,38 @@
1
+ import { BridgeDestinationObservationSchema, buildNextBlock } from '@xyo-network/chain-sdk'
2
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
3
+ import type { WalletInstance } from '@xyo-network/wallet-model'
4
+ import type {
5
+ BridgeDestinationObservation, BridgeDestinationObservationFields, ChainService, HydratedBlock,
6
+ } from '@xyo-network/xl1-protocol'
7
+
8
+ import {
9
+ getBridgeDestConfirmation, getBridgeDestinationDetails, getBridgeSourceDetails,
10
+ } from './config/index.ts'
11
+
12
+ /**
13
+ * Get the bridge destination observation for a given block.
14
+ * @param previousBlock The previous block to base the observation on.
15
+ * @param account The wallet account to use for the observation.
16
+ * @param chainService The chain service to use for the observation.
17
+ */
18
+ export const getBridgeDestinationObservation = async (
19
+ previousBlock: HydratedBlock,
20
+ account: WalletInstance,
21
+ chainService: ChainService,
22
+ ): Promise<HydratedBlock> => {
23
+ const bridgeDestinationObservationFields: BridgeDestinationObservationFields = {
24
+ ...getBridgeSourceDetails(account, chainService),
25
+ ...getBridgeDestinationDetails(),
26
+ destConfirmation: getBridgeDestConfirmation(),
27
+ }
28
+ const bridgeDestinationObservation = new PayloadBuilder<BridgeDestinationObservation>({ schema: BridgeDestinationObservationSchema })
29
+ .fields(bridgeDestinationObservationFields)
30
+ .build()
31
+
32
+ return await buildNextBlock(
33
+ previousBlock[0],
34
+ [],
35
+ [bridgeDestinationObservation],
36
+ [account],
37
+ )
38
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ BridgeIntentSchema, buildNextBlock, createTransferPayload, XYO_BRIDGE_ADDRESS,
3
+ } from '@xyo-network/chain-sdk'
4
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
5
+ import type { WalletInstance } from '@xyo-network/wallet-model'
6
+ import type {
7
+ BridgeIntent, BridgeIntentFields, ChainService, HydratedBlock,
8
+ } from '@xyo-network/xl1-protocol'
9
+
10
+ import {
11
+ getBridgeDestinationDetails, getBridgeSourceDetails, getForkBlockReward,
12
+ } from './config/index.ts'
13
+
14
+ /**
15
+ * Get the bridge intent for a given block.
16
+ * @param previousBlock The previous block to base the intent on.
17
+ * @param account The wallet account to use for the intent.
18
+ * @param chainService The chain service to use for the intent.
19
+ */
20
+ export const getBridgeIntent = async (
21
+ previousBlock: HydratedBlock,
22
+ account: WalletInstance,
23
+ chainService: ChainService,
24
+ ): Promise<HydratedBlock> => {
25
+ // Create transfer to bridge address
26
+ const transferPayload = createTransferPayload(
27
+ account.address,
28
+ { [XYO_BRIDGE_ADDRESS]: getForkBlockReward() },
29
+ )
30
+
31
+ // Use timestamp as nonce
32
+ const nonce = `${Date.now()}`
33
+
34
+ // Create Bridge Intent
35
+ const bridgeIntentFields: BridgeIntentFields = {
36
+ ...getBridgeSourceDetails(account, chainService),
37
+ ...getBridgeDestinationDetails(),
38
+ nonce,
39
+ }
40
+ const bridgeIntent = new PayloadBuilder<BridgeIntent>({ schema: BridgeIntentSchema })
41
+ .fields(bridgeIntentFields)
42
+ .build()
43
+
44
+ return await buildNextBlock(
45
+ previousBlock[0],
46
+ [],
47
+ [bridgeIntent, transferPayload],
48
+ [account],
49
+ )
50
+ }
@@ -0,0 +1,43 @@
1
+ import { BridgeSourceObservationSchema, buildNextBlock } from '@xyo-network/chain-sdk'
2
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
3
+ import type { WalletInstance } from '@xyo-network/wallet-model'
4
+ import type {
5
+ BridgeSourceObservation, BridgeSourceObservationFields, ChainService, HydratedBlock,
6
+ } from '@xyo-network/xl1-protocol'
7
+
8
+ import { getBridgeDestinationDetails, getBridgeSourceDetails } from './config/index.ts'
9
+
10
+ /**
11
+ * Get the bridge source observation for a given block.
12
+ * @param previousBlock The previous block to base the observation on.
13
+ * @param account The wallet account to use for the observation.
14
+ * @param chainService The chain service to use for the observation.
15
+ */
16
+ export const getBridgeSourceObservation = async (
17
+ previousBlock: HydratedBlock,
18
+ account: WalletInstance,
19
+ chainService: ChainService,
20
+ ): Promise<HydratedBlock> => {
21
+ // TODO: Find actual transfer TX from previous block/source intent?
22
+ // This hash should contain both the source intent and the transfer to the bridge address
23
+ // As the block is itself both a block and a transaction, we can use its hash for now
24
+ const srcTxHash = previousBlock[0]._hash
25
+
26
+ const bridgeSourceObservationFields: BridgeSourceObservationFields = {
27
+ ...getBridgeSourceDetails(account, chainService),
28
+ ...getBridgeDestinationDetails(),
29
+ // Observation
30
+ srcConfirmation: srcTxHash,
31
+ }
32
+
33
+ const bridgeSourceObservation = new PayloadBuilder<BridgeSourceObservation>({ schema: BridgeSourceObservationSchema })
34
+ .fields(bridgeSourceObservationFields)
35
+ .build()
36
+
37
+ return await buildNextBlock(
38
+ previousBlock[0],
39
+ [],
40
+ [bridgeSourceObservation],
41
+ [account],
42
+ )
43
+ }
@@ -0,0 +1,42 @@
1
+ import type { BuildNextBlockOptions } from '@xyo-network/chain-sdk'
2
+ import { buildBlock } from '@xyo-network/chain-sdk'
3
+ import type { WithHashStorageMeta } from '@xyo-network/payload-model'
4
+ import type { WalletInstance } from '@xyo-network/wallet-model'
5
+ import type {
6
+ BlockBoundWitness, ChainService, HydratedBlock,
7
+ } from '@xyo-network/xl1-protocol'
8
+
9
+ import { getForkBlockReward } from './config/index.ts'
10
+
11
+ /**
12
+ * Get the first block for the new forked chain.
13
+ * @param forkBlock The block to fork from
14
+ * @param account The wallet account to sign the block
15
+ * @param chainService The chain service instance
16
+ * @returns The first block for the new forked chain
17
+ */
18
+ export const getFirstBlockForNewChain = async (
19
+ forkBlock: WithHashStorageMeta<BlockBoundWitness>,
20
+ account: WalletInstance,
21
+ chainService: ChainService,
22
+ ): Promise<HydratedBlock> => {
23
+ const {
24
+ _hash: previousBlockHash, block: previousBlockNumber, step_hashes: previousStepHashes, protocol,
25
+ } = forkBlock
26
+ const chainId = chainService.chainId
27
+ const forkBlockReward = getForkBlockReward()
28
+ const options: BuildNextBlockOptions = {
29
+ blockPayloads: [],
30
+ chainId,
31
+ previousBlockHash,
32
+ previousBlockNumber,
33
+ previousStepHashes,
34
+ signers: [account],
35
+ txs: [],
36
+ protocol,
37
+ reward: forkBlockReward,
38
+ rewardAddress: account.address,
39
+ }
40
+ // NOTE: Can not use buildNextBlock because we need to control the chain id change here
41
+ return await buildBlock(options)
42
+ }
@@ -0,0 +1 @@
1
+ export * from './createForkedHead.ts'
@@ -0,0 +1,42 @@
1
+ import type { Address, Hash } from '@xylabs/hex'
2
+ import { asHash, hexFromBigInt } from '@xylabs/hex'
3
+ import { isDefined } from '@xylabs/typeof'
4
+ import type { ArchivistInstance } from '@xyo-network/archivist-model'
5
+ import { isBlockBoundWitnessWithHashStorageMeta } from '@xyo-network/chain-sdk'
6
+ import type { WithHashStorageMeta, WithStorageMeta } from '@xyo-network/payload-model'
7
+ import type { BlockBoundWitness, ChainService } from '@xyo-network/xl1-protocol'
8
+
9
+ /**
10
+ * Determine if the chain should fork based on the current head and chain configuration.
11
+ * @param head The current head block
12
+ * @param chainService The chain service instance
13
+ * @param chainArchivist The chain archivist instance
14
+ * @returns The block to fork from if the chain should fork, undefined otherwise
15
+ */
16
+ export const getForkFromBlock = async (
17
+ head: WithStorageMeta<BlockBoundWitness>,
18
+ chainService: ChainService,
19
+ chainArchivist: ArchivistInstance,
20
+ ): Promise<WithHashStorageMeta<BlockBoundWitness> | undefined> => {
21
+ // If the head's chain doesn't match our chainId, we may need to fork
22
+ if (head.chain !== chainService.chainId) {
23
+ // Get the forked at hash from the chain service
24
+ const forkedAtBigInt = await chainService.forkedAtHash()
25
+ const forkedAtHex = hexFromBigInt(forkedAtBigInt) // Validate it's a proper hex string
26
+ const forkedAtHash: Hash | undefined = asHash(forkedAtHex)
27
+ if (isDefined(forkedAtHash)) {
28
+ // If we have a forkedAtHash, we need to check if the correct block exists
29
+ const [forkedAtBlock] = await chainArchivist.get([forkedAtHash])
30
+ const forkedChainId: Address = await chainService.forkedChainId()
31
+ const forkedAtBlockNumber: number = Number(await chainService.forkedAtBlockNumber())
32
+ // If we found the block hash we should fork at, check if it's a valid BlockBoundWitness
33
+ if (isBlockBoundWitnessWithHashStorageMeta(forkedAtBlock)
34
+ // And it is on the correct forked from chain
35
+ && forkedAtBlock.chain === forkedChainId
36
+ // And it is the correct forked at block number
37
+ && forkedAtBlock.block === forkedAtBlockNumber) {
38
+ return forkedAtBlock
39
+ }
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,52 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import type { Promisable } from '@xylabs/promise'
3
+ import { isDefined } from '@xylabs/typeof'
4
+ import type { ArchivistInstance } from '@xyo-network/archivist-model'
5
+ import { findMostRecentBlock } from '@xyo-network/chain-sdk'
6
+ import type { WithStorageMeta } from '@xyo-network/payload-model'
7
+ import type { WalletInstance } from '@xyo-network/wallet-model'
8
+ import type {
9
+ BlockBoundWitness, ChainService, Initializable,
10
+ } from '@xyo-network/xl1-protocol'
11
+
12
+ import { createBootstrapHead } from './createBootstrapHead.ts'
13
+ import { createForkedHead } from './createForkedHead/index.ts'
14
+ import { getForkFromBlock } from './getForkFromBlock.ts'
15
+ import { submitNewChain } from './submitNewChain.ts'
16
+
17
+ let headSingleton: Promisable<WithStorageMeta<BlockBoundWitness>> | undefined
18
+
19
+ export const initHead: Initializable<{
20
+ account: WalletInstance
21
+ chainArchivist: ArchivistInstance
22
+ chainService: ChainService
23
+ chainSubmissionsArchivistWrite: ArchivistInstance
24
+ }, WithStorageMeta<BlockBoundWitness>>
25
+ = async (params): Promise<WithStorageMeta<BlockBoundWitness>> => {
26
+ const {
27
+ account, chainArchivist, chainSubmissionsArchivistWrite, chainService,
28
+ } = params
29
+ if (headSingleton) return headSingleton
30
+ let head = await findMostRecentBlock(chainArchivist)
31
+
32
+ // If there is a head
33
+ if (head) {
34
+ // If there is a head but check if it matches our chainId or we should fork from it
35
+ const forkFromBlock = await getForkFromBlock(head, chainService, chainArchivist)
36
+ // If we should fork form a block in the current chain
37
+ if (isDefined(forkFromBlock)) {
38
+ // Create a new chain from the fork from block
39
+ const chain = await createForkedHead(forkFromBlock, account, chainService)
40
+ await submitNewChain(chain, chainArchivist, chainSubmissionsArchivistWrite)
41
+ const newBlock = assertEx(chain.at(-1), () => new Error('Failed to get new head after forking'))
42
+ head = newBlock[0]
43
+ }
44
+ } else {
45
+ // If there is no head, create one
46
+ const genesisBlock = await createBootstrapHead(account)
47
+ await submitNewChain([genesisBlock], chainArchivist, chainSubmissionsArchivistWrite)
48
+ head = genesisBlock[0]
49
+ }
50
+ headSingleton = head
51
+ return headSingleton
52
+ }
@@ -0,0 +1 @@
1
+ export * from './head.ts'
@@ -0,0 +1,27 @@
1
+ import { delay } from '@xylabs/delay'
2
+ import type { ArchivistInstance, WriteArchivist } from '@xyo-network/archivist-model'
3
+ import type { HydratedBlock } from '@xyo-network/xl1-protocol'
4
+ import { flattenHydratedBlock } from '@xyo-network/xl1-protocol-sdk'
5
+
6
+ /**
7
+ * Submit a new chain to the archivist.
8
+ * @param chain The new chain to submit
9
+ * @param chainArchivist The chain archivist instance
10
+ * @param chainSubmissionsArchivistWrite The chain submissions archivist instance
11
+ */
12
+ export const submitNewChain = async (
13
+ chain: HydratedBlock[],
14
+ chainArchivist: ArchivistInstance,
15
+ chainSubmissionsArchivistWrite: WriteArchivist,
16
+ ): Promise<void> => {
17
+ for (const block of chain) {
18
+ const [bw] = block
19
+ await chainSubmissionsArchivistWrite.insert(flattenHydratedBlock(block))
20
+ // Wait for block to show up in finalized archivist
21
+ while (true) {
22
+ const result = await chainArchivist.get([bw._hash])
23
+ if (result.length > 0) break
24
+ await delay(1000) // Wait 1 second before retrying
25
+ }
26
+ }
27
+ }
@@ -2,7 +2,7 @@ export * from './account.ts'
2
2
  export * from './balance.ts'
3
3
  export * from './chain/index.ts'
4
4
  export * from './evm/index.ts'
5
- export * from './head.ts'
5
+ export * from './head/index.ts'
6
6
  export * from './iterator.ts'
7
7
  export * from './pendingTransactions.ts'
8
8
  export * from './producer.ts'
@@ -1 +0,0 @@
1
- {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../../../../src/orchestration/services/implementation/head.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAKrE,OAAO,KAAK,EAAuB,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,KAAK,EACV,iBAAiB,EAAS,YAAY,EAAE,aAAa,EACtD,MAAM,2BAA2B,CAAA;AA0DlC,eAAO,MAAM,QAAQ,EAAE,aAAa,CAAC;IACnC,OAAO,EAAE,cAAc,CAAA;IACvB,cAAc,EAAE,iBAAiB,CAAA;IACjC,YAAY,EAAE,YAAY,CAAA;IAC1B,8BAA8B,EAAE,iBAAiB,CAAA;CAClD,EAAE,eAAe,CAAC,iBAAiB,CAAC,CA+BnC,CAAA"}
@@ -1,111 +0,0 @@
1
- import { delay } from '@xylabs/delay'
2
- import type { Address, Hash } from '@xylabs/hex'
3
- import {
4
- asHash, hexFromBigInt, ZERO_ADDRESS,
5
- } from '@xylabs/hex'
6
- import type { Promisable } from '@xylabs/promise'
7
- import { isDefined } from '@xylabs/typeof'
8
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
9
- import type { BuildNextBlockOptions } from '@xyo-network/chain-sdk'
10
- import {
11
- buildBlock, createGenesisBlock, findMostRecentBlock, isBlockBoundWitnessWithHashStorageMeta,
12
- } from '@xyo-network/chain-sdk'
13
- import type { WithHashStorageMeta, WithStorageMeta } from '@xyo-network/payload-model'
14
- import type { WalletInstance } from '@xyo-network/wallet-model'
15
- import type {
16
- BlockBoundWitness, Chain, ChainService, Initializable,
17
- } from '@xyo-network/xl1-protocol'
18
- import { flattenHydratedBlock } from '@xyo-network/xl1-protocol-sdk'
19
-
20
- let headSingleton: Promisable<WithStorageMeta<BlockBoundWitness>> | undefined
21
-
22
- const createBootstrapHead = async (
23
- chainSubmissionsArchivistWrite: ArchivistInstance,
24
- chainArchivist: ArchivistInstance,
25
- account: WalletInstance,
26
- ): Promise<WithStorageMeta<BlockBoundWitness>> => {
27
- const block = await createGenesisBlock(account, ZERO_ADDRESS, 10_000_000n, account.address)
28
- const [bw] = block
29
- await chainSubmissionsArchivistWrite.insert(flattenHydratedBlock(block))
30
- // Wait for block to show up in finalized archivist
31
- while (true) {
32
- const result = await chainArchivist.get([bw._hash])
33
- if (result.length > 0) break
34
- await delay(1000) // Wait 1 second before retrying
35
- }
36
- return bw
37
- }
38
- const createForkedHead = async (
39
- chainSubmissionsArchivistWrite: ArchivistInstance,
40
- chainArchivist: ArchivistInstance,
41
- account: WalletInstance,
42
- forkBlock: WithHashStorageMeta<BlockBoundWitness>,
43
- chainId: Chain,
44
- ): Promise<WithStorageMeta<BlockBoundWitness>> => {
45
- const {
46
- _hash: previousBlockHash, block: previousBlockNumber, step_hashes: previousStepHashes, protocol,
47
- } = forkBlock
48
- // TODO: Configurable per fork to allow different rewards
49
- const forkBlockReward = 0n
50
- const forkBlockRewardAddress = account.address
51
- const options: BuildNextBlockOptions = {
52
- blockPayloads: [],
53
- chainId,
54
- previousBlockHash,
55
- previousBlockNumber,
56
- previousStepHashes,
57
- signers: [account],
58
- txs: [],
59
- protocol,
60
- reward: forkBlockReward,
61
- rewardAddress: forkBlockRewardAddress,
62
- }
63
- const block = await buildBlock(options)
64
- const [bw] = block
65
- await chainSubmissionsArchivistWrite.insert(flattenHydratedBlock(block))
66
- // Wait for block to show up in finalized archivist
67
- while (true) {
68
- const result = await chainArchivist.get([bw._hash])
69
- if (result.length > 0) break
70
- await delay(1000) // Wait 1 second before retrying
71
- }
72
- return bw
73
- }
74
-
75
- export const initHead: Initializable<{
76
- account: WalletInstance
77
- chainArchivist: ArchivistInstance
78
- chainService: ChainService
79
- chainSubmissionsArchivistWrite: ArchivistInstance
80
- }, WithStorageMeta<BlockBoundWitness>>
81
- = async (params): Promise<WithStorageMeta<BlockBoundWitness>> => {
82
- const {
83
- account, chainArchivist, chainSubmissionsArchivistWrite, chainService,
84
- } = params
85
- if (headSingleton) return headSingleton
86
- let head = await findMostRecentBlock(chainArchivist)
87
-
88
- // If there is no head, create one
89
- if (!head) {
90
- head = await createBootstrapHead(chainSubmissionsArchivistWrite, chainArchivist, account)
91
- // If there is a head but it doesn't match our chainId, check if we should fork
92
- } else if (head.chain !== chainService.chainId) {
93
- const forkedAtBigInt = await chainService.forkedAtHash()
94
- const forkedAtHex = hexFromBigInt(forkedAtBigInt) // Validate it's a proper hex string
95
- const forkedAtHash: Hash | undefined = asHash(forkedAtHex)
96
- if (isDefined(forkedAtHash)) {
97
- // If we have a forkedAtHash, we need to check if the correct block exists
98
- const [forkBlock] = await chainArchivist.get([forkedAtHash])
99
- const forkedChainId: Address = await chainService.forkedChainId()
100
- const forkedAtBlockNumber: number = Number(await chainService.forkedAtBlockNumber())
101
- // If we found the block we should fork at
102
- if (isBlockBoundWitnessWithHashStorageMeta(forkBlock)
103
- // and it matches the expected values
104
- && forkBlock.chain === forkedChainId && forkBlock.block === forkedAtBlockNumber) {
105
- head = await createForkedHead(chainSubmissionsArchivistWrite, chainArchivist, account, forkBlock, chainService.chainId)
106
- }
107
- }
108
- }
109
- headSingleton = head
110
- return headSingleton
111
- }