Skip to content

Commit

Permalink
Merge pull request #235 from InjectiveLabs/f/new-cosmos-keyring
Browse files Browse the repository at this point in the history
feat: new cosmos keyring helper for injective client
  • Loading branch information
aarmoa committed Sep 18, 2024
2 parents 5f52a16 + 9dc0b69 commit 2c8f9a0
Show file tree
Hide file tree
Showing 15 changed files with 1,319 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ jobs:
with:
go-version-file: "go.mod"
check-latest: true
- name: Install pass helper
run: sudo apt-get update && sudo apt-get install -y pass
- name: Generate GPG key
run: "
echo \"%no-protection\nKey-Type: 1\nKey-Length: 4096\nSubkey-Type: 1\nSubkey-Length: 4096\nName-Comment: keyring_test\nExpire-Date: 0\" > genkey && gpg --gen-key --batch genkey"
- name: Setup OS keystore
run: pass init keyring_test
- name: Run test and calculate coverage
run: make coverage
- name: Upload coverage to Codecov
Expand Down
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ exclude: |
(?x)^(
chain/.*|
exchange/.*|
proto/.*
proto/.*|
client/keyring/testdata/.*
)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
101 changes: 101 additions & 0 deletions client/keyring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Injective Chain Keyring Helper

Creates a new keyring from a variety of options. See `ConfigOpt` and related options. This keyring helper allows initializing the Cosmos SDK keyring used for signing transactions.

It allows flexibly defining a static configuration of keys, supports multiple pre-defined keys in the same keyring, and allows loading keys from a file, deriving from mnemonic, or reading plain private key bytes from a HEX string. Extremely useful for testing and local development, but also robust for production use cases.

## Usage

```go
NewCosmosKeyring(cdc codec.Codec, opts ...ConfigOpt) (sdk.AccAddress, cosmkeyring.Keyring, error)
```

**ConfigOpts:**

These options are global on the keyring level.

* `WithKeyringDir` option sets keyring path in the filesystem, useful when keyring backend is `file`.
* `WithKeyringAppName` option sets keyring application name (defaults to `injectived`)
* `WithKeyringBackend` sets the keyring backend. Expected values: `test`, `file`, `os`.
* `WithUseLedger` sets the option to use hardware wallet, if available on the system.

These options allow adding keys to the keyring during initialization.

* `WithKey` adds a single key to the keyring, without having alias name.
* `WithNamedKey` addes a single key to the keyring, with a name.
* `WithDefaultKey` sets a default key reference to use for signing (by name).

**KeyConfigOpts:**

These options are set per key.

* `WithKeyFrom` sets the key name to use for signing. Must exist in the provided keyring.
* `WithKeyPassphrase` sets the passphrase for keyring files. The package will fallback to `os.Stdin` if this option was not provided, but passphrase is required.
* `WithPrivKeyHex` allows specifying a private key as plain-text hex. Insecure option, use for testing only. The package will create a virtual keyring holding that key, to meet all the interfaces.
* `WithMnemonic` allows specifying a mnemonic phrase as plain-text hex. Insecure option, use for testing only. The package will create a virtual keyring to derive the keys and meet all the interfaces.

## Examples

Initialize an in-memory keyring with a private key hex:

```go
NewCosmosKeyring(
cdc,
WithKey(
WithPrivKeyHex("e6888cb164d52e4880e08a8a5dbe69cd62f67fde3d5906f2c5c951be553b2267"),
WithKeyFrom("sender"),
),
)
```

Initialize an in-memory keyring with a mnemonic phrase:

```go
NewCosmosKeyring(
s.cdc,
WithKey(
WithMnemonic("real simple naive ....... love"),
WithKeyFrom("sender"),
),
)
```

Real world use case of keyring initialization from CLI flags, with a single named key set as default:

