@xyo-network/xl1-cli-lib 1.7.4
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/LICENSE +165 -0
- package/README.md +46 -0
- package/dist/node/commands/api/index.d.ts +2 -0
- package/dist/node/commands/api/index.d.ts.map +1 -0
- package/dist/node/commands/api/runApi.d.ts +9 -0
- package/dist/node/commands/api/runApi.d.ts.map +1 -0
- package/dist/node/commands/index.d.ts +4 -0
- package/dist/node/commands/index.d.ts.map +1 -0
- package/dist/node/commands/producer/index.d.ts +2 -0
- package/dist/node/commands/producer/index.d.ts.map +1 -0
- package/dist/node/commands/producer/runProducer.d.ts +11 -0
- package/dist/node/commands/producer/runProducer.d.ts.map +1 -0
- package/dist/node/commands/validator/index.d.ts +2 -0
- package/dist/node/commands/validator/index.d.ts.map +1 -0
- package/dist/node/commands/validator/runValidator.d.ts +11 -0
- package/dist/node/commands/validator/runValidator.d.ts.map +1 -0
- package/dist/node/images.d.ts +2 -0
- package/dist/node/images.d.ts.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.mjs +1712 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/node/initConfig.d.ts +3 -0
- package/dist/node/initConfig.d.ts.map +1 -0
- package/dist/node/initLogger.d.ts +4 -0
- package/dist/node/initLogger.d.ts.map +1 -0
- package/dist/node/optionsFromZodSchema.d.ts +4 -0
- package/dist/node/optionsFromZodSchema.d.ts.map +1 -0
- package/dist/node/orchestration/ChainInitializableParams.d.ts +11 -0
- package/dist/node/orchestration/ChainInitializableParams.d.ts.map +1 -0
- package/dist/node/orchestration/actor/implementation/BalanceActor.d.ts +18 -0
- package/dist/node/orchestration/actor/implementation/BalanceActor.d.ts.map +1 -0
- package/dist/node/orchestration/actor/implementation/ChainHeadUpdateActor.d.ts +19 -0
- package/dist/node/orchestration/actor/implementation/ChainHeadUpdateActor.d.ts.map +1 -0
- package/dist/node/orchestration/actor/implementation/ProducerActor.d.ts +28 -0
- package/dist/node/orchestration/actor/implementation/ProducerActor.d.ts.map +1 -0
- package/dist/node/orchestration/actor/implementation/ValidatorActor.d.ts +26 -0
- package/dist/node/orchestration/actor/implementation/ValidatorActor.d.ts.map +1 -0
- package/dist/node/orchestration/actor/implementation/index.d.ts +5 -0
- package/dist/node/orchestration/actor/implementation/index.d.ts.map +1 -0
- package/dist/node/orchestration/actor/index.d.ts +3 -0
- package/dist/node/orchestration/actor/index.d.ts.map +1 -0
- package/dist/node/orchestration/actor/model/Actor.d.ts +39 -0
- package/dist/node/orchestration/actor/model/Actor.d.ts.map +1 -0
- package/dist/node/orchestration/actor/model/Orchestrator.d.ts +27 -0
- package/dist/node/orchestration/actor/model/Orchestrator.d.ts.map +1 -0
- package/dist/node/orchestration/actor/model/index.d.ts +3 -0
- package/dist/node/orchestration/actor/model/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainFinalized/archivist.d.ts +4 -0
- package/dist/node/orchestration/archivists/ChainFinalized/archivist.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainFinalized/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/ChainFinalized/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainFinalized/local.d.ts +7 -0
- package/dist/node/orchestration/archivists/ChainFinalized/local.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainFinalized/remote.d.ts +7 -0
- package/dist/node/orchestration/archivists/ChainFinalized/remote.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/archivist.d.ts +7 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/archivist.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/remote.d.ts +7 -0
- package/dist/node/orchestration/archivists/ChainSubmissions/remote.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/PendingTransactions/archivist.d.ts +6 -0
- package/dist/node/orchestration/archivists/PendingTransactions/archivist.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/PendingTransactions/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/PendingTransactions/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/PendingTransactions/local.d.ts +7 -0
- package/dist/node/orchestration/archivists/PendingTransactions/local.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/PendingTransactions/remote.d.ts +7 -0
- package/dist/node/orchestration/archivists/PendingTransactions/remote.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/archivist.d.ts +4 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/archivist.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/local.d.ts +4 -0
- package/dist/node/orchestration/archivists/RejectedTransactions/local.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/StakeIntentState/archivist.d.ts +5 -0
- package/dist/node/orchestration/archivists/StakeIntentState/archivist.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/StakeIntentState/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/StakeIntentState/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/StakeIntentState/local.d.ts +7 -0
- package/dist/node/orchestration/archivists/StakeIntentState/local.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/index.d.ts +6 -0
- package/dist/node/orchestration/archivists/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/lib/index.d.ts +2 -0
- package/dist/node/orchestration/archivists/lib/index.d.ts.map +1 -0
- package/dist/node/orchestration/archivists/lib/localPersistentArchivist.d.ts +20 -0
- package/dist/node/orchestration/archivists/lib/localPersistentArchivist.d.ts.map +1 -0
- package/dist/node/orchestration/health/index.d.ts +2 -0
- package/dist/node/orchestration/health/index.d.ts.map +1 -0
- package/dist/node/orchestration/health/initHealthEndpoints.d.ts +11 -0
- package/dist/node/orchestration/health/initHealthEndpoints.d.ts.map +1 -0
- package/dist/node/orchestration/host/implementation/DefaultHost.d.ts +12 -0
- package/dist/node/orchestration/host/implementation/DefaultHost.d.ts.map +1 -0
- package/dist/node/orchestration/host/implementation/DefaultServiceCollection.d.ts +24 -0
- package/dist/node/orchestration/host/implementation/DefaultServiceCollection.d.ts.map +1 -0
- package/dist/node/orchestration/host/implementation/DefaultServiceProvider.d.ts +7 -0
- package/dist/node/orchestration/host/implementation/DefaultServiceProvider.d.ts.map +1 -0
- package/dist/node/orchestration/host/implementation/index.d.ts +3 -0
- package/dist/node/orchestration/host/implementation/index.d.ts.map +1 -0
- package/dist/node/orchestration/host/index.d.ts +3 -0
- package/dist/node/orchestration/host/index.d.ts.map +1 -0
- package/dist/node/orchestration/host/model/Host.d.ts +19 -0
- package/dist/node/orchestration/host/model/Host.d.ts.map +1 -0
- package/dist/node/orchestration/host/model/ServiceCollection.d.ts +20 -0
- package/dist/node/orchestration/host/model/ServiceCollection.d.ts.map +1 -0
- package/dist/node/orchestration/host/model/ServiceProvider.d.ts +4 -0
- package/dist/node/orchestration/host/model/ServiceProvider.d.ts.map +1 -0
- package/dist/node/orchestration/host/model/index.d.ts +4 -0
- package/dist/node/orchestration/host/model/index.d.ts.map +1 -0
- package/dist/node/orchestration/index.d.ts +6 -0
- package/dist/node/orchestration/index.d.ts.map +1 -0
- package/dist/node/orchestration/initServices.d.ts +9 -0
- package/dist/node/orchestration/initServices.d.ts.map +1 -0
- package/dist/node/orchestration/map/BalanceSummary/index.d.ts +2 -0
- package/dist/node/orchestration/map/BalanceSummary/index.d.ts.map +1 -0
- package/dist/node/orchestration/map/BalanceSummary/initBalanceSummaryMap.d.ts +7 -0
- package/dist/node/orchestration/map/BalanceSummary/initBalanceSummaryMap.d.ts.map +1 -0
- package/dist/node/orchestration/map/BalanceSummary/local.d.ts +7 -0
- package/dist/node/orchestration/map/BalanceSummary/local.d.ts.map +1 -0
- package/dist/node/orchestration/map/driver/index.d.ts +2 -0
- package/dist/node/orchestration/map/driver/index.d.ts.map +1 -0
- package/dist/node/orchestration/map/driver/lmdb/Params.d.ts +7 -0
- package/dist/node/orchestration/map/driver/lmdb/Params.d.ts.map +1 -0
- package/dist/node/orchestration/map/driver/lmdb/SynchronousLmdbMap.d.ts +32 -0
- package/dist/node/orchestration/map/driver/lmdb/SynchronousLmdbMap.d.ts.map +1 -0
- package/dist/node/orchestration/map/driver/lmdb/index.d.ts +2 -0
- package/dist/node/orchestration/map/driver/lmdb/index.d.ts.map +1 -0
- package/dist/node/orchestration/map/driver/lmdb/spec/SynchronousLmdbMap.spec.d.ts +2 -0
- package/dist/node/orchestration/map/driver/lmdb/spec/SynchronousLmdbMap.spec.d.ts.map +1 -0
- package/dist/node/orchestration/map/index.d.ts +3 -0
- package/dist/node/orchestration/map/index.d.ts.map +1 -0
- package/dist/node/orchestration/map/localPersistentMap.d.ts +13 -0
- package/dist/node/orchestration/map/localPersistentMap.d.ts.map +1 -0
- package/dist/node/orchestration/repository/index.d.ts +2 -0
- package/dist/node/orchestration/repository/index.d.ts.map +1 -0
- package/dist/node/orchestration/repository/lib/index.d.ts +2 -0
- package/dist/node/orchestration/repository/lib/index.d.ts.map +1 -0
- package/dist/node/orchestration/repository/lib/repositoryFromArchivist.d.ts +6 -0
- package/dist/node/orchestration/repository/lib/repositoryFromArchivist.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/account.d.ts +7 -0
- package/dist/node/orchestration/services/implementation/account.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/balance.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/balance.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/chain/evm.d.ts +7 -0
- package/dist/node/orchestration/services/implementation/chain/evm.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/chain/index.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/chain/index.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/evm/index.d.ts +2 -0
- package/dist/node/orchestration/services/implementation/evm/index.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/evm/initChainId.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/evm/initChainId.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/evm/initEvmProvider.d.ts +11 -0
- package/dist/node/orchestration/services/implementation/evm/initEvmProvider.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/evm/initInfuraProvider.d.ts +6 -0
- package/dist/node/orchestration/services/implementation/evm/initInfuraProvider.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/evm/initJsonRpcProvider.d.ts +6 -0
- package/dist/node/orchestration/services/implementation/evm/initJsonRpcProvider.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/head.d.ts +10 -0
- package/dist/node/orchestration/services/implementation/head.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/index.d.ts +11 -0
- package/dist/node/orchestration/services/implementation/index.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/iterator.d.ts +6 -0
- package/dist/node/orchestration/services/implementation/iterator.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/pendingTransactions.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/pendingTransactions.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/producer.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/producer.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/reward.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/reward.d.ts.map +1 -0
- package/dist/node/orchestration/services/implementation/validator.d.ts +4 -0
- package/dist/node/orchestration/services/implementation/validator.d.ts.map +1 -0
- package/dist/node/orchestration/services/index.d.ts +2 -0
- package/dist/node/orchestration/services/index.d.ts.map +1 -0
- package/dist/node/orchestration/status/RuntimeStatusMonitor.d.ts +31 -0
- package/dist/node/orchestration/status/RuntimeStatusMonitor.d.ts.map +1 -0
- package/dist/node/orchestration/status/ServiceStatus.d.ts +9 -0
- package/dist/node/orchestration/status/ServiceStatus.d.ts.map +1 -0
- package/dist/node/orchestration/status/index.d.ts +3 -0
- package/dist/node/orchestration/status/index.d.ts.map +1 -0
- package/dist/node/orchestration/store/StoreKind.d.ts +2 -0
- package/dist/node/orchestration/store/StoreKind.d.ts.map +1 -0
- package/dist/node/orchestration/store/getStoreDirectory.d.ts +10 -0
- package/dist/node/orchestration/store/getStoreDirectory.d.ts.map +1 -0
- package/dist/node/orchestration/store/index.d.ts +3 -0
- package/dist/node/orchestration/store/index.d.ts.map +1 -0
- package/dist/node/runCLI.d.ts +2 -0
- package/dist/node/runCLI.d.ts.map +1 -0
- package/dist/node/spec/BootstrapChain.spec.d.ts +2 -0
- package/dist/node/spec/BootstrapChain.spec.d.ts.map +1 -0
- package/dist/node/start.d.ts +2 -0
- package/dist/node/start.d.ts.map +1 -0
- package/dist/node/waitForHostPort.d.ts +2 -0
- package/dist/node/waitForHostPort.d.ts.map +1 -0
- package/dist/node/xl1.d.ts +2 -0
- package/dist/node/xl1.d.ts.map +1 -0
- package/dist/node/xl1.mjs +1610 -0
- package/dist/node/xl1.mjs.map +1 -0
- package/package.json +96 -0
- package/src/commands/api/index.ts +1 -0
- package/src/commands/api/runApi.ts +12 -0
- package/src/commands/index.ts +3 -0
- package/src/commands/producer/index.ts +1 -0
- package/src/commands/producer/runProducer.ts +38 -0
- package/src/commands/validator/index.ts +1 -0
- package/src/commands/validator/runValidator.ts +38 -0
- package/src/images.ts +19 -0
- package/src/index.ts +3 -0
- package/src/initConfig.ts +6 -0
- package/src/initLogger.ts +23 -0
- package/src/optionsFromZodSchema.ts +69 -0
- package/src/orchestration/ChainInitializableParams.ts +12 -0
- package/src/orchestration/actor/implementation/BalanceActor.ts +50 -0
- package/src/orchestration/actor/implementation/ChainHeadUpdateActor.ts +65 -0
- package/src/orchestration/actor/implementation/ProducerActor.ts +244 -0
- package/src/orchestration/actor/implementation/ValidatorActor.ts +113 -0
- package/src/orchestration/actor/implementation/index.ts +4 -0
- package/src/orchestration/actor/index.ts +2 -0
- package/src/orchestration/actor/model/Actor.ts +114 -0
- package/src/orchestration/actor/model/Orchestrator.ts +71 -0
- package/src/orchestration/actor/model/index.ts +2 -0
- package/src/orchestration/archivists/ChainFinalized/archivist.ts +29 -0
- package/src/orchestration/archivists/ChainFinalized/index.ts +1 -0
- package/src/orchestration/archivists/ChainFinalized/local.ts +18 -0
- package/src/orchestration/archivists/ChainFinalized/remote.ts +20 -0
- package/src/orchestration/archivists/ChainSubmissions/archivist.ts +20 -0
- package/src/orchestration/archivists/ChainSubmissions/index.ts +1 -0
- package/src/orchestration/archivists/ChainSubmissions/remote.ts +20 -0
- package/src/orchestration/archivists/PendingTransactions/archivist.ts +29 -0
- package/src/orchestration/archivists/PendingTransactions/index.ts +1 -0
- package/src/orchestration/archivists/PendingTransactions/local.ts +21 -0
- package/src/orchestration/archivists/PendingTransactions/remote.ts +20 -0
- package/src/orchestration/archivists/RejectedTransactions/archivist.ts +18 -0
- package/src/orchestration/archivists/RejectedTransactions/index.ts +1 -0
- package/src/orchestration/archivists/RejectedTransactions/local.ts +20 -0
- package/src/orchestration/archivists/StakeIntentState/archivist.ts +21 -0
- package/src/orchestration/archivists/StakeIntentState/index.ts +1 -0
- package/src/orchestration/archivists/StakeIntentState/local.ts +19 -0
- package/src/orchestration/archivists/index.ts +5 -0
- package/src/orchestration/archivists/lib/index.ts +1 -0
- package/src/orchestration/archivists/lib/localPersistentArchivist.ts +57 -0
- package/src/orchestration/health/index.ts +1 -0
- package/src/orchestration/health/initHealthEndpoints.ts +72 -0
- package/src/orchestration/host/implementation/DefaultHost.ts +25 -0
- package/src/orchestration/host/implementation/DefaultServiceCollection.ts +60 -0
- package/src/orchestration/host/implementation/DefaultServiceProvider.ts +12 -0
- package/src/orchestration/host/implementation/index.ts +2 -0
- package/src/orchestration/host/index.ts +2 -0
- package/src/orchestration/host/model/Host.ts +21 -0
- package/src/orchestration/host/model/ServiceCollection.ts +22 -0
- package/src/orchestration/host/model/ServiceProvider.ts +3 -0
- package/src/orchestration/host/model/index.ts +3 -0
- package/src/orchestration/index.ts +5 -0
- package/src/orchestration/initServices.ts +240 -0
- package/src/orchestration/map/BalanceSummary/index.ts +1 -0
- package/src/orchestration/map/BalanceSummary/initBalanceSummaryMap.ts +22 -0
- package/src/orchestration/map/BalanceSummary/local.ts +21 -0
- package/src/orchestration/map/driver/index.ts +1 -0
- package/src/orchestration/map/driver/lmdb/Params.ts +7 -0
- package/src/orchestration/map/driver/lmdb/SynchronousLmdbMap.ts +66 -0
- package/src/orchestration/map/driver/lmdb/index.ts +1 -0
- package/src/orchestration/map/driver/lmdb/spec/SynchronousLmdbMap.spec.ts +97 -0
- package/src/orchestration/map/index.ts +2 -0
- package/src/orchestration/map/localPersistentMap.ts +52 -0
- package/src/orchestration/repository/index.ts +1 -0
- package/src/orchestration/repository/lib/index.ts +1 -0
- package/src/orchestration/repository/lib/repositoryFromArchivist.ts +27 -0
- package/src/orchestration/services/implementation/account.ts +24 -0
- package/src/orchestration/services/implementation/balance.ts +13 -0
- package/src/orchestration/services/implementation/chain/evm.ts +38 -0
- package/src/orchestration/services/implementation/chain/index.ts +22 -0
- package/src/orchestration/services/implementation/evm/index.ts +1 -0
- package/src/orchestration/services/implementation/evm/initChainId.ts +20 -0
- package/src/orchestration/services/implementation/evm/initEvmProvider.ts +23 -0
- package/src/orchestration/services/implementation/evm/initInfuraProvider.ts +27 -0
- package/src/orchestration/services/implementation/evm/initJsonRpcProvider.ts +21 -0
- package/src/orchestration/services/implementation/head.ts +44 -0
- package/src/orchestration/services/implementation/index.ts +10 -0
- package/src/orchestration/services/implementation/iterator.ts +36 -0
- package/src/orchestration/services/implementation/pendingTransactions.ts +14 -0
- package/src/orchestration/services/implementation/producer.ts +13 -0
- package/src/orchestration/services/implementation/reward.ts +41 -0
- package/src/orchestration/services/implementation/validator.ts +14 -0
- package/src/orchestration/services/index.ts +1 -0
- package/src/orchestration/status/RuntimeStatusMonitor.ts +117 -0
- package/src/orchestration/status/ServiceStatus.ts +21 -0
- package/src/orchestration/status/index.ts +2 -0
- package/src/orchestration/store/StoreKind.ts +1 -0
- package/src/orchestration/store/getStoreDirectory.ts +14 -0
- package/src/orchestration/store/index.ts +2 -0
- package/src/runCLI.ts +111 -0
- package/src/spec/BootstrapChain.spec.ts +133 -0
- package/src/spec/MultiProducer.ChainOutput.json +864 -0
- package/src/start.ts +8 -0
- package/src/waitForHostPort.ts +26 -0
- package/src/xl1.ts +8 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Base } from '@xylabs/base'
|
|
2
|
+
import type { Logger, LogLevelValue } from '@xylabs/logger'
|
|
3
|
+
import {
|
|
4
|
+
ConsoleLogger, LogLevel, SilentLogger,
|
|
5
|
+
} from '@xylabs/logger'
|
|
6
|
+
import { isDefined } from '@xylabs/typeof'
|
|
7
|
+
import type { Config } from '@xyo-network/xl1-protocol-sdk'
|
|
8
|
+
|
|
9
|
+
export const initLogger = (config: Config): Logger => {
|
|
10
|
+
let logger: Logger
|
|
11
|
+
if (config.silent) {
|
|
12
|
+
logger = new SilentLogger()
|
|
13
|
+
} else {
|
|
14
|
+
let level: LogLevelValue | undefined
|
|
15
|
+
if (isDefined(config.logLevel)) {
|
|
16
|
+
const parsed = LogLevel[config.logLevel.toLowerCase() as keyof typeof LogLevel]
|
|
17
|
+
if (isDefined(parsed)) level = parsed
|
|
18
|
+
}
|
|
19
|
+
logger = new ConsoleLogger(level)
|
|
20
|
+
}
|
|
21
|
+
Base.defaultLogger = logger
|
|
22
|
+
return logger
|
|
23
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Options } from 'yargs'
|
|
2
|
+
import type { ZodTypeAny } from 'zod'
|
|
3
|
+
import {
|
|
4
|
+
ZodBoolean, ZodDefault, ZodEnum, ZodNumber, ZodObject, ZodOptional,
|
|
5
|
+
} from 'zod'
|
|
6
|
+
|
|
7
|
+
const collectOptions = (
|
|
8
|
+
zodType: ZodTypeAny,
|
|
9
|
+
path: string[] = [],
|
|
10
|
+
out: Record<string, Options> = {},
|
|
11
|
+
): Record<string, Options> => {
|
|
12
|
+
// Handle objects by descending into their shape
|
|
13
|
+
if (zodType instanceof ZodObject) {
|
|
14
|
+
const shape = (zodType as ZodObject<{}>).shape
|
|
15
|
+
for (const [k, v] of Object.entries(shape)) {
|
|
16
|
+
collectOptions(v as ZodTypeAny, [...path, k], out)
|
|
17
|
+
}
|
|
18
|
+
return out
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Handle defaults and optionals by descending into the inner type
|
|
22
|
+
// TODO: Less brittle
|
|
23
|
+
const def = zodType._def
|
|
24
|
+
if (def?.innerType instanceof ZodObject) {
|
|
25
|
+
const shape = def.innerType.shape
|
|
26
|
+
for (const [k, v] of Object.entries(shape)) {
|
|
27
|
+
collectOptions(v as ZodTypeAny, [...path, k], out)
|
|
28
|
+
}
|
|
29
|
+
return out
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Construct the Yargs Options
|
|
33
|
+
const options: Options = {}
|
|
34
|
+
|
|
35
|
+
// Assign the description
|
|
36
|
+
options.describe = def.description
|
|
37
|
+
|
|
38
|
+
// Assign the default value
|
|
39
|
+
options.default = typeof def.defaultValue === 'function'
|
|
40
|
+
? def.defaultValue()
|
|
41
|
+
: def.defaultValue
|
|
42
|
+
|
|
43
|
+
// Assign the type
|
|
44
|
+
let type: 'string' | 'number' | 'boolean' = 'string'
|
|
45
|
+
const typeName = def.typeName === ZodDefault.name || def.typeName === ZodOptional.name
|
|
46
|
+
? def.innerType._def.typeName
|
|
47
|
+
: def.typeName
|
|
48
|
+
if (typeName === ZodBoolean.name) type = 'boolean'
|
|
49
|
+
if (typeName === ZodNumber.name || def.coerce) type = 'number'
|
|
50
|
+
if (typeName === ZodEnum.name) {
|
|
51
|
+
options.choices = Object.values(def.innerType.enum)
|
|
52
|
+
}
|
|
53
|
+
options.type = type
|
|
54
|
+
|
|
55
|
+
// Build argument path e.g. "db.host" or "producer.port"
|
|
56
|
+
const key = path.join('.')
|
|
57
|
+
out[key] = options
|
|
58
|
+
return out
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const optionsFromZodSchema = (
|
|
62
|
+
schema: Record<string, ZodTypeAny>,
|
|
63
|
+
): Record<string, Options> => {
|
|
64
|
+
const opts: Record<string, Options> = {}
|
|
65
|
+
for (const [rootKey, zodType] of Object.entries(schema)) {
|
|
66
|
+
collectOptions(zodType, [rootKey], opts)
|
|
67
|
+
}
|
|
68
|
+
return opts
|
|
69
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MeterProvider, TracerProvider } from '@opentelemetry/api'
|
|
2
|
+
import type { InitializableParams } from '@xyo-network/xl1-protocol'
|
|
3
|
+
import type { Config } from '@xyo-network/xl1-protocol-sdk'
|
|
4
|
+
|
|
5
|
+
import type { RuntimeStatusMonitor } from './status/index.ts'
|
|
6
|
+
|
|
7
|
+
export type ChainInitializableParams = InitializableParams<{
|
|
8
|
+
config: Config
|
|
9
|
+
meterProvider: MeterProvider
|
|
10
|
+
statusReporter: RuntimeStatusMonitor
|
|
11
|
+
traceProvider: TracerProvider
|
|
12
|
+
}>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import type { BaseParams } from '@xylabs/base'
|
|
3
|
+
import type { Promisable } from '@xylabs/promise'
|
|
4
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
5
|
+
import type { ChainServiceCollectionV2, Config } from '@xyo-network/xl1-protocol-sdk'
|
|
6
|
+
import { Mutex } from 'async-mutex'
|
|
7
|
+
|
|
8
|
+
import { Actor } from '../model/index.ts'
|
|
9
|
+
|
|
10
|
+
export type BalanceActorParams = BaseParams<
|
|
11
|
+
Pick<ChainServiceCollectionV2, 'balanceService' | 'chainIterator'>
|
|
12
|
+
& { config: Config }>
|
|
13
|
+
|
|
14
|
+
export class BalanceActor extends Actor<BalanceActorParams> {
|
|
15
|
+
protected _updateMutex = new Mutex()
|
|
16
|
+
|
|
17
|
+
protected constructor(params: BalanceActorParams) {
|
|
18
|
+
super('BalanceActor', 'Balance', params)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected get balanceService() {
|
|
22
|
+
return assertEx(this.params.balanceService, () => 'balanceService not set')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected get chainIterator() {
|
|
26
|
+
return assertEx(this.params.chainIterator, () => 'chainIterator not set')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static create(params: BalanceActorParams): Promisable<BalanceActor> {
|
|
30
|
+
return new BalanceActor(params)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override async start(): Promise<void> {
|
|
34
|
+
await super.start()
|
|
35
|
+
this.chainIterator.on('headUpdate', async () => {
|
|
36
|
+
await this.updateBalance()
|
|
37
|
+
})
|
|
38
|
+
this.registerTimer('BalanceTimer', async () => {
|
|
39
|
+
await this.updateBalance()
|
|
40
|
+
}, 1000, 1000)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected async updateBalance(): Promise<void> {
|
|
44
|
+
if (this._updateMutex.isLocked()) return
|
|
45
|
+
await this._updateMutex.runExclusive(async () => {
|
|
46
|
+
const head = await PayloadBuilder.hash(await this.chainIterator.head())
|
|
47
|
+
await this.balanceService.balances(head, [])
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { filterAs } from '@xylabs/array'
|
|
2
|
+
import { assertEx } from '@xylabs/assert'
|
|
3
|
+
import type { BaseParams } from '@xylabs/base'
|
|
4
|
+
import { toHex } from '@xylabs/hex'
|
|
5
|
+
import type { Promisable } from '@xylabs/promise'
|
|
6
|
+
import { findMostRecentBlock, sortBlocks } from '@xyo-network/chain-protocol'
|
|
7
|
+
import type { Payload } from '@xyo-network/payload-model'
|
|
8
|
+
import type { BlockBoundWitness } from '@xyo-network/xl1-protocol'
|
|
9
|
+
import { asBlockBoundWitness } from '@xyo-network/xl1-protocol'
|
|
10
|
+
import type { ChainServiceCollectionV2, Config } from '@xyo-network/xl1-protocol-sdk'
|
|
11
|
+
|
|
12
|
+
import { Actor } from '../model/index.ts'
|
|
13
|
+
|
|
14
|
+
export type ChainHeadUpdateActorParams = BaseParams<Pick<ChainServiceCollectionV2, 'chainIterator' | 'chainArchivist'> & { config: Config }>
|
|
15
|
+
|
|
16
|
+
export class ChainHeadUpdateActor extends Actor<ChainHeadUpdateActorParams> {
|
|
17
|
+
protected constructor(params: ChainHeadUpdateActorParams) {
|
|
18
|
+
super('ChainHeadUpdate', 'ChainHeadUpdate', params)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected get chainFinalizedArchivist() {
|
|
22
|
+
return assertEx(this.params.chainArchivist, () => 'ChainArchivist not set')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected get chainIterator() {
|
|
26
|
+
return assertEx(this.params.chainIterator, () => 'chainIterator not set')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static create(params: ChainHeadUpdateActorParams): Promisable<ChainHeadUpdateActor> {
|
|
30
|
+
return new ChainHeadUpdateActor(params)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override async start(): Promise<void> {
|
|
34
|
+
await super.start()
|
|
35
|
+
// Register event handler on all insert event to check for new head
|
|
36
|
+
this.chainFinalizedArchivist.on('inserted', async (data: { payloads: Payload[] }) => {
|
|
37
|
+
await this.checkInsertedForNewHead(data)
|
|
38
|
+
})
|
|
39
|
+
// Poll in case we missed an event
|
|
40
|
+
this.registerTimer('ChainHeadUpdateTimer', async () => await this.pollForNewHead(), 0, 250)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async checkInsertedForNewHead(data: { payloads: Payload[] }) {
|
|
44
|
+
const candidateBlock = sortBlocks(filterAs(data.payloads, asBlockBoundWitness)).at(-1)
|
|
45
|
+
await this.updateHeadIfNewer(candidateBlock)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async pollForNewHead() {
|
|
49
|
+
const candidateBlock = await findMostRecentBlock(this.chainFinalizedArchivist)
|
|
50
|
+
await this.updateHeadIfNewer(candidateBlock)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async updateHeadIfNewer(candidateBlock?: BlockBoundWitness) {
|
|
54
|
+
if (!candidateBlock) return
|
|
55
|
+
const currentHead = await this.chainIterator.head()
|
|
56
|
+
const candidateBlockNumber = candidateBlock.block
|
|
57
|
+
const candidateBlockNumberDisplay = `0x${toHex(candidateBlockNumber)}`
|
|
58
|
+
const currentBlockNumber = currentHead?.block ?? -1
|
|
59
|
+
if (candidateBlockNumber > currentBlockNumber) {
|
|
60
|
+
this.logger?.log('Found more recent head:', candidateBlockNumberDisplay)
|
|
61
|
+
await this.chainIterator.updateHead(candidateBlock)
|
|
62
|
+
this.logger?.log('Updated head:', candidateBlockNumberDisplay)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import type { BaseParams } from '@xylabs/base'
|
|
3
|
+
import { toHex } from '@xylabs/hex'
|
|
4
|
+
import type { Promisable } from '@xylabs/promise'
|
|
5
|
+
import { isDefined, isUndefined } from '@xylabs/typeof'
|
|
6
|
+
import { createDeclarationIntent } from '@xyo-network/chain-protocol'
|
|
7
|
+
import { BaseBlockProducerService } from '@xyo-network/chain-services'
|
|
8
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
9
|
+
import type { PayloadBundle } from '@xyo-network/payload-model'
|
|
10
|
+
import { PayloadBundleSchema } from '@xyo-network/payload-model'
|
|
11
|
+
import type { ChainStakeIntent, HydratedBlock } from '@xyo-network/xl1-protocol'
|
|
12
|
+
import type { ChainServiceCollectionV2, Config } from '@xyo-network/xl1-protocol-sdk'
|
|
13
|
+
import {
|
|
14
|
+
buildTransaction, flattenHydratedBlock, flattenHydratedTransaction,
|
|
15
|
+
} from '@xyo-network/xl1-protocol-sdk'
|
|
16
|
+
|
|
17
|
+
import { Actor } from '../model/index.ts'
|
|
18
|
+
|
|
19
|
+
export type ProducerActorParams = BaseParams<
|
|
20
|
+
Pick<ChainServiceCollectionV2,
|
|
21
|
+
'account'
|
|
22
|
+
| 'balanceService'
|
|
23
|
+
| 'chainIterator'
|
|
24
|
+
| 'chainStakeViewer'
|
|
25
|
+
| 'chainSubmissionsArchivistWrite'
|
|
26
|
+
| 'pendingBundledTransactionsArchivistWrite'
|
|
27
|
+
| 'producer'
|
|
28
|
+
| 'stakeIntentService'
|
|
29
|
+
> & {
|
|
30
|
+
config: Config
|
|
31
|
+
}>
|
|
32
|
+
|
|
33
|
+
const SHOULD_REGISTER_REDECLARATION_INTENT_TIMER = true
|
|
34
|
+
const TEN_MINUTES = 10 * 60 * 1000 // 10 minutes in milliseconds
|
|
35
|
+
|
|
36
|
+
export class ProducerActor extends Actor<ProducerActorParams> {
|
|
37
|
+
protected _lastProducedBlock: HydratedBlock | undefined
|
|
38
|
+
protected _lastRedeclarationIntent: ChainStakeIntent | undefined
|
|
39
|
+
|
|
40
|
+
protected constructor(params: ProducerActorParams) {
|
|
41
|
+
super(params.producer.address, 'Producer', params)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected get account() {
|
|
45
|
+
return assertEx(this.params.account, () => 'account not set')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected get balanceService() {
|
|
49
|
+
return assertEx(this.params.balanceService, () => 'balanceService not set')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected get chainIterator() {
|
|
53
|
+
return assertEx(this.params.chainIterator, () => 'chainIterator not set')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected get chainStakeViewer() {
|
|
57
|
+
return assertEx(this.params.chainStakeViewer, () => 'chainStakeViewer not set')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected get chainSubmissionsArchivistWrite() {
|
|
61
|
+
return assertEx(this.params.chainSubmissionsArchivistWrite, () => 'chainSubmissionsArchivistWrite not set')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected get pendingBundledTransactionsArchivistWrite() {
|
|
65
|
+
return assertEx(this.params.pendingBundledTransactionsArchivistWrite, () => 'pendingBundledTransactionsArchivistWrite not set')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected get producer() {
|
|
69
|
+
return assertEx(this.params.producer, () => 'producer not set')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected get stakeIntentService() {
|
|
73
|
+
return assertEx(this.params.stakeIntentService, () => 'stakeIntentService not set')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static create(params: ProducerActorParams): Promisable<ProducerActor> {
|
|
77
|
+
return new ProducerActor(params)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override async start(): Promise<void> {
|
|
81
|
+
await super.start()
|
|
82
|
+
// Register a timer to check if we should produce a block
|
|
83
|
+
this.registerTimer('BlockProductionTimer', async () => {
|
|
84
|
+
await this.spanAsync('produceBlock', async () => {
|
|
85
|
+
// Get the updated head
|
|
86
|
+
const head = await this.chainIterator.head()
|
|
87
|
+
// Check if we've already produced the next block for this head
|
|
88
|
+
const headHash = await PayloadBuilder.hash(head)
|
|
89
|
+
// If our last produced block was the next block for the current head, we do not
|
|
90
|
+
// need to produce another. This prevents duplicate blocks from being produced
|
|
91
|
+
if (this._lastProducedBlock && this._lastProducedBlock[0].previous === headHash) {
|
|
92
|
+
this.logger?.log('Block already produced:', `0x${toHex(this._lastProducedBlock[0].block)}`)
|
|
93
|
+
} else {
|
|
94
|
+
// Produce the next block
|
|
95
|
+
const nextBlock = await this.producer.next(head)
|
|
96
|
+
// If it was produced
|
|
97
|
+
if (nextBlock) {
|
|
98
|
+
const displayBlockNumber = `0x${toHex(nextBlock[0].block)}`
|
|
99
|
+
this.logger?.log('Produced block:', displayBlockNumber)
|
|
100
|
+
// Insert the block into the chain
|
|
101
|
+
await this.chainSubmissionsArchivistWrite.insert(flattenHydratedBlock(nextBlock))
|
|
102
|
+
this.logger?.log('Published block:', displayBlockNumber)
|
|
103
|
+
// Record that we have produced a block so we do not produce it again
|
|
104
|
+
this._lastProducedBlock = nextBlock
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}, 100, 500)
|
|
109
|
+
|
|
110
|
+
if (SHOULD_REGISTER_REDECLARATION_INTENT_TIMER) {
|
|
111
|
+
// Register a timer to check if we should redeclare the producer
|
|
112
|
+
this.registerTimer('ProducerRedeclarationTimer', async () => {
|
|
113
|
+
await this.spanAsync('producerRedeclarationTimer', async () => {
|
|
114
|
+
// Decide if we should redeclare intent
|
|
115
|
+
if (this.params.config.producer.disableIntentRedeclaration) return
|
|
116
|
+
|
|
117
|
+
// Get the current block
|
|
118
|
+
const head = await this.chainIterator.head()
|
|
119
|
+
if (isUndefined(head)) return
|
|
120
|
+
const currentBlock = head.block
|
|
121
|
+
|
|
122
|
+
// Calculate the time until the producer's declaration expires
|
|
123
|
+
const blocksUntilExpiration = await this.calculateBlocksUntilProducerDeclarationExpiration(currentBlock)
|
|
124
|
+
|
|
125
|
+
// Allow the producer time to redeclare itself via block production
|
|
126
|
+
// (for free) before submitting a redeclaration intent transaction.
|
|
127
|
+
if (blocksUntilExpiration > BaseBlockProducerService.RedeclarationWindow * 0.1) {
|
|
128
|
+
// Clear any previous redeclaration intent
|
|
129
|
+
this._lastRedeclarationIntent = undefined
|
|
130
|
+
// No need to redeclare yet
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If we already have a valid redeclaration intent, do not create another
|
|
135
|
+
// unless it has expired.
|
|
136
|
+
if (this._lastRedeclarationIntent) {
|
|
137
|
+
// Check if the last redeclaration intent is still valid
|
|
138
|
+
if (this._lastRedeclarationIntent.exp > currentBlock) return
|
|
139
|
+
// If it has expired, clear the last redeclaration intent
|
|
140
|
+
this._lastRedeclarationIntent = undefined
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if we have a valid balance before declaring intent
|
|
144
|
+
if (!await this.validateCurrentBalance()) {
|
|
145
|
+
this.logger?.error(
|
|
146
|
+
`Add balance to address ${this.account.address} for the producer to declare it's intent.`,
|
|
147
|
+
)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check if we have a valid stake before declaring intent
|
|
152
|
+
if (!(await this.validateCurrentStake())) {
|
|
153
|
+
this.logger?.error(
|
|
154
|
+
`Add stake to contract address ${this.params.config.chain.id}`
|
|
155
|
+
+ ' for the producer to declare it\'s intent.',
|
|
156
|
+
)
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Create a redeclaration intent
|
|
161
|
+
this.logger?.log('Creating redeclaration intent for producer:', this.account.address)
|
|
162
|
+
const redeclarationIntent = createDeclarationIntent(
|
|
163
|
+
this.account.address,
|
|
164
|
+
'producer',
|
|
165
|
+
currentBlock,
|
|
166
|
+
currentBlock + BaseBlockProducerService.RedeclarationDuration,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
// Submit the redeclaration intent
|
|
170
|
+
await this.submitRedeclarationIntent(currentBlock, redeclarationIntent)
|
|
171
|
+
|
|
172
|
+
// On successful submission, save the redeclaration intent
|
|
173
|
+
this._lastRedeclarationIntent = redeclarationIntent
|
|
174
|
+
})
|
|
175
|
+
}, 10_000, TEN_MINUTES)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
protected async calculateBlocksUntilProducerDeclarationExpiration(currentBlock: number): Promise<number> {
|
|
180
|
+
// Decide if we need to redeclare intent
|
|
181
|
+
const ranges = await this.stakeIntentService.getDeclaredCandidateRanges(this.account.address, 'producer')
|
|
182
|
+
// TODO: This doesn't handle the case where the producer had declared a range for the future
|
|
183
|
+
// but we're in a range that's not the future
|
|
184
|
+
// Sort in ascending order based on ending range to get range with highest ending block
|
|
185
|
+
const lastRange = ranges.toSorted((a, b) => a[1] > b[1] ? 1 : -1).at(-1)
|
|
186
|
+
|
|
187
|
+
// Use the most recent range's end block as the current declaration end OR
|
|
188
|
+
const [, currentDeclarationEnd] = lastRange
|
|
189
|
+
// If we have no ranges, we need to declare intent, so use the current block
|
|
190
|
+
|| [undefined, currentBlock]
|
|
191
|
+
|
|
192
|
+
// Calculate the time until the producer's declaration expires
|
|
193
|
+
const timeToProducerExpiration = currentDeclarationEnd - currentBlock
|
|
194
|
+
return timeToProducerExpiration
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
protected async submitRedeclarationIntent(currentBlock: number, redeclarationIntent: ChainStakeIntent): Promise<void> {
|
|
198
|
+
this.logger?.log('Submitting redeclaration intent for producer:', this.account.address)
|
|
199
|
+
// Create a transaction to submit the redeclaration intent
|
|
200
|
+
const tx = await buildTransaction(
|
|
201
|
+
this.chainIterator.chainId,
|
|
202
|
+
[redeclarationIntent],
|
|
203
|
+
[],
|
|
204
|
+
this.account,
|
|
205
|
+
currentBlock,
|
|
206
|
+
currentBlock + 1000,
|
|
207
|
+
)
|
|
208
|
+
const payloads = flattenHydratedTransaction(tx)
|
|
209
|
+
const root = tx[0]._hash
|
|
210
|
+
const payloadBundle = new PayloadBuilder<PayloadBundle>({ schema: PayloadBundleSchema }).fields({ payloads, root }).build()
|
|
211
|
+
|
|
212
|
+
// Submit the redeclaration intent
|
|
213
|
+
await this.pendingBundledTransactionsArchivistWrite.insert([payloadBundle])
|
|
214
|
+
|
|
215
|
+
this.logger?.log('Submitted redeclaration intent for producer:', this.account.address)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected async validateCurrentBalance(): Promise<boolean> {
|
|
219
|
+
// Check if we have a valid balance before declaring intent
|
|
220
|
+
const head = this._lastProducedBlock?.[0]._hash
|
|
221
|
+
if (isDefined(head)) {
|
|
222
|
+
const balances = await this.balanceService.balances(head, [this.account.address])
|
|
223
|
+
const currentBalance = balances[this.account.address] ?? 0n
|
|
224
|
+
if (currentBalance <= 0n) {
|
|
225
|
+
this.logger?.error(`Producer ${this.account.address} has no balance.`)
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
return true
|
|
229
|
+
}
|
|
230
|
+
return true
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
protected async validateCurrentStake(): Promise<boolean> {
|
|
234
|
+
// Use StakeIntentService to get the required minimum stake
|
|
235
|
+
const requiredMinimumStake = this.stakeIntentService.getRequiredMinimumStakeForIntent('producer')
|
|
236
|
+
// Check if we have a valid stake before declaring intent
|
|
237
|
+
const currentStake = await this.chainStakeViewer.activeByAddressStaked(this.account.address)
|
|
238
|
+
if (currentStake < requiredMinimumStake) {
|
|
239
|
+
this.logger?.error(`Producer ${this.account.address} has insufficient stake.`)
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
242
|
+
return true
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import type { BaseParams } from '@xylabs/base'
|
|
3
|
+
import type { Hash } from '@xylabs/hex'
|
|
4
|
+
import { toHex } from '@xylabs/hex'
|
|
5
|
+
import type { Promisable } from '@xylabs/promise'
|
|
6
|
+
import { isDefined, isUndefined } from '@xylabs/typeof'
|
|
7
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
8
|
+
import type { BlockBoundWitness } from '@xyo-network/xl1-protocol'
|
|
9
|
+
import type { ChainServiceCollectionV2, Config } from '@xyo-network/xl1-protocol-sdk'
|
|
10
|
+
|
|
11
|
+
import { Actor } from '../model/index.ts'
|
|
12
|
+
|
|
13
|
+
export type ValidatorActorParams = BaseParams<
|
|
14
|
+
Pick<ChainServiceCollectionV2,
|
|
15
|
+
'account'
|
|
16
|
+
| 'balanceService'
|
|
17
|
+
| 'chainIterator'
|
|
18
|
+
| 'chainStakeViewer'
|
|
19
|
+
| 'chainSubmissionsArchivistWrite'
|
|
20
|
+
| 'pendingBundledTransactionsArchivistWrite'
|
|
21
|
+
| 'stakeIntentService'
|
|
22
|
+
> & {
|
|
23
|
+
config: Config
|
|
24
|
+
}>
|
|
25
|
+
|
|
26
|
+
export class ValidatorActor extends Actor<ValidatorActorParams> {
|
|
27
|
+
protected _lastValidatedBlock: BlockBoundWitness | undefined
|
|
28
|
+
protected _lastValidatedBlockHash: Hash | undefined
|
|
29
|
+
|
|
30
|
+
protected constructor(params: ValidatorActorParams) {
|
|
31
|
+
super('Validator', 'Validator', params)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected get account() {
|
|
35
|
+
return assertEx(this.params.account, () => 'account not set')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected get balanceService() {
|
|
39
|
+
return assertEx(this.params.balanceService, () => 'balanceService not set')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected get chainIterator() {
|
|
43
|
+
return assertEx(this.params.chainIterator, () => 'chainIterator not set')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected get chainStakeViewer() {
|
|
47
|
+
return assertEx(this.params.chainStakeViewer, () => 'chainStakeViewer not set')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected get chainSubmissionsArchivistWrite() {
|
|
51
|
+
return assertEx(this.params.chainSubmissionsArchivistWrite, () => 'chainSubmissionsArchivistWrite not set')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected get pendingBundledTransactionsArchivistWrite() {
|
|
55
|
+
return assertEx(this.params.pendingBundledTransactionsArchivistWrite, () => 'pendingBundledTransactionsArchivistWrite not set')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected get stakeIntentService() {
|
|
59
|
+
return assertEx(this.params.stakeIntentService, () => 'stakeIntentService not set')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// protected get validator() {
|
|
63
|
+
// return assertEx(this.params.validator, () => 'validator not set')
|
|
64
|
+
// }
|
|
65
|
+
|
|
66
|
+
static create(params: ValidatorActorParams): Promisable<ValidatorActor> {
|
|
67
|
+
return new ValidatorActor(params)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override async start(): Promise<void> {
|
|
71
|
+
await super.start()
|
|
72
|
+
// Register a timer to check if we should produce a block
|
|
73
|
+
this.registerTimer('BlockProductionTimer', async () => {
|
|
74
|
+
await this.spanAsync('produceBlock', async () => {
|
|
75
|
+
// Get the updated head
|
|
76
|
+
const block = await this.chainIterator.head()
|
|
77
|
+
if (isUndefined(block)) return
|
|
78
|
+
// Check if we've already validated this head
|
|
79
|
+
const hash = await PayloadBuilder.hash(block)
|
|
80
|
+
// If our last validated block was this head, skip validation
|
|
81
|
+
if (isDefined(this._lastValidatedBlock) && this._lastValidatedBlockHash === hash) {
|
|
82
|
+
this.logger?.log('Block already validated:', `0x${toHex(block.block)}`)
|
|
83
|
+
} else {
|
|
84
|
+
this.logger?.log('Validating block:', `0x${toHex(block.block)}`)
|
|
85
|
+
const valid = await this.validateBlock(block)
|
|
86
|
+
if (valid) {
|
|
87
|
+
this.logger?.log('Validated block:', `0x${toHex(block.block)}`)
|
|
88
|
+
} else {
|
|
89
|
+
this.logger?.log('Invalid block:', `0x${toHex(block.block)}`)
|
|
90
|
+
await this.slashInvalidBlock(block)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}, 100, 500)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected async slashInvalidBlock(_block: BlockBoundWitness): Promise<boolean> {
|
|
98
|
+
// TODO: Handle slashing logic
|
|
99
|
+
const slashed = await Promise.resolve(true)
|
|
100
|
+
return slashed
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected async validateBlock(block: BlockBoundWitness): Promise<boolean> {
|
|
104
|
+
// TODO: Validate using validation logic
|
|
105
|
+
const valid = await Promise.resolve(true)
|
|
106
|
+
// Store the last validated block and its hash
|
|
107
|
+
this._lastValidatedBlock = block
|
|
108
|
+
this._lastValidatedBlockHash = await PayloadBuilder.hash(block)
|
|
109
|
+
|
|
110
|
+
// Return the validation result
|
|
111
|
+
return valid
|
|
112
|
+
}
|
|
113
|
+
}
|