mukan-ignite/ignite/pkg/chaincmd/chaincmd.go
Mukan Erkin Törük c32551b6f7
Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:24 +03:00

670 lines
19 KiB
Go

package chaincmd
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/cmdrunner/step"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/cosmosver"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
)
const (
commandStart = "start"
commandInit = "init"
commandKeys = "keys"
commandGenesis = "genesis"
commandAddGenesisAccount = "add-genesis-account"
commandGentx = "gentx"
commandCollectGentxs = "collect-gentxs"
commandValidateGenesis = "validate"
commandExportGenssis = "export"
commandShowNodeID = "show-node-id"
commandStatus = "status"
commandTx = "tx"
commandQuery = "query"
commandUnsafeReset = "unsafe-reset-all"
commandTendermint = "tendermint"
commandTestnetInPlace = "in-place-testnet"
commandTestnetMultiNode = "multi-node"
optionHome = "--home"
optionNode = "--node"
optionKeyringBackend = "--keyring-backend"
optionChainID = "--chain-id"
optionOutput = "--output"
optionRecover = "--recover"
optionAddress = "--address"
optionAmount = "--amount"
optionFees = "--fees"
optionValidatorMoniker = "--moniker"
optionValidatorCommissionRate = "--commission-rate"
optionValidatorCommissionMaxRate = "--commission-max-rate"
optionValidatorCommissionMaxChangeRate = "--commission-max-change-rate"
optionValidatorMinSelfDelegation = "--min-self-delegation"
optionValidatorGasPrices = "--gas-prices"
optionValidatorDetails = "--details"
optionValidatorIdentity = "--identity"
optionValidatorWebsite = "--website"
optionValidatorSecurityContact = "--security-contact"
optionYes = "--yes"
optionHomeClient = "--home-client"
optionCoinType = "--coin-type"
optionVestingAmount = "--vesting-amount"
optionVestingEndTime = "--vesting-end-time"
optionBroadcastMode = "--broadcast-mode"
optionAccount = "--account"
optionIndex = "--index"
optionValidatorPrivateKey = "--validator-privkey"
optionAccountToFund = "--accounts-to-fund"
optionSkipConfirmation = "--skip-confirmation"
optionAmountStakes = "--validators-stake-amount"
optionOutPutDir = "--output-dir"
optionNumValidator = "--v"
optionNodeDirPrefix = "--node-dir-prefix"
optionPorts = "--list-ports"
constTendermint = "tendermint"
constJSON = "json"
)
type KeyringBackend string
const (
KeyringBackendUnspecified KeyringBackend = ""
KeyringBackendOS KeyringBackend = "os"
KeyringBackendFile KeyringBackend = "file"
KeyringBackendPass KeyringBackend = "pass"
KeyringBackendTest KeyringBackend = "test"
KeyringBackendKwallet KeyringBackend = "kwallet"
)
type ChainCmd struct {
appCmd string
chainID string
homeDir string
keyringBackend KeyringBackend
keyringPassword string
nodeAddress string
isAutoChainIDDetectionEnabled bool
sdkVersion cosmosver.Version
}
// New creates a new ChainCmd to launch command with the chain app.
func New(appCmd string, options ...Option) ChainCmd {
chainCmd := ChainCmd{
appCmd: appCmd,
sdkVersion: cosmosver.Latest,
}
applyOptions(&chainCmd, options)
return chainCmd
}
// Copy makes a copy of ChainCmd by overwriting its options with given options.
func (c ChainCmd) Copy(options ...Option) ChainCmd {
applyOptions(&c, options)
return c
}
// Option configures ChainCmd.
type Option func(*ChainCmd)
func applyOptions(c *ChainCmd, options []Option) {
for _, apply := range options {
apply(c)
}
}
// WithVersion sets the version of the blockchain.
// when this is not provided, the latest version of SDK is assumed.
func WithVersion(v cosmosver.Version) Option {
return func(c *ChainCmd) {
c.sdkVersion = v
}
}
// WithHome replaces the default home used by the chain.
func WithHome(home string) Option {
return func(c *ChainCmd) {
c.homeDir = home
}
}
// WithChainID provides a specific chain ID for the commands that accept this option.
func WithChainID(chainID string) Option {
return func(c *ChainCmd) {
c.chainID = chainID
}
}
// WithAutoChainIDDetection finds out the chain id by communicating with the node running.
func WithAutoChainIDDetection() Option {
return func(c *ChainCmd) {
c.isAutoChainIDDetectionEnabled = true
}
}
// WithKeyringBackend provides a specific keyring backend for the commands that accept this option.
func WithKeyringBackend(keyringBackend KeyringBackend) Option {
return func(c *ChainCmd) {
c.keyringBackend = keyringBackend
}
}
// WithKeyringPassword provides a password to unlock keyring.
func WithKeyringPassword(password string) Option {
return func(c *ChainCmd) {
c.keyringPassword = password
}
}
// WithNodeAddress sets the node address for the commands that needs to make an
// API request to the node that has a different node address other than the default one.
func WithNodeAddress(addr string) Option {
return func(c *ChainCmd) {
c.nodeAddress = addr
}
}
// Name returns the app name (prefix of the chain daemon).
func (c ChainCmd) Name() string {
return c.appCmd
}
// StartCommand returns the command to start the daemon of the chain.
func (c ChainCmd) StartCommand(options ...string) step.Option {
command := append([]string{
commandStart,
}, options...)
return c.daemonCommand(command)
}
// InitCommand returns the command to initialize the chain.
func (c ChainCmd) InitCommand(moniker string, options ...string) step.Option {
command := append([]string{
commandInit,
moniker,
}, options...)
command = c.attachChainID(command)
return c.daemonCommand(command)
}
// AddKeyCommand returns the command to add a new key in the chain keyring.
func (c ChainCmd) AddKeyCommand(accountName, coinType, accountNumber, addressIndex string) step.Option {
command := []string{
commandKeys,
"add",
accountName,
optionOutput,
constJSON,
}
if coinType != "" {
command = append(command, optionCoinType, coinType)
}
if accountNumber != "" {
command = append(command, optionAccount, accountNumber)
}
if addressIndex != "" {
command = append(command, optionIndex, addressIndex)
}
command = c.attachKeyringBackend(command)
return c.cliCommand(command)
}
// RecoverKeyCommand returns the command to recover a key into the chain keyring from a mnemonic.
func (c ChainCmd) RecoverKeyCommand(accountName, coinType, accountNumber, addressIndex string) step.Option {
command := []string{
commandKeys,
"add",
accountName,
optionRecover,
}
if coinType != "" {
command = append(command, optionCoinType, coinType)
}
if accountNumber != "" {
command = append(command, optionAccount, accountNumber)
}
if addressIndex != "" {
command = append(command, optionIndex, addressIndex)
}
command = c.attachKeyringBackend(command)
return c.cliCommand(command)
}
// ImportKeyCommand returns the command to import a key into the chain keyring from a key file.
func (c ChainCmd) ImportKeyCommand(accountName, keyFile string) step.Option {
command := []string{
commandKeys,
"import",
accountName,
keyFile,
}
command = c.attachKeyringBackend(command)
return c.cliCommand(command)
}
// ShowKeyAddressCommand returns the command to print the address of a key in the chain keyring.
func (c ChainCmd) ShowKeyAddressCommand(accountName string) step.Option {
command := []string{
commandKeys,
"show",
accountName,
optionAddress,
}
command = c.attachKeyringBackend(command)
return c.cliCommand(command)
}
// ListKeysCommand returns the command to print the list of a keys in the chain keyring.
func (c ChainCmd) ListKeysCommand() step.Option {
command := []string{
commandKeys,
"list",
optionOutput,
constJSON,
}
command = c.attachKeyringBackend(command)
return c.cliCommand(command)
}
// AddGenesisAccountCommand returns the command to add a new account in the genesis file of the chain.
func (c ChainCmd) AddGenesisAccountCommand(address, coins string) step.Option {
command := []string{
commandGenesis,
commandAddGenesisAccount,
address,
coins,
}
return c.daemonCommand(command)
}
// AddVestingAccountCommand returns the command to add a delayed vesting account in the genesis file of the chain.
func (c ChainCmd) AddVestingAccountCommand(address, originalCoins, vestingCoins string, vestingEndTime int64) step.Option {
command := []string{
commandGenesis,
commandAddGenesisAccount,
address,
originalCoins,
optionVestingAmount,
vestingCoins,
optionVestingEndTime,
fmt.Sprintf("%d", vestingEndTime),
}
return c.daemonCommand(command)
}
// GentxOption for the GentxCommand.
type GentxOption func([]string) []string
// GentxWithMoniker provides moniker option for the gentx command.
func GentxWithMoniker(moniker string) GentxOption {
return func(command []string) []string {
if len(moniker) > 0 {
return append(command, optionValidatorMoniker, moniker)
}
return command
}
}
// GentxWithCommissionRate provides commission rate option for the gentx command.
func GentxWithCommissionRate(commissionRate string) GentxOption {
return func(command []string) []string {
if len(commissionRate) > 0 {
return append(command, optionValidatorCommissionRate, commissionRate)
}
return command
}
}
// GentxWithCommissionMaxRate provides commission max rate option for the gentx command.
func GentxWithCommissionMaxRate(commissionMaxRate string) GentxOption {
return func(command []string) []string {
if len(commissionMaxRate) > 0 {
return append(command, optionValidatorCommissionMaxRate, commissionMaxRate)
}
return command
}
}
// GentxWithCommissionMaxChangeRate provides commission max change rate option for the gentx command.
func GentxWithCommissionMaxChangeRate(commissionMaxChangeRate string) GentxOption {
return func(command []string) []string {
if len(commissionMaxChangeRate) > 0 {
return append(command, optionValidatorCommissionMaxChangeRate, commissionMaxChangeRate)
}
return command
}
}
// GentxWithMinSelfDelegation provides minimum self delegation option for the gentx command.
func GentxWithMinSelfDelegation(minSelfDelegation string) GentxOption {
return func(command []string) []string {
if len(minSelfDelegation) > 0 {
return append(command, optionValidatorMinSelfDelegation, minSelfDelegation)
}
return command
}
}
// GentxWithGasPrices provides gas price option for the gentx command.
func GentxWithGasPrices(gasPrices string) GentxOption {
return func(command []string) []string {
if len(gasPrices) > 0 {
return append(command, optionValidatorGasPrices, gasPrices)
}
return command
}
}
// GentxWithDetails provides validator details option for the gentx command.
func GentxWithDetails(details string) GentxOption {
return func(command []string) []string {
if len(details) > 0 {
return append(command, optionValidatorDetails, details)
}
return command
}
}
// GentxWithIdentity provides validator identity option for the gentx command.
func GentxWithIdentity(identity string) GentxOption {
return func(command []string) []string {
if len(identity) > 0 {
return append(command, optionValidatorIdentity, identity)
}
return command
}
}
// GentxWithWebsite provides validator website option for the gentx command.
func GentxWithWebsite(website string) GentxOption {
return func(command []string) []string {
if len(website) > 0 {
return append(command, optionValidatorWebsite, website)
}
return command
}
}
// GentxWithSecurityContact provides validator security contact option for the gentx command.
func GentxWithSecurityContact(securityContact string) GentxOption {
return func(command []string) []string {
if len(securityContact) > 0 {
return append(command, optionValidatorSecurityContact, securityContact)
}
return command
}
}
func (c ChainCmd) IsAutoChainIDDetectionEnabled() bool {
return c.isAutoChainIDDetectionEnabled
}
func (c ChainCmd) SDKVersion() cosmosver.Version {
return c.sdkVersion
}
// GentxCommand returns the command to generate a gentx for the chain.
func (c ChainCmd) GentxCommand(
validatorName string,
selfDelegation string,
options ...GentxOption,
) step.Option {
command := []string{
commandGenesis,
commandGentx,
}
switch {
case c.sdkVersion.LT(cosmosver.StargateFortyVersion):
command = append(command,
validatorName,
optionAmount,
selfDelegation,
)
case c.sdkVersion.GTE(cosmosver.StargateFortyVersion):
command = append(command,
validatorName,
selfDelegation,
)
}
// Apply the options provided by the user
for _, apply := range options {
command = apply(command)
}
command = c.attachChainID(command)
command = c.attachKeyringBackend(command)
return c.daemonCommand(command)
}
// CollectGentxsCommand returns the command to gather the gentxs in /gentx dir into the genesis file of the chain.
func (c ChainCmd) CollectGentxsCommand() step.Option {
command := []string{
commandGenesis,
commandCollectGentxs,
}
return c.daemonCommand(command)
}
// ValidateGenesisCommand returns the command to check the validity of the chain genesis.
func (c ChainCmd) ValidateGenesisCommand() step.Option {
command := []string{
commandGenesis,
commandValidateGenesis,
}
return c.daemonCommand(command)
}
// ShowNodeIDCommand returns the command to print the node ID of the node for the chain.
func (c ChainCmd) ShowNodeIDCommand() step.Option {
command := []string{
constTendermint,
commandShowNodeID,
}
return c.daemonCommand(command)
}
// UnsafeResetCommand returns the command to reset the blockchain database.
func (c ChainCmd) UnsafeResetCommand() step.Option {
var command []string
if c.sdkVersion.GTE(cosmosver.StargateFortyFiveThreeVersion) {
command = append(command, commandTendermint)
}
command = append(command, commandUnsafeReset)
return c.daemonCommand(command)
}
// ExportCommand returns the command to export the state of the blockchain into a genesis file.
func (c ChainCmd) ExportCommand() step.Option {
command := []string{
commandExportGenssis,
}
return c.daemonCommand(command)
}
// BankSendOption for the BankSendCommand.
type BankSendOption func([]string) []string
// BankSendWithFees sets fees to pay along with transaction for the bank send command.
func BankSendWithFees(fee sdk.Coin) BankSendOption {
return func(command []string) []string {
if !fee.IsNil() {
return append(command, optionFees, fee.String())
}
return command
}
}
// BankSendCommand returns the command for transferring tokens.
func (c ChainCmd) BankSendCommand(fromAddress, toAddress, amount string, options ...BankSendOption) step.Option {
command := []string{
commandTx,
}
command = append(command, "bank")
command = append(command,
"send",
fromAddress,
toAddress,
amount,
)
command = append(command,
optionBroadcastMode, flags.BroadcastSync,
optionYes,
)
// Apply the options provided by the user
for _, apply := range options {
command = apply(command)
}
command = c.attachChainID(command)
command = c.attachKeyringBackend(command)
command = c.attachNode(command)
return c.cliCommand(command)
}
// QueryTxCommand returns the command to query tx.
func (c ChainCmd) QueryTxCommand(txHash string) step.Option {
command := []string{
commandQuery,
"tx",
txHash,
}
command = c.attachNode(command)
return c.cliCommand(command)
}
// QueryTxEventsCommand returns the command to query events.
func (c ChainCmd) QueryTxEventsCommand(query string) step.Option {
command := []string{
commandQuery,
"txs",
"--query",
query,
"--page", "1",
"--limit", "1000",
"--output", "json",
}
command = c.attachNode(command)
return c.cliCommand(command)
}
// QueryTxQueryCommand returns the command to query tx.
func (c ChainCmd) QueryTxQueryCommand(query string) step.Option {
command := []string{
commandQuery,
"txs",
"--query",
query,
"--page", "1",
"--limit", "1000",
"--output", "json",
}
command = c.attachNode(command)
return c.cliCommand(command)
}
// StatusCommand returns the command that fetches node's status.
func (c ChainCmd) StatusCommand() step.Option {
command := []string{
commandStatus,
}
command = c.attachNode(command)
return c.cliCommand(command)
}
// KeyringBackend returns the underlying keyring backend.
func (c ChainCmd) KeyringBackend() KeyringBackend {
return c.keyringBackend
}
// KeyringPassword returns the underlying keyring password.
func (c ChainCmd) KeyringPassword() string {
return c.keyringPassword
}
// attachChainID appends the chain ID flag to the provided command.
func (c ChainCmd) attachChainID(command []string) []string {
if c.chainID != "" {
command = append(command, []string{optionChainID, c.chainID}...)
}
return command
}
// attachKeyringBackend appends the keyring backend flag to the provided command.
func (c ChainCmd) attachKeyringBackend(command []string) []string {
if c.keyringBackend != "" {
command = append(command, []string{optionKeyringBackend, string(c.keyringBackend)}...)
}
return command
}
// attachHome appends the home flag to the provided command.
func (c ChainCmd) attachHome(command []string) []string {
if c.homeDir != "" {
command = append(command, []string{optionHome, c.homeDir}...)
}
return command
}
// attachNode appends the node flag to the provided command.
func (c ChainCmd) attachNode(command []string) []string {
if c.nodeAddress != "" {
command = append(command, []string{optionNode, c.nodeAddress}...)
}
return command
}
// daemonCommand returns the daemon command from the provided command.
func (c ChainCmd) daemonCommand(command []string) step.Option {
return step.Exec(c.appCmd, c.attachHome(command)...)
}
// cliCommand returns the cli command from the provided command.
func (c ChainCmd) cliCommand(command []string) step.Option {
return step.Exec(c.appCmd, c.attachHome(command)...)
}
// KeyringBackendFromString returns the keyring backend from its string.
func KeyringBackendFromString(kb string) (KeyringBackend, error) {
existingKeyringBackend := map[KeyringBackend]bool{
KeyringBackendUnspecified: true,
KeyringBackendOS: true,
KeyringBackendFile: true,
KeyringBackendPass: true,
KeyringBackendTest: true,
KeyringBackendKwallet: true,
}
if _, ok := existingKeyringBackend[KeyringBackend(kb)]; ok {
return KeyringBackend(kb), nil
}
return KeyringBackendUnspecified, errors.Errorf("unrecognized keyring backend: %s", kb)
}