@xyo-network/chain-producer 1.20.14 → 1.20.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,112 +1,24 @@
1
- # @xyo-network/chain-api
1
+ # @xyo-network/chain-producer
2
2
 
3
- [![logo][]](https://xyo.network)
3
+ [![npm][npm-badge]][npm-link]
4
+ [![license][license-badge]][license-link]
4
5
 
5
- [![npm-badge][]][npm-link]
6
- [![npm-downloads-badge][]][npm-link]
7
- [![jsdelivr-badge][]][jsdelivr-link]
8
- [![npm-license-badge][]](LICENSE)
9
- [![socket-badge][]][socket-link]
6
+ > XYO Layer One Producer
10
7
 
11
- XYO Layer One API
8
+ ## Install
12
9
 
13
-
14
-
15
- ## Reference
16
-
17
- **@xyo-network/chain-api**
18
-
19
- ***
20
-
21
- ## Functions
22
-
23
- - [getApp](#functions/getApp)
24
- - [getServer](#functions/getServer)
25
-
26
- ### functions
27
-
28
- ### <a id="getApp"></a>getApp
29
-
30
- [**@xyo-network/chain-api**](#../README)
31
-
32
- ***
33
-
34
- ```ts
35
- function getApp(
36
- node,
37
- transferSummaryMap,
38
- stakedChainContext,
39
- initRewardsCache?): Promise<Express>;
40
- ```
41
-
42
- ## Parameters
43
-
44
- ### node
45
-
46
- `NodeInstance`
47
-
48
- ### transferSummaryMap
49
-
50
- `MapType`\<`string`, `WithStorageMeta`\<`TransfersStepSummary`\>\>
51
-
52
- ### stakedChainContext
53
-
54
- `StakedChainContextRead`
55
-
56
- ### initRewardsCache?
57
-
58
- `boolean`
59
-
60
- ## Returns
61
-
62
- `Promise`\<`Express`\>
63
-
64
- ### <a id="getServer"></a>getServer
65
-
66
- [**@xyo-network/chain-api**](#../README)
67
-
68
- ***
69
-
70
- ```ts
71
- function getServer(context): Promise<Server<typeof IncomingMessage, typeof ServerResponse>>;
10
+ ```sh
11
+ {{pm}} add {{name}}
72
12
  ```
73
13
 
74
- ## Parameters
75
-
76
- ### context
77
-
78
- `GetServerContext`
79
-
80
- ## Returns
81
-
82
- `Promise`\<`Server`\<*typeof* `IncomingMessage`, *typeof* `ServerResponse`\>\>
83
-
84
-
85
- ## Maintainers
86
-
87
- - [Arie Trouw](https://github.com/arietrouw) ([arietrouw.com](https://arietrouw.com))
88
- - [Matt Jones](https://github.com/jonesmac)
89
- - [Joel Carter](https://github.com/JoelBCarter)
90
- - [Jordan Trouw](https://github.com/jordantrouw)
91
14
 
92
15
  ## License
93
16
 
94
- > See the [LICENSE](LICENSE) file for license details
95
-
96
- ## Credits
97
-
98
- [Made with 🔥 and ❄️ by XYO](https://xyo.network)
99
-
100
- [logo]: https://cdn.xy.company/img/brand/XYO_full_colored.png
101
-
102
- [npm-badge]: https://img.shields.io/npm/v/@xyo-network/chain-api.svg
103
- [npm-link]: https://www.npmjs.com/package/@xyo-network/chain-api
17
+ See the [LICENSE](LICENSE) file for license rights and limitations (LGPL-3.0-only).
104
18
 
105
- [npm-downloads-badge]: https://img.shields.io/npm/dw/@xyo-network/chain-api
106
- [npm-license-badge]: https://img.shields.io/npm/l/@xyo-network/chain-api
107
19
 
108
- [jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/@xyo-network/chain-api/badge
109
- [jsdelivr-link]: https://www.jsdelivr.com/package/npm/@xyo-network/chain-api
110
20
 
111
- [socket-badge]: https://socket.dev/api/badge/npm/package/@xyo-network/chain-api
112
- [socket-link]: https://socket.dev/npm/package/@xyo-network/chain-api
21
+ [npm-badge]: https://img.shields.io/npm/v/@xyo-network/chain-producer.svg
22
+ [npm-link]: https://www.npmjs.com/package/@xyo-network/chain-producer
23
+ [license-badge]: https://img.shields.io/npm/l/@xyo-network/chain-producer.svg
24
+ [license-link]: https://github.com/xylabs/sdk-js/blob/main/LICENSE
@@ -17,7 +17,7 @@ export declare class ProducerActor extends ActorV3<ProducerActorParams> {
17
17
  /**
18
18
  * The window (in blocks) before expiration to attempt redeclaration of producer intent.
19
19
  */
20
- static readonly RedeclarationWindow = 10000;
20
+ static readonly RedeclarationWindow = 1000;
21
21
  protected _lastProducedBlock?: HydratedBlockWithHashMeta;
22
22
  protected _lastRedeclarationIntent?: ChainStakeIntent;
23
23
  protected _metricAttributes?: Attributes;
@@ -1 +1 @@
1
- {"version":3,"file":"ProducerActor.d.ts","sourceRoot":"","sources":["../../src/ProducerActor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAK7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAGjE,OAAO,KAAK,EACV,aAAa,EAEb,mBAAmB,EAAW,gBAAgB,EAAE,yBAAyB,EAAE,cAAc,EAC1F,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,oBAAoB,EAA+B,OAAO,EAAoB,WAAW,EAAsB,WAAW,EACJ,aAAa,EACnI,aAAa,EAAwB,iBAAiB,EACvD,MAAM,sBAAsB,CAAA;AAG7B,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC;IAC9C,MAAM,EAAE,cAAc,CAAA;CACvB,CAAC,CAAA;AAcF,qBACa,aAAc,SAAQ,OAAO,CAAC,mBAAmB,CAAC;IAC7D;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,4BAA4B,QAAO;IAEnD;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,yBAAyB,SAAkD;IAE3F;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,mBAAmB,SAAS;IAE5C,SAAS,CAAC,kBAAkB,CAAC,EAAE,yBAAyB,CAAA;IACxD,SAAS,CAAC,wBAAwB,CAAC,EAAE,gBAAgB,CAAA;IACrD,SAAS,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAA;IACxC,SAAS,CAAC,qCAAqC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACrE,SAAS,CAAC,mCAAmC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACnE,SAAS,CAAC,4BAA4B,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAC5D,SAAS,CAAC,6BAA6B,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAE7D,OAAO,CAAC,qBAAqB,CAAC,CAAsB;IACpD,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,oBAAoB,CAAC,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,mBAAmB,CAAC,CAAQ;IACpC,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,kBAAkB,CAAc;IACxC,OAAO,CAAC,kBAAkB,CAAC,CAAmB;IAE9C,IAAa,MAAM,oCAElB;IAED,SAAS,KAAK,oBAAoB,yBAEjC;IAED,SAAS,KAAK,WAAW,gBAExB;IAED,SAAS,KAAK,WAAW,+BAExB;IAED,SAAS,KAAK,mBAAmB,wBAEhC;IAED,SAAS,KAAK,OAAO,wCAEpB;IAED,SAAS,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAEnB;IAED,SAAS,KAAK,aAAa,kBAE1B;IAED,SAAS,KAAK,aAAa,kBAE1B;IAED,SAAS,KAAK,iBAAiB,sBAE9B;IAEc,aAAa;IAgFb,YAAY;IAe3B,SAAS,CAAC,iDAAiD,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;cAIzE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;cAgF7B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;cAiEhC,yBAAyB,CAAC,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;cAkB7G,sBAAsB,IAAI,OAAO,CAAC,OAAO,CAAC;cAe1C,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;CAWzD"}
1
+ {"version":3,"file":"ProducerActor.d.ts","sourceRoot":"","sources":["../../src/ProducerActor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAK7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAGjE,OAAO,KAAK,EACV,aAAa,EAEb,mBAAmB,EAAW,gBAAgB,EAAE,yBAAyB,EAAE,cAAc,EAC1F,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,oBAAoB,EAA+B,OAAO,EAAoB,WAAW,EAAsB,WAAW,EACJ,aAAa,EACnI,aAAa,EAAwB,iBAAiB,EACvD,MAAM,sBAAsB,CAAA;AAG7B,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC;IAC9C,MAAM,EAAE,cAAc,CAAA;CACvB,CAAC,CAAA;AAcF,qBACa,aAAc,SAAQ,OAAO,CAAC,mBAAmB,CAAC;IAC7D;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,4BAA4B,QAAO;IAEnD;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,yBAAyB,SAAkD;IAE3F;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,mBAAmB,QAAO;IAE1C,SAAS,CAAC,kBAAkB,CAAC,EAAE,yBAAyB,CAAA;IACxD,SAAS,CAAC,wBAAwB,CAAC,EAAE,gBAAgB,CAAA;IACrD,SAAS,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAA;IACxC,SAAS,CAAC,qCAAqC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACrE,SAAS,CAAC,mCAAmC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACnE,SAAS,CAAC,4BAA4B,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAC5D,SAAS,CAAC,6BAA6B,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAE7D,OAAO,CAAC,qBAAqB,CAAC,CAAsB;IACpD,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,oBAAoB,CAAC,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,mBAAmB,CAAC,CAAQ;IACpC,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,kBAAkB,CAAc;IACxC,OAAO,CAAC,kBAAkB,CAAC,CAAmB;IAE9C,IAAa,MAAM,oCAElB;IAED,SAAS,KAAK,oBAAoB,yBAEjC;IAED,SAAS,KAAK,WAAW,gBAExB;IAED,SAAS,KAAK,WAAW,+BAExB;IAED,SAAS,KAAK,mBAAmB,wBAEhC;IAED,SAAS,KAAK,OAAO,wCAEpB;IAED,SAAS,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAEnB;IAED,SAAS,KAAK,aAAa,kBAE1B;IAED,SAAS,KAAK,aAAa,kBAE1B;IAED,SAAS,KAAK,iBAAiB,sBAE9B;IAEc,aAAa;IAgFb,YAAY;IAe3B,SAAS,CAAC,iDAAiD,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;cAIzE,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;cAgF7B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;cAiEhC,yBAAyB,CAAC,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;cAkB7G,sBAAsB,IAAI,OAAO,CAAC,OAAO,CAAC;cAe1C,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;CAWzD"}
@@ -35,7 +35,7 @@ var ProducerActor = class _ProducerActor extends ActorV3 {
35
35
  /**
36
36
  * The window (in blocks) before expiration to attempt redeclaration of producer intent.
37
37
  */
38
- static RedeclarationWindow = 1e4;
38
+ static RedeclarationWindow = 1e3;
39
39
  _lastProducedBlock;
40
40
  _lastRedeclarationIntent;
41
41
  _metricAttributes;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ProducerActor.ts","../../src/runProducer.ts"],"sourcesContent":["import type { Attributes, Counter } from '@opentelemetry/api'\nimport {\n assertEx, creatable, isDefined, isUndefined,\n toHex,\n} from '@xylabs/sdk-js'\nimport { ProducerConfig } from '@xyo-network/chain-orchestration'\nimport { SimpleBlockRunner } from '@xyo-network/chain-services'\nimport { WithHashMeta } from '@xyo-network/sdk-js'\nimport type {\n ActorParamsV3,\n BlockBoundWitness,\n ChainContractViewer, ChainId, ChainStakeIntent, HydratedBlockWithHashMeta, XL1BlockNumber,\n} from '@xyo-network/xl1-sdk'\nimport {\n AccountBalanceViewer, AccountBalanceViewerMoniker, ActorV3, asXL1BlockNumber, BlockRunner, BlockRunnerMoniker, BlockViewer, BlockViewerMoniker,\n buildTransaction, ChainContractViewerMoniker, createDeclarationIntent, FinalizationViewer, FinalizationViewerMoniker, MempoolRunner, MempoolRunnerMoniker,\n MempoolViewer, MempoolViewerMoniker, StakeTotalsViewer, StakeTotalsViewerMoniker,\n} from '@xyo-network/xl1-sdk'\nimport { Mutex } from 'async-mutex'\n\nexport type ProducerActorParams = ActorParamsV3<{\n config: ProducerConfig\n}>\n\nconst SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true\nconst TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds\n\n/**\n * Formats a hydrated block with hash meta into a string representation of its hash and block number.\n * @param blockBoundWitness The hydrated block with hash meta to format\n * @returns The formatted block reference string\n */\nconst toFormattedBlockReference = (blockBoundWitness: WithHashMeta<BlockBoundWitness>): string => {\n return `${blockBoundWitness.block} [${toHex(blockBoundWitness._hash, { prefix: true })}]`\n}\n\n@creatable()\nexport class ProducerActor extends ActorV3<ProducerActorParams> {\n /**\n * The interval time (in MS) between block production attempts.\n */\n static readonly BlockSubmissionCheckInterval = 1500 // 1.5 seconds\n\n /**\n * The threshold time (in MS) for resubmitting the same block number if the head has not changed.\n */\n static readonly HeadResubmissionThreshold = ProducerActor.BlockSubmissionCheckInterval * 30\n\n /**\n * The window (in blocks) before expiration to attempt redeclaration of producer intent.\n */\n static readonly RedeclarationWindow = 10_000\n\n protected _lastProducedBlock?: HydratedBlockWithHashMeta\n protected _lastRedeclarationIntent?: ChainStakeIntent\n protected _metricAttributes?: Attributes\n protected _producerActorBlockProductionAttempts?: Counter<Attributes>\n protected _producerActorBlockProductionChecks?: Counter<Attributes>\n protected _producerActorBlocksProduced?: Counter<Attributes>\n protected _producerActorBlocksPublished?: Counter<Attributes>\n\n private _accountBalanceViewer?: AccountBalanceViewer\n private _blockRunner?: BlockRunner\n private _blockViewer?: BlockViewer\n private _chainContractViewer?: ChainContractViewer\n private _chainId?: ChainId\n private _lastHeadChangeTime?: number\n private _lastHeadHash?: string\n private _mempoolRunner?: MempoolRunner\n private _mempoolViewer?: MempoolViewer\n private _produceBlockMutex = new Mutex()\n private _stakeTotalsViewer?: StakeTotalsViewer\n\n override get logger() {\n return assertEx(super.logger, () => 'Logger is required for ProducerActor')\n }\n\n protected get accountBalanceViewer() {\n return this._accountBalanceViewer!\n }\n\n protected get blockRunner() {\n return this._blockRunner!\n }\n\n protected get blockViewer() {\n return this._blockViewer!\n }\n\n protected get chainContractViewer() {\n return this._chainContractViewer!\n }\n\n protected get chainId() {\n return this._chainId!\n }\n\n protected get config() {\n return this.params.config\n }\n\n protected get mempoolRunner() {\n return this._mempoolRunner!\n }\n\n protected get mempoolViewer() {\n return this._mempoolViewer!\n }\n\n protected get stakeTotalsViewer() {\n return this._stakeTotalsViewer!\n }\n\n override async createHandler() {\n await super.createHandler()\n // Create the consistent meter attributes that will\n // be included with all metrics from this actor\n this._metricAttributes = { address: this.account.address.toString() }\n // Create the metrics\n this._producerActorBlockProductionChecks = this.meter?.createCounter(\n 'producer_actor_block_production_checks',\n { description: 'Number of block production checks' },\n )\n this._producerActorBlockProductionAttempts = this.meter?.createCounter(\n 'producer_actor_block_production_attempts',\n { description: 'Number of block production attempts' },\n )\n this._producerActorBlocksProduced = this.meter?.createCounter(\n 'producer_actor_blocks_produced',\n { description: 'Number of blocks produced' },\n )\n this._producerActorBlocksPublished = this.meter?.createCounter(\n 'producer_actor_blocks_published',\n { description: 'Number of blocks published' },\n )\n const final = await this.locator?.getInstance<FinalizationViewer>(FinalizationViewerMoniker)\n await final.start()\n this._accountBalanceViewer = assertEx(\n await this.locator?.getInstance<AccountBalanceViewer>(AccountBalanceViewerMoniker),\n () => 'Unable to locate AccountBalanceViewer',\n )\n this._blockRunner = assertEx(\n await this.locator?.getInstance<BlockRunner>(BlockRunnerMoniker),\n () => 'Unable to locate BlockRunner',\n )\n this._blockViewer = assertEx(\n await this.locator?.getInstance<BlockViewer>(BlockViewerMoniker),\n () => 'Unable to locate BlockViewer',\n )\n this._chainContractViewer = assertEx(\n await this.locator?.getInstance<ChainContractViewer>(ChainContractViewerMoniker),\n () => 'Unable to locate ChainContractViewer',\n )\n this._mempoolRunner = assertEx(\n await this.locator?.getInstance<MempoolRunner>(MempoolRunnerMoniker),\n () => 'Unable to locate MempoolRunner',\n )\n this._mempoolViewer = assertEx(\n await this.locator?.getInstance<MempoolViewer>(MempoolViewerMoniker),\n () => 'Unable to locate MempoolViewer',\n )\n this._stakeTotalsViewer = assertEx(\n await this.locator?.getInstance<StakeTotalsViewer>(StakeTotalsViewerMoniker),\n () => 'Unable to locate StakeTotalsViewer',\n )\n this._chainId = await this.chainContractViewer.chainId()\n }\n\n // async initLocator() {\n // const config = this.config\n // const endpoint = assertEx(config.services.apiEndpoint, () => 'API endpoint is required in config.services.apiEndpoint')\n // const transportFactory: TransportFactory = (schemas: RpcSchemaMap) => new HttpRpcTransport(endpoint, schemas)\n // const locator = await buildJsonRpcProviderLocatorV2(config, transportFactory)\n\n // const version = '1.0.0'\n // const telemetryConfig = buildTelemetryConfig(config, this.name, version, DefaultMetricsScrapePorts.producer)\n // const { traceProvider, meterProvider } = await startupSpanAsync('initTelemetry', () => initTelemetry(telemetryConfig))\n\n // locator.context.traceProvider = traceProvider\n // locator.context.meterProvider = meterProvider\n\n // locator.register(SimpleBlockRewardViewer.factory<SimpleBlockRewardViewer>(SimpleBlockRewardViewer.dependencies, {}))\n // locator.register(SimpleBlockValidationViewer.factory<SimpleBlockValidationViewer>(\n // SimpleBlockValidationViewer.dependencies,\n // { state: validateHydratedBlockState, protocol: validateHydratedBlock },\n // ))\n // locator.register(SimpleBlockRunner.factory<SimpleBlockRunner>(\n // SimpleBlockRunner.dependencies,\n // { account: this.params.account, rewardAddress: this.params.rewardAddress },\n // ))\n // return await initEvmProvidersIfAvailable(locator)\n // }\n\n override async startHandler() {\n await super.startHandler()\n // Register a timer to check if we should produce a block\n this.registerTimer('BlockProductionTimer', async () => {\n await this.produceBlock()\n }, 2000, ProducerActor.BlockSubmissionCheckInterval)\n\n if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {\n // Register a timer to check if we should redeclare the producer\n this.registerTimer('ProducerRedeclarationTimer', async () => {\n await this.redeclareIntent()\n }, TEN_MINUTES, TEN_MINUTES)\n }\n }\n\n protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number {\n return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock\n }\n\n protected async produceBlock(): Promise<void> {\n this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)\n await this.spanAsync('produceBlock', async () => {\n if (this._produceBlockMutex.isLocked()) {\n this.logger?.log('Skipping block production, previous production still in progress')\n return\n }\n\n // eslint-disable-next-line complexity\n await this._produceBlockMutex.runExclusive(async () => {\n // Get the updated head\n const head = (await this.blockViewer.currentBlock())[0]\n // Check if we've already produced the next block for this head\n const headHash = head._hash\n\n // Track head changes\n const currentTime = Date.now()\n if (this._lastHeadHash !== headHash) {\n // Log the head change\n const lastHeadHashHex = isDefined(this._lastHeadHash) ? `0x${this._lastHeadHash}` : 'undefined'\n const currentHeadHashHex = `0x${headHash}`\n this.logger?.log(`Found updated head ${lastHeadHashHex} -> ${currentHeadHashHex}`)\n\n // Update the head change info\n this._lastHeadHash = headHash\n this._lastHeadChangeTime = currentTime\n }\n\n // Check if we should resubmit due to stale head\n const timeSinceHeadChange = isDefined(this._lastHeadChangeTime) ? currentTime - this._lastHeadChangeTime : 0\n\n // If we have never produced a block or the last produced block was not built on the current head we\n // need to attempt to produce a new block\n const shouldSubmit = !this._lastProducedBlock || this._lastProducedBlock[0].previous !== headHash\n\n // If the head has not changed determine if we should resubmit again based on head staleness\n const shouldResubmit = timeSinceHeadChange > ProducerActor.HeadResubmissionThreshold\n\n // Determine if we should submit or resubmit\n const shouldSubmitBlock = shouldSubmit || shouldResubmit\n\n if (shouldSubmitBlock) {\n // If we are resubmitting due to stale head\n if (shouldResubmit) {\n // Log that we are resubmitting due to stale head\n this.logger?.log(`Resubmitting block due to stale head. Head ${toFormattedBlockReference(head)} unchanged for ${timeSinceHeadChange}ms`)\n // Reset timer since we're resubmitting\n this._lastHeadChangeTime = currentTime\n }\n this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)\n // Produce the next block\n const nextBlock = await this.blockRunner.produceNextBlock(head, shouldResubmit)\n // If it was produced\n if (nextBlock) {\n const displayBlockNumber = toFormattedBlockReference(nextBlock[0])\n this.logger?.log('Produced block:', displayBlockNumber)\n this._producerActorBlocksProduced?.add(1, this._metricAttributes)\n // Insert the block into the chain\n await this.mempoolRunner.submitBlocks([nextBlock])\n this.logger?.log('Published block:', displayBlockNumber)\n this._producerActorBlocksPublished?.add(1, this._metricAttributes)\n // Record that we have produced a block so we do not produce it again\n this._lastProducedBlock = nextBlock\n } else {\n this.logger?.log('No block produced for submission.')\n }\n } else {\n // Log that we are not submitting a block at this time\n let nonSubmissionMessage = 'No block submission required at this time'\n // If we have already produced a block\n if (isDefined(this._lastProducedBlock)) {\n // include that info in the log\n nonSubmissionMessage = nonSubmissionMessage + `, already produced: ${toFormattedBlockReference(this._lastProducedBlock[0])}`\n }\n this.logger?.log(`${nonSubmissionMessage}.`)\n }\n })\n }, { ...this.context, timeBudgetLimit: 1000 })\n }\n\n protected async redeclareIntent(): Promise<void> {\n await this.spanAsync('redeclareIntent', async () => {\n // Decide if we should redeclare intent\n if (this.config.disableIntentRedeclaration) return\n\n // Get the current block\n const head = (await this.blockViewer.currentBlock())[0]\n if (isUndefined(head)) return\n const currentBlock = head.block\n\n // // Calculate the time until the producer's declaration expires\n const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)\n\n // Allow the producer time to redeclare itself via block production\n // (for free) before submitting a redeclaration intent transaction.\n if (blocksUntilExpiration > ProducerActor.RedeclarationWindow * 0.1) {\n // Clear any previous redeclaration intent\n this._lastRedeclarationIntent = undefined\n // No need to redeclare yet\n return\n }\n\n // If we already have a valid redeclaration intent, do not create another\n // unless it has expired.\n if (this._lastRedeclarationIntent) {\n // Check if the last redeclaration intent is still valid\n if (this._lastRedeclarationIntent.exp > currentBlock) return\n // If it has expired, clear the last redeclaration intent\n this._lastRedeclarationIntent = undefined\n }\n\n // Check if we have a valid balance before declaring intent\n if (!await this.validateCurrentBalance()) {\n this.logger?.error(\n `Add balance to address ${this.account.address} for the producer to declare it's intent.`,\n )\n return\n }\n\n // Check if we have a valid stake before declaring intent\n if (!(await this.validateCurrentStake())) {\n this.logger?.error(\n `Add stake to contract address ${this.chainId}`\n + ' for the producer to declare it\\'s intent.',\n )\n return\n }\n\n // Create a redeclaration intent\n this.logger?.log('Creating redeclaration intent for producer:', this.account.address)\n const redeclarationIntent = createDeclarationIntent(\n this.account.address,\n 'producer',\n currentBlock,\n currentBlock + SimpleBlockRunner.RedeclarationDuration,\n )\n\n // Submit the redeclaration intent\n await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)\n\n // On successful submission, save the redeclaration intent\n this._lastRedeclarationIntent = redeclarationIntent\n }, { ...this.context, timeBudgetLimit: 1000 })\n }\n\n protected async submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void> {\n this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)\n // Create a transaction to submit the redeclaration intent\n const tx = await buildTransaction(\n this.chainId,\n [redeclarationIntent],\n [],\n this.account,\n currentBlock,\n asXL1BlockNumber(currentBlock + 1000, true),\n )\n\n // Submit the redeclaration intent\n await this.mempoolRunner.submitTransactions([tx])\n\n this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)\n }\n\n protected async validateCurrentBalance(): Promise<boolean> {\n // Check if we have a valid balance before declaring intent\n const head = (await this.blockViewer.currentBlock())?.[0]?._hash\n if (isDefined(head)) {\n const balances = await this.accountBalanceViewer.accountBalances([this.account.address], { head })\n const currentBalance = balances[this.account.address] ?? 0n\n if (currentBalance <= 0n) {\n this.logger?.error(`Producer ${this.account.address} has no balance.`)\n return false\n }\n return true\n }\n return true\n }\n\n protected async validateCurrentStake(): Promise<boolean> {\n // Use StakeIntentService to get the required minimum stake\n const requiredMinimumStake = 1n // this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')\n // Check if we have a valid stake before declaring intent\n const currentStake = await this.stakeTotalsViewer.activeByStaked(this.account.address)\n if (currentStake < requiredMinimumStake) {\n this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)\n return false\n }\n return true\n }\n}\n","import type { CreatableName } from '@xylabs/sdk-js'\nimport { exists } from '@xylabs/sdk-js'\nimport {\n initActorAccount,\n type OrchestratorInstance,\n type ProducerConfig,\n} from '@xyo-network/chain-orchestration'\nimport type { ProviderFactoryLocatorInstance } from '@xyo-network/xl1-sdk'\n\nimport type { ProducerActorParams } from './ProducerActor.ts'\nimport { ProducerActor } from './ProducerActor.ts'\n\nexport const runProducer = async (\n config: ProducerConfig,\n orchestrator: OrchestratorInstance,\n locator: ProviderFactoryLocatorInstance,\n) => {\n const account = await initActorAccount({ ...locator.context, config })\n // Create actors\n const producer = await ProducerActor.create({\n config, locator, name: 'xl1-producer' as CreatableName, account,\n } satisfies ProducerActorParams)\n const actors = [producer].filter(exists)\n\n for (const actor of actors) {\n // Register the actor with the orchestrator\n await orchestrator.registerActor(actor)\n }\n // Start the orchestrator => automatically activates the actor\n await orchestrator.start()\n}\n"],"mappings":"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","names":["assertEx","creatable","isDefined","isUndefined","toHex","SimpleBlockRunner","AccountBalanceViewerMoniker","ActorV3","asXL1BlockNumber","BlockRunnerMoniker","BlockViewerMoniker","buildTransaction","ChainContractViewerMoniker","createDeclarationIntent","FinalizationViewerMoniker","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","Mutex","SHOULD_REGISTER_REDECLARATION_INTENT_TIMER","TEN_MINUTES","toFormattedBlockReference","blockBoundWitness","block","toHex","_hash","prefix","ProducerActor","ActorV3","BlockSubmissionCheckInterval","HeadResubmissionThreshold","RedeclarationWindow","_lastProducedBlock","_lastRedeclarationIntent","_metricAttributes","_producerActorBlockProductionAttempts","_producerActorBlockProductionChecks","_producerActorBlocksProduced","_producerActorBlocksPublished","_accountBalanceViewer","_blockRunner","_blockViewer","_chainContractViewer","_chainId","_lastHeadChangeTime","_lastHeadHash","_mempoolRunner","_mempoolViewer","_produceBlockMutex","Mutex","_stakeTotalsViewer","logger","assertEx","accountBalanceViewer","blockRunner","blockViewer","chainContractViewer","chainId","config","params","mempoolRunner","mempoolViewer","stakeTotalsViewer","createHandler","address","account","toString","meter","createCounter","description","final","locator","getInstance","FinalizationViewerMoniker","start","AccountBalanceViewerMoniker","BlockRunnerMoniker","BlockViewerMoniker","ChainContractViewerMoniker","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","startHandler","registerTimer","produceBlock","redeclareIntent","calculateBlocksUntilProducerDeclarationExpiration","currentBlock","exp","add","spanAsync","isLocked","log","runExclusive","head","headHash","currentTime","Date","now","lastHeadHashHex","isDefined","currentHeadHashHex","timeSinceHeadChange","shouldSubmit","previous","shouldResubmit","shouldSubmitBlock","nextBlock","produceNextBlock","displayBlockNumber","submitBlocks","nonSubmissionMessage","context","timeBudgetLimit","disableIntentRedeclaration","isUndefined","blocksUntilExpiration","undefined","validateCurrentBalance","error","validateCurrentStake","redeclarationIntent","createDeclarationIntent","SimpleBlockRunner","RedeclarationDuration","submitRedeclarationIntent","tx","buildTransaction","asXL1BlockNumber","submitTransactions","balances","accountBalances","currentBalance","requiredMinimumStake","currentStake","activeByStaked","exists","initActorAccount","runProducer","config","orchestrator","locator","account","initActorAccount","context","producer","ProducerActor","create","name","actors","filter","exists","actor","registerActor","start"]}
1
+ {"version":3,"sources":["../../src/ProducerActor.ts","../../src/runProducer.ts"],"sourcesContent":["import type { Attributes, Counter } from '@opentelemetry/api'\nimport {\n assertEx, creatable, isDefined, isUndefined,\n toHex,\n} from '@xylabs/sdk-js'\nimport { ProducerConfig } from '@xyo-network/chain-orchestration'\nimport { SimpleBlockRunner } from '@xyo-network/chain-services'\nimport { WithHashMeta } from '@xyo-network/sdk-js'\nimport type {\n ActorParamsV3,\n BlockBoundWitness,\n ChainContractViewer, ChainId, ChainStakeIntent, HydratedBlockWithHashMeta, XL1BlockNumber,\n} from '@xyo-network/xl1-sdk'\nimport {\n AccountBalanceViewer, AccountBalanceViewerMoniker, ActorV3, asXL1BlockNumber, BlockRunner, BlockRunnerMoniker, BlockViewer, BlockViewerMoniker,\n buildTransaction, ChainContractViewerMoniker, createDeclarationIntent, FinalizationViewer, FinalizationViewerMoniker, MempoolRunner, MempoolRunnerMoniker,\n MempoolViewer, MempoolViewerMoniker, StakeTotalsViewer, StakeTotalsViewerMoniker,\n} from '@xyo-network/xl1-sdk'\nimport { Mutex } from 'async-mutex'\n\nexport type ProducerActorParams = ActorParamsV3<{\n config: ProducerConfig\n}>\n\nconst SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true\nconst TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds\n\n/**\n * Formats a hydrated block with hash meta into a string representation of its hash and block number.\n * @param blockBoundWitness The hydrated block with hash meta to format\n * @returns The formatted block reference string\n */\nconst toFormattedBlockReference = (blockBoundWitness: WithHashMeta<BlockBoundWitness>): string => {\n return `${blockBoundWitness.block} [${toHex(blockBoundWitness._hash, { prefix: true })}]`\n}\n\n@creatable()\nexport class ProducerActor extends ActorV3<ProducerActorParams> {\n /**\n * The interval time (in MS) between block production attempts.\n */\n static readonly BlockSubmissionCheckInterval = 1500 // 1.5 seconds\n\n /**\n * The threshold time (in MS) for resubmitting the same block number if the head has not changed.\n */\n static readonly HeadResubmissionThreshold = ProducerActor.BlockSubmissionCheckInterval * 30\n\n /**\n * The window (in blocks) before expiration to attempt redeclaration of producer intent.\n */\n static readonly RedeclarationWindow = 1000\n\n protected _lastProducedBlock?: HydratedBlockWithHashMeta\n protected _lastRedeclarationIntent?: ChainStakeIntent\n protected _metricAttributes?: Attributes\n protected _producerActorBlockProductionAttempts?: Counter<Attributes>\n protected _producerActorBlockProductionChecks?: Counter<Attributes>\n protected _producerActorBlocksProduced?: Counter<Attributes>\n protected _producerActorBlocksPublished?: Counter<Attributes>\n\n private _accountBalanceViewer?: AccountBalanceViewer\n private _blockRunner?: BlockRunner\n private _blockViewer?: BlockViewer\n private _chainContractViewer?: ChainContractViewer\n private _chainId?: ChainId\n private _lastHeadChangeTime?: number\n private _lastHeadHash?: string\n private _mempoolRunner?: MempoolRunner\n private _mempoolViewer?: MempoolViewer\n private _produceBlockMutex = new Mutex()\n private _stakeTotalsViewer?: StakeTotalsViewer\n\n override get logger() {\n return assertEx(super.logger, () => 'Logger is required for ProducerActor')\n }\n\n protected get accountBalanceViewer() {\n return this._accountBalanceViewer!\n }\n\n protected get blockRunner() {\n return this._blockRunner!\n }\n\n protected get blockViewer() {\n return this._blockViewer!\n }\n\n protected get chainContractViewer() {\n return this._chainContractViewer!\n }\n\n protected get chainId() {\n return this._chainId!\n }\n\n protected get config() {\n return this.params.config\n }\n\n protected get mempoolRunner() {\n return this._mempoolRunner!\n }\n\n protected get mempoolViewer() {\n return this._mempoolViewer!\n }\n\n protected get stakeTotalsViewer() {\n return this._stakeTotalsViewer!\n }\n\n override async createHandler() {\n await super.createHandler()\n // Create the consistent meter attributes that will\n // be included with all metrics from this actor\n this._metricAttributes = { address: this.account.address.toString() }\n // Create the metrics\n this._producerActorBlockProductionChecks = this.meter?.createCounter(\n 'producer_actor_block_production_checks',\n { description: 'Number of block production checks' },\n )\n this._producerActorBlockProductionAttempts = this.meter?.createCounter(\n 'producer_actor_block_production_attempts',\n { description: 'Number of block production attempts' },\n )\n this._producerActorBlocksProduced = this.meter?.createCounter(\n 'producer_actor_blocks_produced',\n { description: 'Number of blocks produced' },\n )\n this._producerActorBlocksPublished = this.meter?.createCounter(\n 'producer_actor_blocks_published',\n { description: 'Number of blocks published' },\n )\n const final = await this.locator?.getInstance<FinalizationViewer>(FinalizationViewerMoniker)\n await final.start()\n this._accountBalanceViewer = assertEx(\n await this.locator?.getInstance<AccountBalanceViewer>(AccountBalanceViewerMoniker),\n () => 'Unable to locate AccountBalanceViewer',\n )\n this._blockRunner = assertEx(\n await this.locator?.getInstance<BlockRunner>(BlockRunnerMoniker),\n () => 'Unable to locate BlockRunner',\n )\n this._blockViewer = assertEx(\n await this.locator?.getInstance<BlockViewer>(BlockViewerMoniker),\n () => 'Unable to locate BlockViewer',\n )\n this._chainContractViewer = assertEx(\n await this.locator?.getInstance<ChainContractViewer>(ChainContractViewerMoniker),\n () => 'Unable to locate ChainContractViewer',\n )\n this._mempoolRunner = assertEx(\n await this.locator?.getInstance<MempoolRunner>(MempoolRunnerMoniker),\n () => 'Unable to locate MempoolRunner',\n )\n this._mempoolViewer = assertEx(\n await this.locator?.getInstance<MempoolViewer>(MempoolViewerMoniker),\n () => 'Unable to locate MempoolViewer',\n )\n this._stakeTotalsViewer = assertEx(\n await this.locator?.getInstance<StakeTotalsViewer>(StakeTotalsViewerMoniker),\n () => 'Unable to locate StakeTotalsViewer',\n )\n this._chainId = await this.chainContractViewer.chainId()\n }\n\n // async initLocator() {\n // const config = this.config\n // const endpoint = assertEx(config.services.apiEndpoint, () => 'API endpoint is required in config.services.apiEndpoint')\n // const transportFactory: TransportFactory = (schemas: RpcSchemaMap) => new HttpRpcTransport(endpoint, schemas)\n // const locator = await buildJsonRpcProviderLocatorV2(config, transportFactory)\n\n // const version = '1.0.0'\n // const telemetryConfig = buildTelemetryConfig(config, this.name, version, DefaultMetricsScrapePorts.producer)\n // const { traceProvider, meterProvider } = await startupSpanAsync('initTelemetry', () => initTelemetry(telemetryConfig))\n\n // locator.context.traceProvider = traceProvider\n // locator.context.meterProvider = meterProvider\n\n // locator.register(SimpleBlockRewardViewer.factory<SimpleBlockRewardViewer>(SimpleBlockRewardViewer.dependencies, {}))\n // locator.register(SimpleBlockValidationViewer.factory<SimpleBlockValidationViewer>(\n // SimpleBlockValidationViewer.dependencies,\n // { state: validateHydratedBlockState, protocol: validateHydratedBlock },\n // ))\n // locator.register(SimpleBlockRunner.factory<SimpleBlockRunner>(\n // SimpleBlockRunner.dependencies,\n // { account: this.params.account, rewardAddress: this.params.rewardAddress },\n // ))\n // return await initEvmProvidersIfAvailable(locator)\n // }\n\n override async startHandler() {\n await super.startHandler()\n // Register a timer to check if we should produce a block\n this.registerTimer('BlockProductionTimer', async () => {\n await this.produceBlock()\n }, 2000, ProducerActor.BlockSubmissionCheckInterval)\n\n if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {\n // Register a timer to check if we should redeclare the producer\n this.registerTimer('ProducerRedeclarationTimer', async () => {\n await this.redeclareIntent()\n }, TEN_MINUTES, TEN_MINUTES)\n }\n }\n\n protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number {\n return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock\n }\n\n protected async produceBlock(): Promise<void> {\n this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)\n await this.spanAsync('produceBlock', async () => {\n if (this._produceBlockMutex.isLocked()) {\n this.logger?.log('Skipping block production, previous production still in progress')\n return\n }\n\n // eslint-disable-next-line complexity\n await this._produceBlockMutex.runExclusive(async () => {\n // Get the updated head\n const head = (await this.blockViewer.currentBlock())[0]\n // Check if we've already produced the next block for this head\n const headHash = head._hash\n\n // Track head changes\n const currentTime = Date.now()\n if (this._lastHeadHash !== headHash) {\n // Log the head change\n const lastHeadHashHex = isDefined(this._lastHeadHash) ? `0x${this._lastHeadHash}` : 'undefined'\n const currentHeadHashHex = `0x${headHash}`\n this.logger?.log(`Found updated head ${lastHeadHashHex} -> ${currentHeadHashHex}`)\n\n // Update the head change info\n this._lastHeadHash = headHash\n this._lastHeadChangeTime = currentTime\n }\n\n // Check if we should resubmit due to stale head\n const timeSinceHeadChange = isDefined(this._lastHeadChangeTime) ? currentTime - this._lastHeadChangeTime : 0\n\n // If we have never produced a block or the last produced block was not built on the current head we\n // need to attempt to produce a new block\n const shouldSubmit = !this._lastProducedBlock || this._lastProducedBlock[0].previous !== headHash\n\n // If the head has not changed determine if we should resubmit again based on head staleness\n const shouldResubmit = timeSinceHeadChange > ProducerActor.HeadResubmissionThreshold\n\n // Determine if we should submit or resubmit\n const shouldSubmitBlock = shouldSubmit || shouldResubmit\n\n if (shouldSubmitBlock) {\n // If we are resubmitting due to stale head\n if (shouldResubmit) {\n // Log that we are resubmitting due to stale head\n this.logger?.log(`Resubmitting block due to stale head. Head ${toFormattedBlockReference(head)} unchanged for ${timeSinceHeadChange}ms`)\n // Reset timer since we're resubmitting\n this._lastHeadChangeTime = currentTime\n }\n this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)\n // Produce the next block\n const nextBlock = await this.blockRunner.produceNextBlock(head, shouldResubmit)\n // If it was produced\n if (nextBlock) {\n const displayBlockNumber = toFormattedBlockReference(nextBlock[0])\n this.logger?.log('Produced block:', displayBlockNumber)\n this._producerActorBlocksProduced?.add(1, this._metricAttributes)\n // Insert the block into the chain\n await this.mempoolRunner.submitBlocks([nextBlock])\n this.logger?.log('Published block:', displayBlockNumber)\n this._producerActorBlocksPublished?.add(1, this._metricAttributes)\n // Record that we have produced a block so we do not produce it again\n this._lastProducedBlock = nextBlock\n } else {\n this.logger?.log('No block produced for submission.')\n }\n } else {\n // Log that we are not submitting a block at this time\n let nonSubmissionMessage = 'No block submission required at this time'\n // If we have already produced a block\n if (isDefined(this._lastProducedBlock)) {\n // include that info in the log\n nonSubmissionMessage = nonSubmissionMessage + `, already produced: ${toFormattedBlockReference(this._lastProducedBlock[0])}`\n }\n this.logger?.log(`${nonSubmissionMessage}.`)\n }\n })\n }, { ...this.context, timeBudgetLimit: 1000 })\n }\n\n protected async redeclareIntent(): Promise<void> {\n await this.spanAsync('redeclareIntent', async () => {\n // Decide if we should redeclare intent\n if (this.config.disableIntentRedeclaration) return\n\n // Get the current block\n const head = (await this.blockViewer.currentBlock())[0]\n if (isUndefined(head)) return\n const currentBlock = head.block\n\n // // Calculate the time until the producer's declaration expires\n const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)\n\n // Allow the producer time to redeclare itself via block production\n // (for free) before submitting a redeclaration intent transaction.\n if (blocksUntilExpiration > ProducerActor.RedeclarationWindow * 0.1) {\n // Clear any previous redeclaration intent\n this._lastRedeclarationIntent = undefined\n // No need to redeclare yet\n return\n }\n\n // If we already have a valid redeclaration intent, do not create another\n // unless it has expired.\n if (this._lastRedeclarationIntent) {\n // Check if the last redeclaration intent is still valid\n if (this._lastRedeclarationIntent.exp > currentBlock) return\n // If it has expired, clear the last redeclaration intent\n this._lastRedeclarationIntent = undefined\n }\n\n // Check if we have a valid balance before declaring intent\n if (!await this.validateCurrentBalance()) {\n this.logger?.error(\n `Add balance to address ${this.account.address} for the producer to declare it's intent.`,\n )\n return\n }\n\n // Check if we have a valid stake before declaring intent\n if (!(await this.validateCurrentStake())) {\n this.logger?.error(\n `Add stake to contract address ${this.chainId}`\n + ' for the producer to declare it\\'s intent.',\n )\n return\n }\n\n // Create a redeclaration intent\n this.logger?.log('Creating redeclaration intent for producer:', this.account.address)\n const redeclarationIntent = createDeclarationIntent(\n this.account.address,\n 'producer',\n currentBlock,\n currentBlock + SimpleBlockRunner.RedeclarationDuration,\n )\n\n // Submit the redeclaration intent\n await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)\n\n // On successful submission, save the redeclaration intent\n this._lastRedeclarationIntent = redeclarationIntent\n }, { ...this.context, timeBudgetLimit: 1000 })\n }\n\n protected async submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void> {\n this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)\n // Create a transaction to submit the redeclaration intent\n const tx = await buildTransaction(\n this.chainId,\n [redeclarationIntent],\n [],\n this.account,\n currentBlock,\n asXL1BlockNumber(currentBlock + 1000, true),\n )\n\n // Submit the redeclaration intent\n await this.mempoolRunner.submitTransactions([tx])\n\n this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)\n }\n\n protected async validateCurrentBalance(): Promise<boolean> {\n // Check if we have a valid balance before declaring intent\n const head = (await this.blockViewer.currentBlock())?.[0]?._hash\n if (isDefined(head)) {\n const balances = await this.accountBalanceViewer.accountBalances([this.account.address], { head })\n const currentBalance = balances[this.account.address] ?? 0n\n if (currentBalance <= 0n) {\n this.logger?.error(`Producer ${this.account.address} has no balance.`)\n return false\n }\n return true\n }\n return true\n }\n\n protected async validateCurrentStake(): Promise<boolean> {\n // Use StakeIntentService to get the required minimum stake\n const requiredMinimumStake = 1n // this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')\n // Check if we have a valid stake before declaring intent\n const currentStake = await this.stakeTotalsViewer.activeByStaked(this.account.address)\n if (currentStake < requiredMinimumStake) {\n this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)\n return false\n }\n return true\n }\n}\n","import type { CreatableName } from '@xylabs/sdk-js'\nimport { exists } from '@xylabs/sdk-js'\nimport {\n initActorAccount,\n type OrchestratorInstance,\n type ProducerConfig,\n} from '@xyo-network/chain-orchestration'\nimport type { ProviderFactoryLocatorInstance } from '@xyo-network/xl1-sdk'\n\nimport type { ProducerActorParams } from './ProducerActor.ts'\nimport { ProducerActor } from './ProducerActor.ts'\n\nexport const runProducer = async (\n config: ProducerConfig,\n orchestrator: OrchestratorInstance,\n locator: ProviderFactoryLocatorInstance,\n) => {\n const account = await initActorAccount({ ...locator.context, config })\n // Create actors\n const producer = await ProducerActor.create({\n config, locator, name: 'xl1-producer' as CreatableName, account,\n } satisfies ProducerActorParams)\n const actors = [producer].filter(exists)\n\n for (const actor of actors) {\n // Register the actor with the orchestrator\n await orchestrator.registerActor(actor)\n }\n // Start the orchestrator => automatically activates the actor\n await orchestrator.start()\n}\n"],"mappings":"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","names":["assertEx","creatable","isDefined","isUndefined","toHex","SimpleBlockRunner","AccountBalanceViewerMoniker","ActorV3","asXL1BlockNumber","BlockRunnerMoniker","BlockViewerMoniker","buildTransaction","ChainContractViewerMoniker","createDeclarationIntent","FinalizationViewerMoniker","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","Mutex","SHOULD_REGISTER_REDECLARATION_INTENT_TIMER","TEN_MINUTES","toFormattedBlockReference","blockBoundWitness","block","toHex","_hash","prefix","ProducerActor","ActorV3","BlockSubmissionCheckInterval","HeadResubmissionThreshold","RedeclarationWindow","_lastProducedBlock","_lastRedeclarationIntent","_metricAttributes","_producerActorBlockProductionAttempts","_producerActorBlockProductionChecks","_producerActorBlocksProduced","_producerActorBlocksPublished","_accountBalanceViewer","_blockRunner","_blockViewer","_chainContractViewer","_chainId","_lastHeadChangeTime","_lastHeadHash","_mempoolRunner","_mempoolViewer","_produceBlockMutex","Mutex","_stakeTotalsViewer","logger","assertEx","accountBalanceViewer","blockRunner","blockViewer","chainContractViewer","chainId","config","params","mempoolRunner","mempoolViewer","stakeTotalsViewer","createHandler","address","account","toString","meter","createCounter","description","final","locator","getInstance","FinalizationViewerMoniker","start","AccountBalanceViewerMoniker","BlockRunnerMoniker","BlockViewerMoniker","ChainContractViewerMoniker","MempoolRunnerMoniker","MempoolViewerMoniker","StakeTotalsViewerMoniker","startHandler","registerTimer","produceBlock","redeclareIntent","calculateBlocksUntilProducerDeclarationExpiration","currentBlock","exp","add","spanAsync","isLocked","log","runExclusive","head","headHash","currentTime","Date","now","lastHeadHashHex","isDefined","currentHeadHashHex","timeSinceHeadChange","shouldSubmit","previous","shouldResubmit","shouldSubmitBlock","nextBlock","produceNextBlock","displayBlockNumber","submitBlocks","nonSubmissionMessage","context","timeBudgetLimit","disableIntentRedeclaration","isUndefined","blocksUntilExpiration","undefined","validateCurrentBalance","error","validateCurrentStake","redeclarationIntent","createDeclarationIntent","SimpleBlockRunner","RedeclarationDuration","submitRedeclarationIntent","tx","buildTransaction","asXL1BlockNumber","submitTransactions","balances","accountBalances","currentBalance","requiredMinimumStake","currentStake","activeByStaked","exists","initActorAccount","runProducer","config","orchestrator","locator","account","initActorAccount","context","producer","ProducerActor","create","name","actors","filter","exists","actor","registerActor","start"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/chain-producer",
3
- "version": "1.20.14",
3
+ "version": "1.20.16",
4
4
  "description": "XYO Layer One Producer",
5
5
  "homepage": "https://xylabs.com",
6
6
  "bugs": {
@@ -22,74 +22,169 @@
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./dist/node/index.d.ts",
25
- "source": "./src/index.ts",
26
25
  "default": "./dist/node/index.mjs"
27
26
  },
28
27
  "./package.json": "./package.json",
29
28
  "./README.md": "./README.md"
30
29
  },
31
30
  "module": "./dist/node/index.mjs",
32
- "source": "./src/index.ts",
33
31
  "types": "./dist/node/index.d.ts",
34
32
  "files": [
35
33
  "dist",
36
- "src",
37
34
  "!**/*.bench.*",
38
35
  "!**/*.spec.*",
39
- "!**/*.test.*"
36
+ "!**/*.test.*",
37
+ "README.md"
40
38
  ],
41
- "scripts": {
42
- "build-tests": "tsc --noEmit --lib dom,esnext",
43
- "deploy": "echo Deploy not allowed!",
44
- "deploy3": "echo Deploy3 not allowed!",
45
- "start": "nodemon",
46
- "start-esm": "node ./dist/node/index.mjs",
47
- "test": "vitest run",
48
- "test:ci": "vitest run",
49
- "types": "tsc --noEmit -p tsconfig.test.json"
50
- },
51
39
  "dependencies": {
52
40
  "@opentelemetry/api": "^1.9.1",
53
- "@xyo-network/chain-orchestration": "~1.20.14",
54
- "@xyo-network/chain-services": "~1.20.14",
55
- "async-mutex": "~0.5.0"
41
+ "@xyo-network/xl1-sdk": "^1.26.19",
42
+ "async-mutex": "~0.5.0",
43
+ "@xyo-network/chain-orchestration": "~1.20.16"
56
44
  },
57
45
  "devDependencies": {
58
- "@opentelemetry/api": "^1.9.1",
59
- "@types/node": "~25.5.0",
60
- "@xylabs/mongo": "~5.0.90",
61
- "@xylabs/sdk-js": "~5.0.90",
62
- "@xylabs/ts-scripts-common": "~7.5.11",
63
- "@xylabs/ts-scripts-yarn3": "~7.5.11",
64
- "@xylabs/tsconfig": "~7.5.11",
65
- "@xyo-network/chain-services": "~1.20.14",
66
- "@xyo-network/sdk-js": "~5.3.20",
67
- "@xyo-network/xl1-sdk": "~1.26.11",
68
- "axios": "^1.13.6",
69
- "dotenv": "~17.3.1",
46
+ "@types/node": "~25.5.2",
47
+ "@xylabs/express": "^5.0.95",
48
+ "@xylabs/mongo": "~5.0.95",
49
+ "@xylabs/sdk-js": "~5.0.95",
50
+ "@xylabs/ts-scripts-common": "~7.9.3",
51
+ "@xylabs/ts-scripts-pnpm": "~7.9.3",
52
+ "@xylabs/tsconfig": "~7.9.3",
53
+ "@xyo-network/account": "~5.3.30",
54
+ "@xyo-network/account-model": "~5.3",
55
+ "@xyo-network/api-models": "~5.3.30",
56
+ "@xyo-network/archivist-abstract": "~5.3",
57
+ "@xyo-network/archivist-generic": "~5.3.30",
58
+ "@xyo-network/archivist-memory": "~5.3.30",
59
+ "@xyo-network/archivist-model": "~5.3",
60
+ "@xyo-network/archivist-view": "~5.3.30",
61
+ "@xyo-network/archivist-wrapper": "~5.3",
62
+ "@xyo-network/boundwitness-builder": "~5.3.30",
63
+ "@xyo-network/boundwitness-validator": "~5.3",
64
+ "@xyo-network/bridge-abstract": "~5.3.30",
65
+ "@xyo-network/bridge-model": "~5.3.30",
66
+ "@xyo-network/config-payload-plugin": "~5.3.30",
67
+ "@xyo-network/data": "~5.3.30",
68
+ "@xyo-network/diviner-abstract": "~5.3",
69
+ "@xyo-network/diviner-boundwitness-memory": "~5.3.30",
70
+ "@xyo-network/diviner-identity": "~5.3.30",
71
+ "@xyo-network/diviner-model": "~5.3.30",
72
+ "@xyo-network/diviner-payload-generic": "~5.3.30",
73
+ "@xyo-network/diviner-payload-model": "~5.3.30",
74
+ "@xyo-network/diviner-wrapper": "~5.3",
75
+ "@xyo-network/dns": "~5.3.30",
76
+ "@xyo-network/domain-payload-plugin": "~5.3.30",
77
+ "@xyo-network/elliptic": "~5.3.30",
78
+ "@xyo-network/hash": "~5.3",
79
+ "@xyo-network/huri": "~5.3.30",
80
+ "@xyo-network/manifest-model": "~5.3",
81
+ "@xyo-network/module-abstract": "~5.3",
82
+ "@xyo-network/module-model": "~5.3",
83
+ "@xyo-network/module-model-mongodb": "~5.3",
84
+ "@xyo-network/module-resolver": "~5.3.30",
85
+ "@xyo-network/module-wrapper": "~5.3",
86
+ "@xyo-network/network": "~5.3.30",
87
+ "@xyo-network/node-abstract": "~5.3.30",
88
+ "@xyo-network/node-memory": "~5.3.30",
89
+ "@xyo-network/node-model": "~5.3.30",
90
+ "@xyo-network/node-view": "~5.3.30",
91
+ "@xyo-network/node-wrapper": "~5.3",
92
+ "@xyo-network/payload-builder": "~5.3",
93
+ "@xyo-network/payload-model": "~5.3.30",
94
+ "@xyo-network/payload-validator": "~5.3",
95
+ "@xyo-network/previous-hash-store-model": "~5.3.30",
96
+ "@xyo-network/sdk-js": "~5.3.30",
97
+ "@xyo-network/sentinel-abstract": "~5.3.30",
98
+ "@xyo-network/sentinel-memory": "~5.3.30",
99
+ "@xyo-network/sentinel-model": "~5.3.30",
100
+ "@xyo-network/wallet-model": "~5.3.30",
101
+ "@xyo-network/wasm": "~5.3.30",
102
+ "@xyo-network/witness-adhoc": "~5.3.30",
103
+ "@xyo-network/witness-model": "~5.3.30",
104
+ "ajv": "^8",
105
+ "axios": "^1.14.0",
106
+ "dotenv": "~17.4.1",
107
+ "esbuild": ">=0.18",
70
108
  "ethers": "^6.16.0",
109
+ "lmdb": "^3",
71
110
  "mongodb": "~7.1.1",
72
111
  "nodemon": "~3.1.14",
112
+ "pako": "~2.1.0",
73
113
  "tslib": "~2.8.1",
74
114
  "typescript": "~5.9.3",
75
- "vitest": "^4.1.2",
76
- "zod": "^4.3.6"
115
+ "vite": "^8.0.7",
116
+ "vitest": "^4.1.3",
117
+ "zod": "^4.3.6",
118
+ "@xyo-network/chain-services": "~1.20.16",
119
+ "@xyo-network/chain-telemetry": "~1.20.16"
77
120
  },
78
121
  "peerDependencies": {
79
- "@xylabs/mongo": "^5",
80
- "@xylabs/sdk-js": "^5",
81
- "@xyo-network/sdk-js": "^5",
82
- "@xyo-network/xl1-sdk": "^1",
83
- "axios": "^1",
84
- "ethers": "^6",
85
- "mongodb": "^7",
86
- "zod": "^4"
122
+ "@xylabs/express": "^5.0.95",
123
+ "@xylabs/sdk-js": "~5.0.93",
124
+ "@xyo-network/account": "~5.3.30",
125
+ "@xyo-network/account-model": "~5.3",
126
+ "@xyo-network/api-models": "~5.3.30",
127
+ "@xyo-network/archivist-abstract": "~5.3",
128
+ "@xyo-network/archivist-generic": "~5.3.30",
129
+ "@xyo-network/archivist-memory": "~5.3.30",
130
+ "@xyo-network/archivist-model": "~5.3",
131
+ "@xyo-network/archivist-view": "~5.3.30",
132
+ "@xyo-network/boundwitness-builder": "~5.3.30",
133
+ "@xyo-network/boundwitness-validator": "~5.3",
134
+ "@xyo-network/bridge-abstract": "~5.3.30",
135
+ "@xyo-network/bridge-model": "~5.3.30",
136
+ "@xyo-network/config-payload-plugin": "~5.3.30",
137
+ "@xyo-network/data": "~5.3.30",
138
+ "@xyo-network/diviner-boundwitness-memory": "~5.3.30",
139
+ "@xyo-network/diviner-identity": "~5.3.30",
140
+ "@xyo-network/diviner-model": "~5.3.30",
141
+ "@xyo-network/diviner-payload-generic": "~5.3.30",
142
+ "@xyo-network/diviner-payload-model": "~5.3.30",
143
+ "@xyo-network/dns": "~5.3.30",
144
+ "@xyo-network/domain-payload-plugin": "~5.3.30",
145
+ "@xyo-network/elliptic": "~5.3.30",
146
+ "@xyo-network/hash": "~5.3",
147
+ "@xyo-network/huri": "~5.3.30",
148
+ "@xyo-network/manifest-model": "~5.3",
149
+ "@xyo-network/module-abstract": "~5.3",
150
+ "@xyo-network/module-model": "~5.3",
151
+ "@xyo-network/module-model-mongodb": "~5.3",
152
+ "@xyo-network/module-resolver": "~5.3.30",
153
+ "@xyo-network/network": "~5.3.30",
154
+ "@xyo-network/node-abstract": "~5.3.30",
155
+ "@xyo-network/node-memory": "~5.3.30",
156
+ "@xyo-network/node-model": "~5.3.30",
157
+ "@xyo-network/node-view": "~5.3.30",
158
+ "@xyo-network/payload-builder": "~5.3",
159
+ "@xyo-network/payload-model": "~5.3.30",
160
+ "@xyo-network/payload-validator": "~5.3",
161
+ "@xyo-network/previous-hash-store-model": "~5.3.30",
162
+ "@xyo-network/sentinel-abstract": "~5.3.30",
163
+ "@xyo-network/sentinel-memory": "~5.3.30",
164
+ "@xyo-network/sentinel-model": "~5.3.30",
165
+ "@xyo-network/wallet-model": "~5.3.30",
166
+ "@xyo-network/wasm": "~5.3.30",
167
+ "@xyo-network/witness-adhoc": "~5.3.30",
168
+ "@xyo-network/witness-model": "~5.3.30",
169
+ "pako": "~2.1.0",
170
+ "@xyo-network/chain-services": "~1.20.16",
171
+ "@xyo-network/chain-telemetry": "~1.20.16"
87
172
  },
88
173
  "engines": {
89
- "node": ">=22.3 <23"
174
+ "node": ">=24"
90
175
  },
91
176
  "engineStrict": true,
92
177
  "publishConfig": {
93
178
  "access": "restricted"
179
+ },
180
+ "scripts": {
181
+ "build-tests": "tsc --noEmit --lib dom,esnext",
182
+ "deploy": "echo Deploy not allowed!",
183
+ "deploy3": "echo Deploy3 not allowed!",
184
+ "start": "nodemon",
185
+ "start-esm": "node ./dist/node/index.mjs",
186
+ "test": "vitest run",
187
+ "test:ci": "vitest run",
188
+ "types": "tsc --noEmit -p tsconfig.test.json"
94
189
  }
95
- }
190
+ }
@@ -1,402 +0,0 @@
1
- import type { Attributes, Counter } from '@opentelemetry/api'
2
- import {
3
- assertEx, creatable, isDefined, isUndefined,
4
- toHex,
5
- } from '@xylabs/sdk-js'
6
- import { ProducerConfig } from '@xyo-network/chain-orchestration'
7
- import { SimpleBlockRunner } from '@xyo-network/chain-services'
8
- import { WithHashMeta } from '@xyo-network/sdk-js'
9
- import type {
10
- ActorParamsV3,
11
- BlockBoundWitness,
12
- ChainContractViewer, ChainId, ChainStakeIntent, HydratedBlockWithHashMeta, XL1BlockNumber,
13
- } from '@xyo-network/xl1-sdk'
14
- import {
15
- AccountBalanceViewer, AccountBalanceViewerMoniker, ActorV3, asXL1BlockNumber, BlockRunner, BlockRunnerMoniker, BlockViewer, BlockViewerMoniker,
16
- buildTransaction, ChainContractViewerMoniker, createDeclarationIntent, FinalizationViewer, FinalizationViewerMoniker, MempoolRunner, MempoolRunnerMoniker,
17
- MempoolViewer, MempoolViewerMoniker, StakeTotalsViewer, StakeTotalsViewerMoniker,
18
- } from '@xyo-network/xl1-sdk'
19
- import { Mutex } from 'async-mutex'
20
-
21
- export type ProducerActorParams = ActorParamsV3<{
22
- config: ProducerConfig
23
- }>
24
-
25
- const SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true
26
- const TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds
27
-
28
- /**
29
- * Formats a hydrated block with hash meta into a string representation of its hash and block number.
30
- * @param blockBoundWitness The hydrated block with hash meta to format
31
- * @returns The formatted block reference string
32
- */
33
- const toFormattedBlockReference = (blockBoundWitness: WithHashMeta<BlockBoundWitness>): string => {
34
- return `${blockBoundWitness.block} [${toHex(blockBoundWitness._hash, { prefix: true })}]`
35
- }
36
-
37
- @creatable()
38
- export class ProducerActor extends ActorV3<ProducerActorParams> {
39
- /**
40
- * The interval time (in MS) between block production attempts.
41
- */
42
- static readonly BlockSubmissionCheckInterval = 1500 // 1.5 seconds
43
-
44
- /**
45
- * The threshold time (in MS) for resubmitting the same block number if the head has not changed.
46
- */
47
- static readonly HeadResubmissionThreshold = ProducerActor.BlockSubmissionCheckInterval * 30
48
-
49
- /**
50
- * The window (in blocks) before expiration to attempt redeclaration of producer intent.
51
- */
52
- static readonly RedeclarationWindow = 10_000
53
-
54
- protected _lastProducedBlock?: HydratedBlockWithHashMeta
55
- protected _lastRedeclarationIntent?: ChainStakeIntent
56
- protected _metricAttributes?: Attributes
57
- protected _producerActorBlockProductionAttempts?: Counter<Attributes>
58
- protected _producerActorBlockProductionChecks?: Counter<Attributes>
59
- protected _producerActorBlocksProduced?: Counter<Attributes>
60
- protected _producerActorBlocksPublished?: Counter<Attributes>
61
-
62
- private _accountBalanceViewer?: AccountBalanceViewer
63
- private _blockRunner?: BlockRunner
64
- private _blockViewer?: BlockViewer
65
- private _chainContractViewer?: ChainContractViewer
66
- private _chainId?: ChainId
67
- private _lastHeadChangeTime?: number
68
- private _lastHeadHash?: string
69
- private _mempoolRunner?: MempoolRunner
70
- private _mempoolViewer?: MempoolViewer
71
- private _produceBlockMutex = new Mutex()
72
- private _stakeTotalsViewer?: StakeTotalsViewer
73
-
74
- override get logger() {
75
- return assertEx(super.logger, () => 'Logger is required for ProducerActor')
76
- }
77
-
78
- protected get accountBalanceViewer() {
79
- return this._accountBalanceViewer!
80
- }
81
-
82
- protected get blockRunner() {
83
- return this._blockRunner!
84
- }
85
-
86
- protected get blockViewer() {
87
- return this._blockViewer!
88
- }
89
-
90
- protected get chainContractViewer() {
91
- return this._chainContractViewer!
92
- }
93
-
94
- protected get chainId() {
95
- return this._chainId!
96
- }
97
-
98
- protected get config() {
99
- return this.params.config
100
- }
101
-
102
- protected get mempoolRunner() {
103
- return this._mempoolRunner!
104
- }
105
-
106
- protected get mempoolViewer() {
107
- return this._mempoolViewer!
108
- }
109
-
110
- protected get stakeTotalsViewer() {
111
- return this._stakeTotalsViewer!
112
- }
113
-
114
- override async createHandler() {
115
- await super.createHandler()
116
- // Create the consistent meter attributes that will
117
- // be included with all metrics from this actor
118
- this._metricAttributes = { address: this.account.address.toString() }
119
- // Create the metrics
120
- this._producerActorBlockProductionChecks = this.meter?.createCounter(
121
- 'producer_actor_block_production_checks',
122
- { description: 'Number of block production checks' },
123
- )
124
- this._producerActorBlockProductionAttempts = this.meter?.createCounter(
125
- 'producer_actor_block_production_attempts',
126
- { description: 'Number of block production attempts' },
127
- )
128
- this._producerActorBlocksProduced = this.meter?.createCounter(
129
- 'producer_actor_blocks_produced',
130
- { description: 'Number of blocks produced' },
131
- )
132
- this._producerActorBlocksPublished = this.meter?.createCounter(
133
- 'producer_actor_blocks_published',
134
- { description: 'Number of blocks published' },
135
- )
136
- const final = await this.locator?.getInstance<FinalizationViewer>(FinalizationViewerMoniker)
137
- await final.start()
138
- this._accountBalanceViewer = assertEx(
139
- await this.locator?.getInstance<AccountBalanceViewer>(AccountBalanceViewerMoniker),
140
- () => 'Unable to locate AccountBalanceViewer',
141
- )
142
- this._blockRunner = assertEx(
143
- await this.locator?.getInstance<BlockRunner>(BlockRunnerMoniker),
144
- () => 'Unable to locate BlockRunner',
145
- )
146
- this._blockViewer = assertEx(
147
- await this.locator?.getInstance<BlockViewer>(BlockViewerMoniker),
148
- () => 'Unable to locate BlockViewer',
149
- )
150
- this._chainContractViewer = assertEx(
151
- await this.locator?.getInstance<ChainContractViewer>(ChainContractViewerMoniker),
152
- () => 'Unable to locate ChainContractViewer',
153
- )
154
- this._mempoolRunner = assertEx(
155
- await this.locator?.getInstance<MempoolRunner>(MempoolRunnerMoniker),
156
- () => 'Unable to locate MempoolRunner',
157
- )
158
- this._mempoolViewer = assertEx(
159
- await this.locator?.getInstance<MempoolViewer>(MempoolViewerMoniker),
160
- () => 'Unable to locate MempoolViewer',
161
- )
162
- this._stakeTotalsViewer = assertEx(
163
- await this.locator?.getInstance<StakeTotalsViewer>(StakeTotalsViewerMoniker),
164
- () => 'Unable to locate StakeTotalsViewer',
165
- )
166
- this._chainId = await this.chainContractViewer.chainId()
167
- }
168
-
169
- // async initLocator() {
170
- // const config = this.config
171
- // const endpoint = assertEx(config.services.apiEndpoint, () => 'API endpoint is required in config.services.apiEndpoint')
172
- // const transportFactory: TransportFactory = (schemas: RpcSchemaMap) => new HttpRpcTransport(endpoint, schemas)
173
- // const locator = await buildJsonRpcProviderLocatorV2(config, transportFactory)
174
-
175
- // const version = '1.0.0'
176
- // const telemetryConfig = buildTelemetryConfig(config, this.name, version, DefaultMetricsScrapePorts.producer)
177
- // const { traceProvider, meterProvider } = await startupSpanAsync('initTelemetry', () => initTelemetry(telemetryConfig))
178
-
179
- // locator.context.traceProvider = traceProvider
180
- // locator.context.meterProvider = meterProvider
181
-
182
- // locator.register(SimpleBlockRewardViewer.factory<SimpleBlockRewardViewer>(SimpleBlockRewardViewer.dependencies, {}))
183
- // locator.register(SimpleBlockValidationViewer.factory<SimpleBlockValidationViewer>(
184
- // SimpleBlockValidationViewer.dependencies,
185
- // { state: validateHydratedBlockState, protocol: validateHydratedBlock },
186
- // ))
187
- // locator.register(SimpleBlockRunner.factory<SimpleBlockRunner>(
188
- // SimpleBlockRunner.dependencies,
189
- // { account: this.params.account, rewardAddress: this.params.rewardAddress },
190
- // ))
191
- // return await initEvmProvidersIfAvailable(locator)
192
- // }
193
-
194
- override async startHandler() {
195
- await super.startHandler()
196
- // Register a timer to check if we should produce a block
197
- this.registerTimer('BlockProductionTimer', async () => {
198
- await this.produceBlock()
199
- }, 2000, ProducerActor.BlockSubmissionCheckInterval)
200
-
201
- if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {
202
- // Register a timer to check if we should redeclare the producer
203
- this.registerTimer('ProducerRedeclarationTimer', async () => {
204
- await this.redeclareIntent()
205
- }, TEN_MINUTES, TEN_MINUTES)
206
- }
207
- }
208
-
209
- protected calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): number {
210
- return (this._lastRedeclarationIntent?.exp ?? currentBlock) - currentBlock
211
- }
212
-
213
- protected async produceBlock(): Promise<void> {
214
- this._producerActorBlockProductionChecks?.add(1, this._metricAttributes)
215
- await this.spanAsync('produceBlock', async () => {
216
- if (this._produceBlockMutex.isLocked()) {
217
- this.logger?.log('Skipping block production, previous production still in progress')
218
- return
219
- }
220
-
221
- // eslint-disable-next-line complexity
222
- await this._produceBlockMutex.runExclusive(async () => {
223
- // Get the updated head
224
- const head = (await this.blockViewer.currentBlock())[0]
225
- // Check if we've already produced the next block for this head
226
- const headHash = head._hash
227
-
228
- // Track head changes
229
- const currentTime = Date.now()
230
- if (this._lastHeadHash !== headHash) {
231
- // Log the head change
232
- const lastHeadHashHex = isDefined(this._lastHeadHash) ? `0x${this._lastHeadHash}` : 'undefined'
233
- const currentHeadHashHex = `0x${headHash}`
234
- this.logger?.log(`Found updated head ${lastHeadHashHex} -> ${currentHeadHashHex}`)
235
-
236
- // Update the head change info
237
- this._lastHeadHash = headHash
238
- this._lastHeadChangeTime = currentTime
239
- }
240
-
241
- // Check if we should resubmit due to stale head
242
- const timeSinceHeadChange = isDefined(this._lastHeadChangeTime) ? currentTime - this._lastHeadChangeTime : 0
243
-
244
- // If we have never produced a block or the last produced block was not built on the current head we
245
- // need to attempt to produce a new block
246
- const shouldSubmit = !this._lastProducedBlock || this._lastProducedBlock[0].previous !== headHash
247
-
248
- // If the head has not changed determine if we should resubmit again based on head staleness
249
- const shouldResubmit = timeSinceHeadChange > ProducerActor.HeadResubmissionThreshold
250
-
251
- // Determine if we should submit or resubmit
252
- const shouldSubmitBlock = shouldSubmit || shouldResubmit
253
-
254
- if (shouldSubmitBlock) {
255
- // If we are resubmitting due to stale head
256
- if (shouldResubmit) {
257
- // Log that we are resubmitting due to stale head
258
- this.logger?.log(`Resubmitting block due to stale head. Head ${toFormattedBlockReference(head)} unchanged for ${timeSinceHeadChange}ms`)
259
- // Reset timer since we're resubmitting
260
- this._lastHeadChangeTime = currentTime
261
- }
262
- this._producerActorBlockProductionAttempts?.add(1, this._metricAttributes)
263
- // Produce the next block
264
- const nextBlock = await this.blockRunner.produceNextBlock(head, shouldResubmit)
265
- // If it was produced
266
- if (nextBlock) {
267
- const displayBlockNumber = toFormattedBlockReference(nextBlock[0])
268
- this.logger?.log('Produced block:', displayBlockNumber)
269
- this._producerActorBlocksProduced?.add(1, this._metricAttributes)
270
- // Insert the block into the chain
271
- await this.mempoolRunner.submitBlocks([nextBlock])
272
- this.logger?.log('Published block:', displayBlockNumber)
273
- this._producerActorBlocksPublished?.add(1, this._metricAttributes)
274
- // Record that we have produced a block so we do not produce it again
275
- this._lastProducedBlock = nextBlock
276
- } else {
277
- this.logger?.log('No block produced for submission.')
278
- }
279
- } else {
280
- // Log that we are not submitting a block at this time
281
- let nonSubmissionMessage = 'No block submission required at this time'
282
- // If we have already produced a block
283
- if (isDefined(this._lastProducedBlock)) {
284
- // include that info in the log
285
- nonSubmissionMessage = nonSubmissionMessage + `, already produced: ${toFormattedBlockReference(this._lastProducedBlock[0])}`
286
- }
287
- this.logger?.log(`${nonSubmissionMessage}.`)
288
- }
289
- })
290
- }, { ...this.context, timeBudgetLimit: 1000 })
291
- }
292
-
293
- protected async redeclareIntent(): Promise<void> {
294
- await this.spanAsync('redeclareIntent', async () => {
295
- // Decide if we should redeclare intent
296
- if (this.config.disableIntentRedeclaration) return
297
-
298
- // Get the current block
299
- const head = (await this.blockViewer.currentBlock())[0]
300
- if (isUndefined(head)) return
301
- const currentBlock = head.block
302
-
303
- // // Calculate the time until the producer's declaration expires
304
- const blocksUntilExpiration = this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)
305
-
306
- // Allow the producer time to redeclare itself via block production
307
- // (for free) before submitting a redeclaration intent transaction.
308
- if (blocksUntilExpiration > ProducerActor.RedeclarationWindow * 0.1) {
309
- // Clear any previous redeclaration intent
310
- this._lastRedeclarationIntent = undefined
311
- // No need to redeclare yet
312
- return
313
- }
314
-
315
- // If we already have a valid redeclaration intent, do not create another
316
- // unless it has expired.
317
- if (this._lastRedeclarationIntent) {
318
- // Check if the last redeclaration intent is still valid
319
- if (this._lastRedeclarationIntent.exp > currentBlock) return
320
- // If it has expired, clear the last redeclaration intent
321
- this._lastRedeclarationIntent = undefined
322
- }
323
-
324
- // Check if we have a valid balance before declaring intent
325
- if (!await this.validateCurrentBalance()) {
326
- this.logger?.error(
327
- `Add balance to address ${this.account.address} for the producer to declare it's intent.`,
328
- )
329
- return
330
- }
331
-
332
- // Check if we have a valid stake before declaring intent
333
- if (!(await this.validateCurrentStake())) {
334
- this.logger?.error(
335
- `Add stake to contract address ${this.chainId}`
336
- + ' for the producer to declare it\'s intent.',
337
- )
338
- return
339
- }
340
-
341
- // Create a redeclaration intent
342
- this.logger?.log('Creating redeclaration intent for producer:', this.account.address)
343
- const redeclarationIntent = createDeclarationIntent(
344
- this.account.address,
345
- 'producer',
346
- currentBlock,
347
- currentBlock + SimpleBlockRunner.RedeclarationDuration,
348
- )
349
-
350
- // Submit the redeclaration intent
351
- await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)
352
-
353
- // On successful submission, save the redeclaration intent
354
- this._lastRedeclarationIntent = redeclarationIntent
355
- }, { ...this.context, timeBudgetLimit: 1000 })
356
- }
357
-
358
- protected async submitRedeclarationIntent(currentBlock: XL1BlockNumber, redeclarationIntent: ChainStakeIntent): Promise<void> {
359
- this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)
360
- // Create a transaction to submit the redeclaration intent
361
- const tx = await buildTransaction(
362
- this.chainId,
363
- [redeclarationIntent],
364
- [],
365
- this.account,
366
- currentBlock,
367
- asXL1BlockNumber(currentBlock + 1000, true),
368
- )
369
-
370
- // Submit the redeclaration intent
371
- await this.mempoolRunner.submitTransactions([tx])
372
-
373
- this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)
374
- }
375
-
376
- protected async validateCurrentBalance(): Promise<boolean> {
377
- // Check if we have a valid balance before declaring intent
378
- const head = (await this.blockViewer.currentBlock())?.[0]?._hash
379
- if (isDefined(head)) {
380
- const balances = await this.accountBalanceViewer.accountBalances([this.account.address], { head })
381
- const currentBalance = balances[this.account.address] ?? 0n
382
- if (currentBalance <= 0n) {
383
- this.logger?.error(`Producer ${this.account.address} has no balance.`)
384
- return false
385
- }
386
- return true
387
- }
388
- return true
389
- }
390
-
391
- protected async validateCurrentStake(): Promise<boolean> {
392
- // Use StakeIntentService to get the required minimum stake
393
- const requiredMinimumStake = 1n // this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')
394
- // Check if we have a valid stake before declaring intent
395
- const currentStake = await this.stakeTotalsViewer.activeByStaked(this.account.address)
396
- if (currentStake < requiredMinimumStake) {
397
- this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)
398
- return false
399
- }
400
- return true
401
- }
402
- }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './ProducerActor.ts'
2
- export * from './runProducer.ts'
@@ -1,31 +0,0 @@
1
- import type { CreatableName } from '@xylabs/sdk-js'
2
- import { exists } from '@xylabs/sdk-js'
3
- import {
4
- initActorAccount,
5
- type OrchestratorInstance,
6
- type ProducerConfig,
7
- } from '@xyo-network/chain-orchestration'
8
- import type { ProviderFactoryLocatorInstance } from '@xyo-network/xl1-sdk'
9
-
10
- import type { ProducerActorParams } from './ProducerActor.ts'
11
- import { ProducerActor } from './ProducerActor.ts'
12
-
13
- export const runProducer = async (
14
- config: ProducerConfig,
15
- orchestrator: OrchestratorInstance,
16
- locator: ProviderFactoryLocatorInstance,
17
- ) => {
18
- const account = await initActorAccount({ ...locator.context, config })
19
- // Create actors
20
- const producer = await ProducerActor.create({
21
- config, locator, name: 'xl1-producer' as CreatableName, account,
22
- } satisfies ProducerActorParams)
23
- const actors = [producer].filter(exists)
24
-
25
- for (const actor of actors) {
26
- // Register the actor with the orchestrator
27
- await orchestrator.registerActor(actor)
28
- }
29
- // Start the orchestrator => automatically activates the actor
30
- await orchestrator.start()
31
- }