```go
NewCosmosKeyring(
cdc,
WithKeyringDir(*keyringDir),
WithKeyringAppName(*keyringAppName),
WithKeyringBackend(Backend(*keyringBackend)),
WithNamedKey(
"dispatcher",
WithKeyFrom(*dispatcherKeyFrom),
WithKeyPassphrase(*dispatcherKeyPassphrase),
WithPrivKeyHex(*dispatcherKeyPrivateHex),
WithMnemonic(*dispatcherKeyMnemonic),
),
WithDefaultKey(
"dispatcher",
),
)
```

## Testing

```bash
go test -v -cover

PASS
coverage: 83.1% of statements
```

## Generating a Test Fixture

```bash
> cd testdata

> injectived keys --keyring-dir `pwd` --keyring-backend file add test
```

Passphrase should be `test12345678` for this fixture to work.
20 changes: 20 additions & 0 deletions client/keyring/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package keyring

import "github.com/pkg/errors"

var (
ErrCosmosKeyringCreationFailed = errors.New("cosmos keyring creation failed")
ErrCosmosKeyringImportFailed = errors.New("cosmos keyring unable to import key")
ErrDeriveFailed = errors.New("key derivation failed")
ErrFailedToApplyConfigOption = errors.New("failed to apply config option")
ErrFailedToApplyKeyConfigOption = errors.New("failed to apply a key config option")
ErrFilepathIncorrect = errors.New("incorrect filepath")
ErrHexFormatError = errors.New("hex format error")
ErrIncompatibleOptionsProvided = errors.New("incompatible keyring options provided")
ErrInsufficientKeyDetails = errors.New("insufficient cosmos key details provided")
ErrKeyIncompatible = errors.New("provided key is incompatible with requested config")
ErrKeyRecordNotFound = errors.New("key record not found")
ErrPrivkeyConflict = errors.New("privkey conflict")
ErrUnexpectedAddress = errors.New("unexpected address")
ErrMultipleKeysWithDifferentSecurity = errors.New("key security is different: cannot mix keyring with privkeys")
)
69 changes: 69 additions & 0 deletions client/keyring/key_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package keyring

import (
bip39 "github.com/cosmos/go-bip39"
"github.com/pkg/errors"
)

type cosmosKeyConfig struct {
Name string
KeyFrom string
KeyPassphrase string
PrivKeyHex string
Mnemonic string
}

// KeyConfigOpt defines a known cosmos keyring key option.
type KeyConfigOpt func(c *cosmosKeyConfig) error

// WithKeyFrom sets the key name to use for signing. Must exist in the provided keyring.
func WithKeyFrom(v string) KeyConfigOpt {
return func(c *cosmosKeyConfig) error {
if v != "" {
c.KeyFrom = v
}

return nil
}
}

// WithKeyPassphrase sets the passphrase for keyring files. Insecure option, use for testing only.
// The package will fallback to os.Stdin if this option was not provided, but pass is required.
func WithKeyPassphrase(v string) KeyConfigOpt {
return func(c *cosmosKeyConfig) error {
if v != "" {
c.KeyPassphrase = v
}

return nil
}
}

// WithPrivKeyHex allows to specify a private key as plaintext hex. Insecure option, use for testing only.
// The package will create a virtual keyring holding that key, to meet all the interfaces.
func WithPrivKeyHex(v string) KeyConfigOpt {
return func(c *cosmosKeyConfig) error {
if v != "" {
c.PrivKeyHex = v
}

return nil
}
}

// WithMnemonic allows to specify a mnemonic pharse as plaintext. Insecure option, use for testing only.
// The package will create a virtual keyring to derive the keys and meet all the interfaces.
func WithMnemonic(v string) KeyConfigOpt {
return func(c *cosmosKeyConfig) error {
if v != "" {
if !bip39.IsMnemonicValid(v) {
err := errors.New("provided mnemonic is not a valid BIP39 mnemonic")
return err
}

c.Mnemonic = v
}

return nil
}
}
Loading

0 comments on commit 2c8f9a0

Please sign in to comment.