forked from cosmos/relayer
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Keystore implementation for substrate chain (cosmos#922)
* add substrate keystore implementation * implement key provider for substrate chain * clean up keystore code * run go mod tidy moving rename test to _test * remove verbs from error messages * correct interface method comments in Keyring and Info types * initialize log in NewProvider method * move type definitions to method implementation files * check if KeyDirectory is empty before overwriting it in the NewProvider method * remove comments in ExportPrivKeyArmor * bump substrate-rpc-client to latest master * change network type to uint16 * update go.mod in ibctest
- Loading branch information
1 parent
1bbccc9
commit 8c6d418
Showing
8 changed files
with
1,123 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package substrate | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/cosmos/go-bip39" | ||
"github.com/cosmos/relayer/v2/relayer/chains/substrate/keystore" | ||
"github.com/cosmos/relayer/v2/relayer/provider" | ||
) | ||
|
||
func (sp *SubstrateProvider) CreateKeystore(path string) error { | ||
keybase, err := keystore.New(sp.PCfg.ChainID, sp.PCfg.KeyringBackend, sp.PCfg.KeyDirectory, sp.Input) | ||
if err != nil { | ||
return err | ||
} | ||
sp.Keybase = keybase | ||
return nil | ||
} | ||
|
||
func (sp *SubstrateProvider) KeystoreCreated(path string) bool { | ||
if _, err := os.Stat(path); err != nil || sp.Keybase == nil { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func (sp *SubstrateProvider) AddKey(name string, coinType uint32) (output *provider.KeyOutput, err error) { | ||
ko, err := sp.KeyAddOrRestore(name, coinType) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return ko, nil | ||
} | ||
|
||
func (sp *SubstrateProvider) RestoreKey(name, mnemonic string, coinType uint32) (address string, err error) { | ||
ko, err := sp.KeyAddOrRestore(name, coinType, mnemonic) | ||
if err != nil { | ||
return "", err | ||
} | ||
return ko.Address, nil | ||
} | ||
|
||
func (sp *SubstrateProvider) ShowAddress(name string) (address string, err error) { | ||
|
||
info, err := sp.Keybase.Key(name) | ||
if err != nil { | ||
return "", err | ||
} | ||
return info.GetAddress(), nil | ||
} | ||
|
||
func (sp *SubstrateProvider) ListAddresses() (map[string]string, error) { | ||
|
||
out := map[string]string{} | ||
info, err := sp.Keybase.List() | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, k := range info { | ||
addr := k.GetAddress() | ||
out[k.GetName()] = addr | ||
} | ||
return out, nil | ||
} | ||
|
||
func (sp *SubstrateProvider) DeleteKey(name string) error { | ||
|
||
if err := sp.Keybase.Delete(name); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (sp *SubstrateProvider) KeyExists(name string) bool { | ||
if sp.Keybase == nil { | ||
return false | ||
} | ||
|
||
k, err := sp.Keybase.Key(name) | ||
if err != nil { | ||
return false | ||
} | ||
return k.GetName() == name | ||
} | ||
|
||
func (sp *SubstrateProvider) ExportPrivKeyArmor(keyName string) (armor string, err error) { | ||
return "", nil | ||
} | ||
|
||
func (sp *SubstrateProvider) KeyAddOrRestore(keyName string, coinType uint32, mnemonic ...string) (*provider.KeyOutput, error) { | ||
|
||
var mnemonicStr string | ||
var err error | ||
|
||
if len(mnemonic) > 0 { | ||
mnemonicStr = mnemonic[0] | ||
} else { | ||
mnemonicStr, err = CreateMnemonic() | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
info, err := sp.Keybase.NewAccount(keyName, mnemonicStr, sp.PCfg.Network) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &provider.KeyOutput{Mnemonic: mnemonicStr, Address: info.GetAddress()}, nil | ||
} | ||
|
||
// CreateMnemonic creates a new mnemonic | ||
func CreateMnemonic() (string, error) { | ||
entropySeed, err := bip39.NewEntropy(256) | ||
if err != nil { | ||
return "", err | ||
} | ||
mnemonic, err := bip39.NewMnemonic(entropySeed) | ||
if err != nil { | ||
return "", err | ||
} | ||
return mnemonic, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package substrate_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cosmos/relayer/v2/relayer/chains/substrate/keystore" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestKeyRestore restores a test mnemonic | ||
func TestKeyRestoreAndRetrieve(t *testing.T) { | ||
keyName := "test_key" | ||
mnemonic := "blind master acoustic speak victory lend kiss grab glad help demand hood roast zone lend sponsor level cheap truck kingdom apology token hover reunion" | ||
expectedAddress := "5Hn67YZ75F3XrHiJAtiscJMGQ4zFNw9e45CNfLuxL6vEVYz8" | ||
|
||
provider, err := getTestProvider() | ||
require.Nil(t, err) | ||
|
||
config := getSubstrateConfig(homePath, 42) | ||
provider.Keybase, err = keystore.New(config.ChainName, config.KeyringBackend, config.KeyDirectory, nil) | ||
require.Nil(t, err) | ||
|
||
if provider.KeyExists(keyName) { | ||
err = provider.DeleteKey(keyName) // Delete if test is being run again | ||
require.Nil(t, err) | ||
} | ||
|
||
address, err := provider.RestoreKey(keyName, mnemonic, 0) | ||
require.Nil(t, err) | ||
require.Equal(t, expectedAddress, address, "Restored address: %s does not match expected: %s", address, expectedAddress) | ||
|
||
retrievedAddress, err := provider.ShowAddress(keyName) | ||
require.Nil(t, err) | ||
require.Equal(t, expectedAddress, address, "Restored address: %s does not match expected: %s", retrievedAddress, expectedAddress) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package keystore | ||
|
||
const ( | ||
ErrTextKeyNotFound = "key not found" | ||
ErrTextKeyWithAddressNotFound = "key with address not found" | ||
ErrTextAddressExists = "account with address already exists in keyring" | ||
ErrTextPubkeyExists = "public key already exists in keystore" | ||
ErrTextUnknownKeyringBackend = "unknown keyring backend" | ||
ErrTextFailedToRead = "failed to read" | ||
ErrTextFailedToOpen = "failed to open" | ||
ErrTextTooManyWrongPassphrases = "too many failed passphrase attempts" | ||
ErrTextIncorrectPassphrase = "incorrect passphrase" | ||
ErrTextPassphraseDoNotMatch = "passphrase do not match" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package keystore | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/ComposableFi/go-substrate-rpc-client/v4/signature" | ||
) | ||
|
||
// Info is the publicly exposed information about a keypair | ||
type Info interface { | ||
// GetName returns the name of the underlying key | ||
GetName() string | ||
// GetAddress returns the address of the underlying key as a string | ||
GetAddress() string | ||
// GetPublicKey returns the public key as bytes | ||
GetPublicKey() []byte | ||
// GetKeyringPair returns the keyring pair | ||
GetKeyringPair() signature.KeyringPair | ||
} | ||
|
||
// localInfo is the public information about a locally stored key | ||
type localInfo struct { | ||
Name string `json:"name"` | ||
Keypair signature.KeyringPair `json:"keypair"` | ||
} | ||
|
||
func newLocalInfo(name string, kp signature.KeyringPair) Info { | ||
return &localInfo{ | ||
Name: name, | ||
Keypair: kp, | ||
} | ||
} | ||
|
||
func (i localInfo) GetAddress() string { | ||
return i.Keypair.Address | ||
} | ||
|
||
func (i localInfo) GetName() string { | ||
return i.Name | ||
} | ||
|
||
func (i localInfo) GetPublicKey() []byte { | ||
return i.Keypair.PublicKey | ||
} | ||
|
||
func (i localInfo) GetKeyringPair() signature.KeyringPair { | ||
return i.Keypair | ||
} | ||
|
||
func marshalInfo(i Info) ([]byte, error) { | ||
marshalled, err := json.Marshal(i) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return marshalled, nil | ||
} | ||
|
||
func unmarshalInfo(bz []byte) (Info, error) { | ||
localInfo := localInfo{} | ||
if err := json.Unmarshal(bz, &localInfo); err != nil { | ||
return nil, err | ||
} | ||
|
||
return localInfo, nil | ||
} | ||
|
||
func infoKey(name string) string { return fmt.Sprintf("%s.%s", name, infoSuffix) } | ||
|
||
func infoKeyBz(name string) []byte { return []byte(infoKey(name)) } |
Oops, something went wrong.