mukan-ignite/ignite/pkg/chaincmd/runner/account.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

226 lines
5.7 KiB
Go

package chaincmdrunner
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/cmdrunner/step"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
)
var (
// ErrAccountAlreadyExists returned when an already exists account attempted to be imported.
ErrAccountAlreadyExists = errors.New("account already exists")
// ErrAccountDoesNotExist returned when account does not exit.
ErrAccountDoesNotExist = errors.New("account does not exit")
)
const msgEmptyKeyring = "No records were found in keyring"
// Account represents a user account.
type Account struct {
Name string `json:"name"`
Address string `json:"address"`
Mnemonic string `json:"mnemonic,omitempty"`
}
// AddAccount creates a new account or imports an account when mnemonic is provided.
// returns with an error if the operation went unsuccessful or an account with the provided name
// already exists.
func (r Runner) AddAccount(
ctx context.Context,
name,
mnemonic,
coinType,
accountNumber,
addressIndex string,
) (Account, error) {
if err := r.CheckAccountExist(ctx, name); err != nil {
return Account{}, err
}
b := newBuffer()
account := Account{
Name: name,
Mnemonic: mnemonic,
}
// import the account when mnemonic is provided, otherwise create a new one.
if mnemonic != "" {
input := newBuffer()
_, err := fmt.Fprintln(input, mnemonic)
if err != nil {
return Account{}, err
}
if r.chainCmd.KeyringPassword() != "" {
_, err = fmt.Fprintln(input, r.chainCmd.KeyringPassword())
if err != nil {
return Account{}, err
}
_, err = fmt.Fprintln(input, r.chainCmd.KeyringPassword())
if err != nil {
return Account{}, err
}
}
if err := r.run(
ctx,
runOptions{},
r.chainCmd.RecoverKeyCommand(name, coinType, accountNumber, addressIndex),
step.Write(input.Bytes()),
); err != nil {
return Account{}, err
}
} else {
if err := r.run(ctx, runOptions{
stdout: b,
stderr: b,
stdin: os.Stdin,
}, r.chainCmd.AddKeyCommand(name, coinType, accountNumber, addressIndex)); err != nil {
return Account{}, err
}
data, err := b.JSONEnsuredBytes()
if err != nil {
return Account{}, err
}
if err := json.Unmarshal(data, &account); err != nil {
return Account{}, err
}
}
// get the address of the account.
retrieved, err := r.ShowAccount(ctx, name)
if err != nil {
return Account{}, err
}
account.Address = retrieved.Address
return account, nil
}
// ImportAccount import an account from a key file.
func (r Runner) ImportAccount(ctx context.Context, name, keyFile, passphrase string) (Account, error) {
if err := r.CheckAccountExist(ctx, name); err != nil {
return Account{}, err
}
// write the passphrase as input
// TODO: manage keyring backend other than test
input := newBuffer()
_, err := fmt.Fprintln(input, passphrase)
if err != nil {
return Account{}, err
}
if err := r.run(
ctx,
runOptions{},
r.chainCmd.ImportKeyCommand(name, keyFile),
step.Write(input.Bytes()),
); err != nil {
return Account{}, err
}
return r.ShowAccount(ctx, name)
}
// ListAccounts returns the list of accounts in the keyring.
func (r Runner) ListAccounts(ctx context.Context) ([]Account, error) {
// Get a JSON string with all accounts in the keyring
b := newBuffer()
if err := r.run(ctx, runOptions{stdout: b}, r.chainCmd.ListKeysCommand()); err != nil {
return nil, err
}
// Make sure that the command output is not the empty keyring message.
// This need to be checked because when the keyring is empty the command
// finishes with exit code 0 and a plain text message.
// This behavior was added to Cosmos SDK v0.46.2. See the link
// https://github.com/cosmos/cosmos-sdk/blob/d01aa5b4a8/client/keys/list.go#L37
if strings.TrimSpace(b.String()) == msgEmptyKeyring {
return nil, nil
}
data, err := b.JSONEnsuredBytes()
if err != nil {
return nil, err
}
var accounts []Account
if err := json.Unmarshal(data, &accounts); err != nil {
return nil, err
}
return accounts, nil
}
// CheckAccountExist returns an error if the account already exists in the chain keyring.
func (r Runner) CheckAccountExist(ctx context.Context, name string) error {
accounts, err := r.ListAccounts(ctx)
if err != nil {
return err
}
// Search for the account name
for _, account := range accounts {
if account.Name == name {
return ErrAccountAlreadyExists
}
}
return nil
}
// ShowAccount shows details of an account.
func (r Runner) ShowAccount(ctx context.Context, name string) (Account, error) {
b := newBuffer()
opt := []step.Option{
r.chainCmd.ShowKeyAddressCommand(name),
}
if r.chainCmd.KeyringPassword() != "" {
input := newBuffer()
_, err := fmt.Fprintln(input, r.chainCmd.KeyringPassword())
if err != nil {
return Account{}, err
}
opt = append(opt, step.Write(input.Bytes()))
}
if err := r.run(ctx, runOptions{stdout: b}, opt...); err != nil {
if strings.Contains(err.Error(), "item could not be found") ||
strings.Contains(err.Error(), "not a valid name or address") {
return Account{}, ErrAccountDoesNotExist
}
return Account{}, err
}
return Account{
Name: name,
Address: strings.TrimSpace(b.String()),
}, nil
}
// AddGenesisAccount adds account to genesis by its address.
func (r Runner) AddGenesisAccount(ctx context.Context, address, coins string) error {
return r.run(ctx, runOptions{}, r.chainCmd.AddGenesisAccountCommand(address, coins))
}
// AddVestingAccount adds vesting account to genesis by its address.
func (r Runner) AddVestingAccount(
ctx context.Context,
address,
originalCoins,
vestingCoins string,
vestingEndTime int64,
) error {
return r.run(ctx, runOptions{}, r.chainCmd.AddVestingAccountCommand(address, originalCoins, vestingCoins, vestingEndTime))
}