Update keybase back to cosmos-sdk with support (#147)

* Migrate keybase back to Cosmos-sdk using temporary fork

* Cleaned up other affected code

* Change to updated tendermint

* fmt

* Change auth codec update

* clean up codec registration to respective packages

* Fix import fmt

* Remove no longer necessary replace

* Fix function comment
This commit is contained in:
Austin Abell 2019-11-13 12:00:21 -05:00 committed by GitHub
parent 327abc4edf
commit 9311f9efd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 137 additions and 2542 deletions

View File

@ -4,10 +4,12 @@ import (
"encoding/json"
"os"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/cosmos/ethermint/x/evm"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
@ -76,8 +78,10 @@ func MakeCodec() *codec.Codec {
var cdc = codec.New()
ModuleBasics.RegisterCodec(cdc)
cryptokeys.RegisterCodec(cdc) // temporary
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
emintcrypto.RegisterCodec(cdc)
eminttypes.RegisterCodec(cdc)
return cdc

68
cmd/emintcli/keys.go Normal file
View File

@ -0,0 +1,68 @@
package main
import (
"github.com/cosmos/cosmos-sdk/client/flags"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys"
emintCrypto "github.com/cosmos/ethermint/crypto"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
flagDryRun = "dry-run"
)
// keyCommands registers a sub-tree of commands to interact with
// local private key storage.
func keyCommands() *cobra.Command {
cmd := &cobra.Command{
Use: "keys",
Short: "Add or view local private keys",
Long: `Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.`,
}
addCmd := clientkeys.AddKeyCommand()
addCmd.RunE = runAddCmd
cmd.AddCommand(
clientkeys.MnemonicKeyCommand(),
addCmd,
clientkeys.ExportKeyCommand(),
clientkeys.ImportKeyCommand(),
clientkeys.ListKeysCmd(),
clientkeys.ShowKeysCmd(),
flags.LineBreak,
clientkeys.DeleteKeyCommand(),
clientkeys.UpdateKeyCommand(),
clientkeys.ParseKeyStringCommand(),
clientkeys.MigrateCommand(),
)
return cmd
}
func getKeybase(dryrun bool) (keys.Keybase, error) {
if dryrun {
return keys.NewInMemory(keys.WithKeygenFunc(ethermintKeygenFunc)), nil
}
return clientkeys.NewKeyBaseFromHomeFlag(keys.WithKeygenFunc(ethermintKeygenFunc))
}
func runAddCmd(cmd *cobra.Command, args []string) error {
kb, err := getKeybase(viper.GetBool(flagDryRun))
if err != nil {
return err
}
return clientkeys.RunAddCmd(cmd, args, kb)
}
func ethermintKeygenFunc(bz [32]byte) tmcrypto.PrivKey {
return emintCrypto.PrivKeySecp256k1(bz[:])
}

View File

@ -4,19 +4,20 @@ import (
"os"
"path"
emintapp "github.com/cosmos/ethermint/app"
"github.com/cosmos/ethermint/rpc"
"github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/client"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
sdkrpc "github.com/cosmos/cosmos-sdk/client/rpc"
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
emintkeys "github.com/cosmos/ethermint/keys"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
emintapp "github.com/cosmos/ethermint/app"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
@ -27,7 +28,8 @@ func main() {
cdc := emintapp.MakeCodec()
authtypes.ModuleCdc = cdc
cryptokeys.CryptoCdc = cdc
clientkeys.KeysCdc = cdc
// Read in the configuration file for the sdk
config := sdk.GetConfig()
@ -56,7 +58,7 @@ func main() {
// TODO: Set up rest routes (if included, different from web3 api)
rpc.Web3RpcCmd(cdc),
client.LineBreak,
emintkeys.Commands(),
keyCommands(),
client.LineBreak,
)

View File

@ -9,6 +9,7 @@ import (
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -16,7 +17,6 @@ import (
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/ethermint/keys"
ethermint "github.com/cosmos/ethermint/types"
)

View File

@ -1,302 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/common"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
kbkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/ethermint/keys"
clientutils "github.com/cosmos/ethermint/x/evm/client/utils"
)
// StakingMsgBuildingHelpers helpers for message building gen-tx command
type StakingMsgBuildingHelpers interface {
CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string)
PrepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey)
BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) (auth.TxBuilder, sdk.Msg, error)
}
// GenTxCmd builds the application's gentx command.
// nolint: errcheck
func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, smbh StakingMsgBuildingHelpers,
genAccIterator types.GenesisAccountsIterator, defaultNodeHome, defaultCLIHome string) *cobra.Command {
ipDefault, _ := server.ExternalIP()
fsCreateValidator, flagNodeID, flagPubKey, flagAmount, defaultsDesc := smbh.CreateValidatorMsgHelpers(ipDefault)
cmd := &cobra.Command{
Use: "gentx",
Short: "Generate a genesis tx carrying a self delegation",
Args: cobra.NoArgs,
Long: fmt.Sprintf(`This command is an alias of the 'tx create-validator' command'.
It creates a genesis transaction to create a validator.
The following default parameters are included:
%s`, defaultsDesc),
RunE: func(cmd *cobra.Command, args []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(client.FlagHome))
nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(ctx.Config)
if err != nil {
return errors.Wrap(err, "failed to initialize node validator files")
}
// Read --nodeID, if empty take it from priv_validator.json
if nodeIDString := viper.GetString(flagNodeID); nodeIDString != "" {
nodeID = nodeIDString
}
// Read --pubkey, if empty take it from priv_validator.json
if valPubKeyString := viper.GetString(flagPubKey); valPubKeyString != "" {
valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString)
if err != nil {
return errors.Wrap(err, "failed to get consensus node public key")
}
}
genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile())
if err != nil {
return errors.Wrapf(err, "failed to read genesis doc file %s", config.GenesisFile())
}
var genesisState map[string]json.RawMessage
if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil {
return errors.Wrap(err, "failed to unmarshal genesis state")
}
if err = mbm.ValidateGenesis(genesisState); err != nil {
return errors.Wrap(err, "failed to validate genesis state")
}
// * Necessary to change keybase here
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
if err != nil {
return errors.Wrap(err, "failed to initialize keybase")
}
name := viper.GetString(client.FlagName)
key, err := kb.Get(name)
if err != nil {
return errors.Wrap(err, "failed to read from keybase")
}
// Set flags for creating gentx
viper.Set(client.FlagHome, viper.GetString(flagClientHome))
smbh.PrepareFlagsForTxCreateValidator(config, nodeID, genDoc.ChainID, valPubKey)
// Fetch the amount of coins staked
amount := viper.GetString(flagAmount)
coins, err := sdk.ParseCoins(amount)
if err != nil {
return errors.Wrap(err, "failed to parse coins")
}
err = genutil.ValidateAccountInGenesis(genesisState, genAccIterator, key.GetAddress(), coins, cdc)
if err != nil {
return errors.Wrap(err, "failed to validate account in genesis")
}
txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)).WithKeybase(kb)
cliCtx := clientutils.NewETHCLIContext().WithCodec(cdc)
// Set the generate-only flag here after the CLI context has
// been created. This allows the from name/key to be correctly populated.
//
// TODO: Consider removing the manual setting of generate-only in
// favor of a 'gentx' flag in the create-validator command.
viper.Set(client.FlagGenerateOnly, true)
// create a 'create-validator' message
txBldr, msg, err := smbh.BuildCreateValidatorMsg(cliCtx, txBldr)
if err != nil {
return errors.Wrap(err, "failed to build create-validator message")
}
info, err := kb.Get(name)
if err != nil {
return errors.Wrap(err, "failed to read from tx builder keybase")
}
if info.GetType() == kbkeys.TypeOffline || info.GetType() == kbkeys.TypeMulti {
fmt.Println("Offline key passed in. Use `tx sign` command to sign:")
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// write the unsigned transaction to the buffer
w := bytes.NewBuffer([]byte{})
cliCtx = cliCtx.WithOutput(w)
if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}); err != nil {
return errors.Wrap(err, "failed to print unsigned std tx")
}
// read the transaction
stdTx, err := readUnsignedGenTxFile(cdc, w)
if err != nil {
return errors.Wrap(err, "failed to read unsigned gen tx file")
}
// * Function needed to be overriden for signStdTx function using default keybase
// sign the transaction and write it to the output file
signedTx, err := signStdTx(txBldr, cliCtx, name, stdTx, false, true)
if err != nil {
return errors.Wrap(err, "failed to sign std tx")
}
// Fetch output file name
outputDocument := viper.GetString(client.FlagOutputDocument)
if outputDocument == "" {
outputDocument, err = makeOutputFilepath(config.RootDir, nodeID)
if err != nil {
return errors.Wrap(err, "failed to create output file path")
}
}
if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil {
return errors.Wrap(err, "failed to write signed gen tx")
}
fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument)
return nil
},
}
cmd.Flags().String(client.FlagHome, defaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, defaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().String(client.FlagOutputDocument, "",
"write the genesis transaction JSON document to the given file instead of the default location")
cmd.Flags().AddFlagSet(fsCreateValidator)
if err := cmd.MarkFlagRequired(client.FlagName); err != nil {
panic(err)
}
return cmd
}
func makeOutputFilepath(rootDir, nodeID string) (string, error) {
writePath := filepath.Join(rootDir, "config", "gentx")
if err := common.EnsureDir(writePath, 0700); err != nil {
return "", err
}
return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil
}
func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) {
var stdTx auth.StdTx
bytes, err := ioutil.ReadAll(r)
if err != nil {
return stdTx, err
}
err = cdc.UnmarshalJSON(bytes, &stdTx)
return stdTx, err
}
func writeSignedGenTx(cdc *codec.Codec, outputDocument string, tx auth.StdTx) error {
outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer func() {
if err := outputFile.Close(); err != nil {
panic(err)
}
}()
json, err := cdc.MarshalJSON(tx)
if err != nil {
return err
}
_, err = fmt.Fprintf(outputFile, "%s\n", json)
return err
}
// SignStdTx appends a signature to a StdTx and returns a copy of it. If appendSig
// is false, it replaces the signatures already attached with the new signature.
// Don't perform online validation or lookups if offline is true.
func signStdTx(
txBldr authtypes.TxBuilder, cliCtx context.CLIContext, name string,
stdTx authtypes.StdTx, appendSig bool, offline bool,
) (authtypes.StdTx, error) {
var signedStdTx authtypes.StdTx
info, err := txBldr.Keybase().Get(name)
if err != nil {
return signedStdTx, err
}
addr := info.GetPubKey().Address()
// check whether the address is a signer
if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) {
return signedStdTx, fmt.Errorf("%s: %s", errors.New("tx intended signer does not match the given signer"), name)
}
if !offline {
txBldr, err = populateAccountFromState(txBldr, cliCtx, sdk.AccAddress(addr))
if err != nil {
return signedStdTx, err
}
}
// * Switched to use Ethermint keybase
passphrase, err := keys.GetPassphrase(name)
if err != nil {
return signedStdTx, err
}
return txBldr.SignStdTx(name, passphrase, stdTx, appendSig)
}
func populateAccountFromState(
txBldr authtypes.TxBuilder, cliCtx context.CLIContext, addr sdk.AccAddress,
) (authtypes.TxBuilder, error) {
num, seq, err := authtypes.NewAccountRetriever(cliCtx).GetAccountNumberSequence(addr)
if err != nil {
return txBldr, err
}
return txBldr.WithAccountNumber(num).WithSequence(seq), nil
}
func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool {
for _, s := range signers {
if bytes.Equal(user.Bytes(), s.Bytes()) {
return true
}
}
return false
}

View File

@ -8,12 +8,13 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
genutil "github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/cosmos/cosmos-sdk/x/staking"
@ -36,9 +37,10 @@ func main() {
cdc := emintapp.MakeCodec()
cryptokeys.CryptoCdc = cdc
genutil.ModuleCdc = cdc
genutiltypes.ModuleCdc = cdc
authtypes.ModuleCdc = cdc
clientkeys.KeysCdc = cdc
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
@ -57,7 +59,7 @@ func main() {
rootCmd.AddCommand(
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, emintapp.ModuleBasics, emintapp.DefaultNodeHome)),
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, emintapp.DefaultNodeHome),
GenTxCmd(
genutilcli.GenTxCmd(
ctx, cdc, emintapp.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{}, emintapp.DefaultNodeHome, emintapp.DefaultCLIHome,
),
genutilcli.ValidateGenesisCmd(ctx, cdc, emintapp.ModuleBasics),

View File

@ -1,25 +0,0 @@
package keys
import (
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/cosmos/cosmos-sdk/codec"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
emintCrypto "github.com/cosmos/ethermint/crypto"
)
var cdc *codec.Codec
func init() {
cdc = codec.New()
cryptoAmino.RegisterAmino(cdc)
cdc.RegisterInterface((*cosmosKeys.Info)(nil), nil)
emintCrypto.RegisterCodec(cdc)
cdc.RegisterConcrete(hd.BIP44Params{}, "crypto/keys/hd/BIP44Params", nil)
cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil)
cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil)
cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil)
// cdc.RegisterConcrete(multiInfo{}, "crypto/keys/multiInfo", nil)
cdc.Seal()
}

View File

@ -1,512 +0,0 @@
package keys
import (
"bufio"
"fmt"
"os"
"reflect"
"strings"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/crypto"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/crypto/keys/mintkey"
bip39 "github.com/cosmos/go-bip39"
emintCrypto "github.com/cosmos/ethermint/crypto"
tmcrypto "github.com/tendermint/tendermint/crypto"
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
dbm "github.com/tendermint/tm-db"
)
var _ cosmosKeys.Keybase = dbKeybase{}
// Language is a language to create the BIP 39 mnemonic in.
// Currently, only english is supported though.
// Find a list of all supported languages in the BIP 39 spec (word lists).
type Language int
//noinspection ALL
const (
// English is the default language to create a mnemonic.
// It is the only supported language by this package.
English Language = iota + 1
// Japanese is currently not supported.
Japanese
// Korean is currently not supported.
Korean
// Spanish is currently not supported.
Spanish
// ChineseSimplified is currently not supported.
ChineseSimplified
// ChineseTraditional is currently not supported.
ChineseTraditional
// French is currently not supported.
French
// Italian is currently not supported.
Italian
addressSuffix = "address"
infoSuffix = "info"
)
const (
// used for deriving seed from mnemonic
DefaultBIP39Passphrase = ""
// bits of entropy to draw when creating a mnemonic
defaultEntropySize = 256
)
var (
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a
// different signing scheme than secp256k1.
ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported")
// ErrUnsupportedLanguage is raised when the caller tries to use a
// different language than english for creating a mnemonic sentence.
ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported")
)
// dbKeybase combines encryption and storage implementation to provide
// a full-featured key manager
type dbKeybase struct {
db dbm.DB
}
// newDbKeybase creates a new keybase instance using the passed DB for reading and writing keys.
func newDbKeybase(db dbm.DB) cosmosKeys.Keybase {
return dbKeybase{
db: db,
}
}
// NewInMemory creates a transient keybase on top of in-memory storage
// instance useful for testing purposes and on-the-fly key generation.
func NewInMemory() cosmosKeys.Keybase { return dbKeybase{dbm.NewMemDB()} }
// CreateMnemonic generates a new key and persists it to storage, encrypted
// using the provided password.
// It returns the generated mnemonic and the key Info.
// It returns an error if it fails to
// generate a key for the given algo type, or if another key is
// already stored under the same name.
func (kb dbKeybase) CreateMnemonic(name string, language cosmosKeys.Language, passwd string, algo cosmosKeys.SigningAlgo) (info cosmosKeys.Info, mnemonic string, err error) {
if language != cosmosKeys.English {
return nil, "", ErrUnsupportedLanguage
}
if algo != Secp256k1 {
err = ErrUnsupportedSigningAlgo
return
}
// default number of words (24):
// this generates a mnemonic directly from the number of words by reading system entropy.
entropy, err := bip39.NewEntropy(defaultEntropySize)
if err != nil {
return
}
mnemonic, err = bip39.NewMnemonic(entropy)
if err != nil {
return
}
seed := bip39.NewSeed(mnemonic, DefaultBIP39Passphrase)
fullFundraiserPath := types.GetConfig().GetFullFundraiserPath()
info, err = kb.persistDerivedKey(seed, passwd, name, fullFundraiserPath)
return
}
// CreateAccount converts a mnemonic to a private key and persists it, encrypted with the given password.
func (kb dbKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (cosmosKeys.Info, error) {
coinType := types.GetConfig().GetCoinType()
hdPath := hd.NewFundraiserParams(account, coinType, index)
return kb.Derive(name, mnemonic, bip39Passwd, encryptPasswd, *hdPath)
}
func (kb dbKeybase) Derive(name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params) (info cosmosKeys.Info, err error) {
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
if err != nil {
return
}
info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String())
return info, err
}
// CreateLedger creates a new locally-stored reference to a Ledger keypair
// It returns the created key info and an error if the Ledger could not be queried
func (kb dbKeybase) CreateLedger(name string, algo cosmosKeys.SigningAlgo, hrp string, account, index uint32) (cosmosKeys.Info, error) {
if algo != Secp256k1 {
return nil, ErrUnsupportedSigningAlgo
}
coinType := types.GetConfig().GetCoinType()
hdPath := hd.NewFundraiserParams(account, coinType, index)
priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
if err != nil {
return nil, err
}
pub := priv.PubKey()
// Note: Once Cosmos App v1.3.1 is compulsory, it could be possible to check that pubkey and addr match
return kb.writeLedgerKey(name, pub, *hdPath), nil
}
// CreateOffline creates a new reference to an offline keypair. It returns the
// created key info.
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (cosmosKeys.Info, error) {
return kb.writeOfflineKey(name, pub), nil
}
// CreateMulti creates a new reference to a multisig (offline) keypair. It
// returns the created key info.
func (kb dbKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (cosmosKeys.Info, error) {
return nil, nil
}
func (kb *dbKeybase) persistDerivedKey(seed []byte, passwd, name, fullHdPath string) (info cosmosKeys.Info, err error) {
// create master key and derive first key:
masterPriv, ch := hd.ComputeMastersFromSeed(seed)
derivedPriv, err := hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath)
if err != nil {
return
}
// if we have a password, use it to encrypt the private key and store it
// else store the public key only
if passwd != "" {
info = kb.writeLocalKey(name, emintCrypto.PrivKeySecp256k1(derivedPriv[:]), passwd)
} else {
pubk := emintCrypto.PrivKeySecp256k1(derivedPriv[:]).PubKey()
info = kb.writeOfflineKey(name, pubk)
}
return info, nil
}
// List returns the keys from storage in alphabetical order.
func (kb dbKeybase) List() ([]cosmosKeys.Info, error) {
var res []cosmosKeys.Info
iter := kb.db.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := string(iter.Key())
// need to include only keys in storage that have an info suffix
if strings.HasSuffix(key, infoSuffix) {
info, err := readInfo(iter.Value())
if err != nil {
return nil, err
}
res = append(res, info)
}
}
return res, nil
}
// Get returns the public information about one key.
func (kb dbKeybase) Get(name string) (cosmosKeys.Info, error) {
bs := kb.db.Get(infoKey(name))
if len(bs) == 0 {
return nil, keyerror.NewErrKeyNotFound(name)
}
return readInfo(bs)
}
func (kb dbKeybase) GetByAddress(address types.AccAddress) (cosmosKeys.Info, error) {
ik := kb.db.Get(addrKey(address))
if len(ik) == 0 {
return nil, fmt.Errorf("key with address %s not found", address)
}
bs := kb.db.Get(ik)
return readInfo(bs)
}
// Sign signs the msg with the named key.
// It returns an error if the key doesn't exist or the decryption fails.
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
info, err := kb.Get(name)
if err != nil {
return
}
var priv tmcrypto.PrivKey
switch info := info.(type) {
case localInfo:
if info.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
}
priv, err = mintkey.UnarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
case ledgerInfo:
priv, err = crypto.NewPrivKeyLedgerSecp256k1Unsafe(info.Path)
if err != nil {
return
}
// case offlineInfo, multiInfo:
case offlineInfo:
_, err := fmt.Fprintf(os.Stderr, "Message to sign:\n\n%s\n", msg)
if err != nil {
return nil, nil, err
}
buf := bufio.NewReader(os.Stdin)
_, err = fmt.Fprintf(os.Stderr, "\nEnter Amino-encoded signature:\n")
if err != nil {
return nil, nil, err
}
// Will block until user inputs the signature
signed, err := buf.ReadString('\n')
if err != nil {
return nil, nil, err
}
if err := cdc.UnmarshalBinaryLengthPrefixed([]byte(signed), sig); err != nil {
return nil, nil, errors.Wrap(err, "failed to decode signature")
}
return sig, info.GetPubKey(), nil
}
sig, err = priv.Sign(msg)
if err != nil {
return nil, nil, err
}
pub = priv.PubKey()
return sig, pub, nil
}
func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
info, err := kb.Get(name)
if err != nil {
return nil, err
}
var priv tmcrypto.PrivKey
switch info := info.(type) {
case localInfo:
if info.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return nil, err
}
priv, err = mintkey.UnarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
// case ledgerInfo, offlineInfo, multiInfo:
case ledgerInfo, offlineInfo:
return nil, errors.New("only works on local private keys")
}
return priv, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
bz := kb.db.Get(infoKey(name))
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return mintkey.ArmorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format.
// Retrieve a Info object by its name and return the public key in
// a portable format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz := kb.db.Get(infoKey(name))
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := readInfo(bz)
if err != nil {
return
}
return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil
}
// ExportPrivKey returns a private key in ASCII armored format.
// It returns an error if the key does not exist or a wrong encryption passphrase is supplied.
func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
if err != nil {
return "", err
}
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase), nil
}
// ImportPrivKey imports a private key in ASCII armor format.
// It returns an error if a key with the same name exists or a wrong encryption passphrase is
// supplied.
func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string) error {
if _, err := kb.Get(name); err == nil {
return errors.New("Cannot overwrite key " + name)
}
privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
if err != nil {
return errors.Wrap(err, "couldn't import private key")
}
kb.writeLocalKey(name, privKey, passphrase)
return nil
}
func (kb dbKeybase) Import(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
infoBytes, err := mintkey.UnarmorInfoBytes(armor)
if err != nil {
return
}
kb.db.Set(infoKey(name), infoBytes)
return nil
}
// ImportPubKey imports ASCII-armored public keys.
// Store a new Info object holding a public key only, i.e. it will
// not be possible to sign with it as it lacks the secret key.
func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
if err != nil {
return
}
pubKey, err := cryptoAmino.PubKeyFromBytes(pubBytes)
if err != nil {
return
}
kb.writeOfflineKey(name, pubKey)
return
}
// Delete removes key forever, but we must present the
// proper passphrase before deleting it (for security).
// It returns an error if the key doesn't exist or
// passphrases don't match.
// Passphrase is ignored when deleting references to
// offline and Ledger / HW wallet keys.
func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
// verify we have the proper password before deleting
info, err := kb.Get(name)
if err != nil {
return err
}
if linfo, ok := info.(localInfo); ok && !skipPass {
if _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
return err
}
}
kb.db.DeleteSync(addrKey(info.GetAddress()))
kb.db.DeleteSync(infoKey(name))
return nil
}
// Update changes the passphrase with which an already stored key is
// encrypted.
//
// oldpass must be the current passphrase used for encryption,
// getNewpass is a function to get the passphrase to permanently replace
// the current passphrase
func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
info, err := kb.Get(name)
if err != nil {
return err
}
switch info := info.(type) {
case localInfo:
key, err := mintkey.UnarmorDecryptPrivKey(info.PrivKeyArmor, oldpass)
if err != nil {
return err
}
newpass, err := getNewpass()
if err != nil {
return err
}
kb.writeLocalKey(name, key, newpass)
return nil
default:
return fmt.Errorf("locally stored key required. Received: %v", reflect.TypeOf(info).String())
}
}
// CloseDB releases the lock and closes the storage backend.
func (kb dbKeybase) CloseDB() {
kb.db.Close()
}
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string) cosmosKeys.Info {
privkey, ok := priv.(emintCrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", priv))
}
// encrypt private key using passphrase
privArmor := mintkey.EncryptArmorPrivKey(privkey, passphrase)
// make Info
pub := privkey.PubKey()
pubkey, ok := pub.(emintCrypto.PubKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid public key type: %T", pub))
}
info := newLocalInfo(name, pubkey, privArmor)
kb.writeInfo(name, info)
return info
}
func (kb dbKeybase) writeLedgerKey(name string, pub tmcrypto.PubKey, path hd.BIP44Params) cosmosKeys.Info {
pubkey, ok := pub.(emintCrypto.PubKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid public key type: %T", pub))
}
info := newLedgerInfo(name, pubkey, path)
kb.writeInfo(name, info)
return info
}
func (kb dbKeybase) writeOfflineKey(name string, pub tmcrypto.PubKey) cosmosKeys.Info {
pubkey, ok := pub.(emintCrypto.PubKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid public key type: %T", pub))
}
info := newOfflineInfo(name, pubkey)
kb.writeInfo(name, info)
return info
}
func (kb dbKeybase) writeInfo(name string, info cosmosKeys.Info) {
// write the info by key
key := infoKey(name)
serializedInfo := writeInfo(info)
kb.db.SetSync(key, serializedInfo)
// store a pointer to the infokey by address for fast lookup
kb.db.SetSync(addrKey(info.GetAddress()), key)
}
func addrKey(address types.AccAddress) []byte {
return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix))
}
func infoKey(name string) []byte {
return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
}

View File

@ -1,13 +0,0 @@
package keys
import (
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
)
// SigningAlgo defines an algorithm to derive key-pairs which can be used for cryptographic signing.
type SigningAlgo string
const (
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
Secp256k1 = cosmosKeys.SigningAlgo("emintsecp256k1")
)

View File

@ -1,222 +0,0 @@
package keys
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var _ cosmosKeys.Keybase = lazyKeybase{}
type lazyKeybase struct {
name string
dir string
}
// New creates a new instance of a lazy keybase.
func New(name, dir string) cosmosKeys.Keybase {
if err := cmn.EnsureDir(dir, 0700); err != nil {
panic(fmt.Sprintf("failed to create Keybase directory: %s", err))
}
return lazyKeybase{name: name, dir: dir}
}
func (lkb lazyKeybase) List() ([]cosmosKeys.Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).List()
}
func (lkb lazyKeybase) Get(name string) (cosmosKeys.Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).Get(name)
}
func (lkb lazyKeybase) GetByAddress(address sdk.AccAddress) (cosmosKeys.Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).GetByAddress(address)
}
func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Delete(name, passphrase, skipPass)
}
func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, nil, err
}
defer db.Close()
return newDbKeybase(db).Sign(name, passphrase, msg)
}
func (lkb lazyKeybase) CreateMnemonic(name string, language cosmosKeys.Language, passwd string, algo cosmosKeys.SigningAlgo) (info cosmosKeys.Info, seed string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, "", err
}
defer db.Close()
return newDbKeybase(db).CreateMnemonic(name, language, passwd, algo)
}
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (cosmosKeys.Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index)
}
func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (cosmosKeys.Info, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params)
}
func (lkb lazyKeybase) CreateLedger(name string, algo cosmosKeys.SigningAlgo, hrp string, account, index uint32) (info cosmosKeys.Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateLedger(name, algo, hrp, account, index)
}
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info cosmosKeys.Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateOffline(name, pubkey)
}
func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info cosmosKeys.Info, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).CreateMulti(name, pubkey)
}
func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Update(name, oldpass, getNewpass)
}
func (lkb lazyKeybase) Import(name string, armor string) (err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).Import(name, armor)
}
func (lkb lazyKeybase) ImportPrivKey(name string, armor string, passphrase string) error {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).ImportPrivKey(name, armor, passphrase)
}
func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return err
}
defer db.Close()
return newDbKeybase(db).ImportPubKey(name, armor)
}
func (lkb lazyKeybase) Export(name string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDbKeybase(db).Export(name)
}
func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDbKeybase(db).ExportPubKey(name)
}
func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return nil, err
}
defer db.Close()
return newDbKeybase(db).ExportPrivateKeyObject(name, passphrase)
}
func (lkb lazyKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
if err != nil {
return "", err
}
defer db.Close()
return newDbKeybase(db).ExportPrivKey(name, decryptPassphrase, encryptPassphrase)
}
func (lkb lazyKeybase) CloseDB() {}

View File

@ -1,111 +0,0 @@
package keys
import (
"encoding/hex"
sdk "github.com/cosmos/cosmos-sdk/types"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
)
// KeyOutput defines a structure wrapping around an Info object used for output
// functionality.
type KeyOutput struct {
Name string `json:"name"`
Type string `json:"type"`
Address string `json:"address"`
ETHAddress string `json:"ethaddress"`
PubKey string `json:"pubkey"`
ETHPubKey string `json:"ethpubkey"`
Mnemonic string `json:"mnemonic,omitempty"`
Threshold uint `json:"threshold,omitempty"`
}
// NewKeyOutput creates a default KeyOutput instance without Mnemonic, Threshold and PubKeys
func NewKeyOutput(name, keyType, address, ethaddress, pubkey, ethpubkey string) KeyOutput {
return KeyOutput{
Name: name,
Type: keyType,
Address: address,
ETHAddress: ethaddress,
PubKey: pubkey,
ETHPubKey: ethpubkey,
}
}
// Bech32KeysOutput returns a slice of KeyOutput objects, each with the "acc"
// Bech32 prefixes, given a slice of Info objects. It returns an error if any
// call to Bech32KeyOutput fails.
func Bech32KeysOutput(infos []cosmosKeys.Info) ([]KeyOutput, error) {
kos := make([]KeyOutput, len(infos))
for i, info := range infos {
ko, err := Bech32KeyOutput(info)
if err != nil {
return nil, err
}
kos[i] = ko
}
return kos, nil
}
// Bech32ConsKeyOutput create a KeyOutput in with "cons" Bech32 prefixes.
func Bech32ConsKeyOutput(keyInfo cosmosKeys.Info) (KeyOutput, error) {
address := keyInfo.GetPubKey().Address()
bechPubKey, err := sdk.Bech32ifyConsPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return NewKeyOutput(
keyInfo.GetName(),
keyInfo.GetType().String(),
sdk.ConsAddress(address.Bytes()).String(),
getEthAddress(keyInfo),
bechPubKey,
hex.EncodeToString(keyInfo.GetPubKey().Bytes()),
), nil
}
// Bech32ValKeyOutput create a KeyOutput in with "val" Bech32 prefixes.
func Bech32ValKeyOutput(keyInfo cosmosKeys.Info) (KeyOutput, error) {
address := keyInfo.GetPubKey().Address()
bechPubKey, err := sdk.Bech32ifyValPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return NewKeyOutput(
keyInfo.GetName(),
keyInfo.GetType().String(),
sdk.ValAddress(address.Bytes()).String(),
getEthAddress(keyInfo),
bechPubKey,
hex.EncodeToString(keyInfo.GetPubKey().Bytes()),
), nil
}
// Bech32KeyOutput create a KeyOutput in with "acc" Bech32 prefixes.
func Bech32KeyOutput(keyInfo cosmosKeys.Info) (KeyOutput, error) {
address := keyInfo.GetPubKey().Address()
bechPubKey, err := sdk.Bech32ifyAccPub(keyInfo.GetPubKey())
if err != nil {
return KeyOutput{}, err
}
return NewKeyOutput(
keyInfo.GetName(),
keyInfo.GetType().String(),
sdk.AccAddress(address.Bytes()).String(),
getEthAddress(keyInfo),
bechPubKey,
hex.EncodeToString(keyInfo.GetPubKey().Bytes()),
), nil
}
func getEthAddress(info cosmosKeys.Info) string {
return info.GetPubKey().Address().String()
}

View File

@ -1,156 +0,0 @@
package keys
import (
"fmt"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
emintCrypto "github.com/cosmos/ethermint/crypto"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/cosmos-sdk/types"
)
// KeyType reflects a human-readable type for key listing.
type KeyType uint
// Info KeyTypes
const (
TypeLocal KeyType = 0
TypeLedger KeyType = 1
TypeOffline KeyType = 2
TypeMulti KeyType = 3
)
var keyTypes = map[KeyType]string{
TypeLocal: "local",
TypeLedger: "ledger",
TypeOffline: "offline",
TypeMulti: "multi",
}
// String implements the stringer interface for KeyType.
func (kt KeyType) String() string {
return keyTypes[kt]
}
var (
_ cosmosKeys.Info = &localInfo{}
_ cosmosKeys.Info = &ledgerInfo{}
_ cosmosKeys.Info = &offlineInfo{}
)
// localInfo is the public information about a locally stored key
type localInfo struct {
Name string `json:"name"`
PubKey emintCrypto.PubKeySecp256k1 `json:"pubkey"`
PrivKeyArmor string `json:"privkey.armor"`
}
func newLocalInfo(name string, pub emintCrypto.PubKeySecp256k1, privArmor string) cosmosKeys.Info {
return &localInfo{
Name: name,
PubKey: pub,
PrivKeyArmor: privArmor,
}
}
func (i localInfo) GetType() cosmosKeys.KeyType {
return cosmosKeys.TypeLocal
}
func (i localInfo) GetName() string {
return i.Name
}
func (i localInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
func (i localInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i localInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// ledgerInfo is the public information about a Ledger key
type ledgerInfo struct {
Name string `json:"name"`
PubKey emintCrypto.PubKeySecp256k1 `json:"pubkey"`
Path hd.BIP44Params `json:"path"`
}
func newLedgerInfo(name string, pub emintCrypto.PubKeySecp256k1, path hd.BIP44Params) cosmosKeys.Info {
return &ledgerInfo{
Name: name,
PubKey: pub,
Path: path,
}
}
func (i ledgerInfo) GetType() cosmosKeys.KeyType {
return cosmosKeys.TypeLedger
}
func (i ledgerInfo) GetName() string {
return i.Name
}
func (i ledgerInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
func (i ledgerInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
tmp := i.Path
return &tmp, nil
}
// offlineInfo is the public information about an offline key
type offlineInfo struct {
Name string `json:"name"`
PubKey emintCrypto.PubKeySecp256k1 `json:"pubkey"`
}
func newOfflineInfo(name string, pub emintCrypto.PubKeySecp256k1) cosmosKeys.Info {
return &offlineInfo{
Name: name,
PubKey: pub,
}
}
func (i offlineInfo) GetType() cosmosKeys.KeyType {
return cosmosKeys.TypeOffline
}
func (i offlineInfo) GetName() string {
return i.Name
}
func (i offlineInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
func (i offlineInfo) GetAddress() types.AccAddress {
return i.PubKey.Address().Bytes()
}
func (i offlineInfo) GetPath() (*hd.BIP44Params, error) {
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
}
// encoding info
func writeInfo(i cosmosKeys.Info) []byte {
return cdc.MustMarshalBinaryLengthPrefixed(i)
}
// decoding info
func readInfo(bz []byte) (info cosmosKeys.Info, err error) {
err = cdc.UnmarshalBinaryLengthPrefixed(bz, &info)
return
}

View File

@ -1,46 +0,0 @@
package keys
import (
"testing"
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
"github.com/cosmos/ethermint/crypto"
"github.com/stretchr/testify/assert"
)
func TestWriteReadInfo(t *testing.T) {
tmpKey, err := crypto.GenerateKey()
assert.NoError(t, err)
pkey := tmpKey.PubKey()
pubkey, ok := pkey.(crypto.PubKeySecp256k1)
assert.True(t, ok)
info := newOfflineInfo("offline", pubkey)
bytes := writeInfo(info)
assert.NotNil(t, bytes)
regeneratedKey, err := readInfo(bytes)
assert.NoError(t, err)
assert.Equal(t, info.GetPubKey(), regeneratedKey.GetPubKey())
assert.Equal(t, info.GetName(), regeneratedKey.GetName())
info = newLocalInfo("local", pubkey, "testarmor")
bytes = writeInfo(info)
assert.NotNil(t, bytes)
regeneratedKey, err = readInfo(bytes)
assert.NoError(t, err)
assert.Equal(t, info.GetPubKey(), regeneratedKey.GetPubKey())
assert.Equal(t, info.GetName(), regeneratedKey.GetName())
info = newLedgerInfo("ledger", pubkey,
hd.BIP44Params{Purpose: 1, CoinType: 1, Account: 1, Change: false, AddressIndex: 1})
bytes = writeInfo(info)
assert.NotNil(t, bytes)
regeneratedKey, err = readInfo(bytes)
assert.NoError(t, err)
assert.Equal(t, info.GetPubKey(), regeneratedKey.GetPubKey())
assert.Equal(t, info.GetName(), regeneratedKey.GetName())
}

View File

@ -7,9 +7,19 @@ import (
ethcrypto "github.com/ethereum/go-ethereum/crypto"
ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
tmcrypto "github.com/tendermint/tendermint/crypto"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
)
func init() {
tmamino.RegisterKeyType(PubKeySecp256k1{}, PubKeyAminoName)
tmamino.RegisterKeyType(PrivKeySecp256k1{}, PrivKeyAminoName)
authtypes.RegisterAccountTypeCodec(PubKeySecp256k1{}, PubKeyAminoName)
authtypes.RegisterAccountTypeCodec(PrivKeySecp256k1{}, PrivKeyAminoName)
}
// ----------------------------------------------------------------------------
// secp256k1 Private Key

13
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8 // indirect
github.com/cespare/cp v1.1.1 // indirect
github.com/cosmos/cosmos-sdk v0.34.4-0.20191031200835-02c6c9fafd58
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
github.com/elastic/gosigar v0.10.3 // indirect
@ -35,8 +35,7 @@ require (
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0
github.com/spf13/viper v1.5.0
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 // indirect
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
@ -54,8 +53,10 @@ require (
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/urfave/cli.v1 v1.0.0-00010101000000-000000000000 // indirect
gopkg.in/yaml.v2 v2.2.4
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
)
replace gopkg.in/urfave/cli.v1 => github.com/urfave/cli v1.21.0
replace (
github.com/cosmos/cosmos-sdk v0.34.4-0.20191031200835-02c6c9fafd58 => github.com/chainsafe/cosmos-sdk v0.34.4-0.20191105182341-b5e2a1dfdcf6
github.com/tendermint/tendermint v0.32.7 => github.com/chainsafe/tendermint v0.32.2-0.20191105211315-bd56da568e15
)

25
go.sum
View File

@ -40,6 +40,10 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chainsafe/cosmos-sdk v0.34.4-0.20191105182341-b5e2a1dfdcf6 h1:v3aiYKq2JKNp86QHW53GuyIljwnOm+zF4H9WoQwqir0=
github.com/chainsafe/cosmos-sdk v0.34.4-0.20191105182341-b5e2a1dfdcf6/go.mod h1:jfj68M7UAsSvG+XmGU5zVfSij5fzlOdiQc1NKt+YK2A=
github.com/chainsafe/tendermint v0.32.2-0.20191105211315-bd56da568e15 h1:wGxBiAN4kWM/XfQnOIcnQH6rdzwfMelph+Y5CjqzYcw=
github.com/chainsafe/tendermint v0.32.2-0.20191105211315-bd56da568e15/go.mod h1:zhk6jGOCB5pi7NF8XtP77lqcDZ5n0biPwl2/AyS/vPM=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -48,8 +52,6 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191031200835-02c6c9fafd58 h1:B3fXc6Y9ztj9glE2ANU0+NQJ0M1BXaG5LpnyKFwHW4M=
github.com/cosmos/cosmos-sdk v0.34.4-0.20191031200835-02c6c9fafd58/go.mod h1:PuN72vbZxlorpnHmoIl6RjzVMXEOMejmwORJlZ74K7Q=
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI=
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
@ -115,8 +117,6 @@ github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
@ -308,8 +308,8 @@ github.com/spf13/viper v1.0.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I=
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw=
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
@ -328,6 +328,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stumble/gorocksdb v0.0.3 h1:9UU+QA1pqFYJuf9+5p7z1IqdE5k0mma4UAeu2wmX8kA=
github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYtSWXadLybY=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw=
github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
@ -341,8 +343,6 @@ github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM
github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8=
github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o=
github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU=
github.com/tendermint/tendermint v0.32.7 h1:Szu5Fm1L3pvn3t4uQxPAcP+7ndZEQKgLie/yokM56rU=
github.com/tendermint/tendermint v0.32.7/go.mod h1:D2+A3pNjY+Po72X0mTfaXorFhiVI8dh/Zg640FGyGtE=
github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0=
github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw=
github.com/tendermint/tm-db v0.2.0 h1:rJxgdqn6fIiVJZy4zLpY1qVlyD0TU6vhkT4kEf71TQQ=
@ -352,8 +352,6 @@ github.com/tyler-smith/go-bip39 v1.0.0 h1:FOHg9gaQLeBBRbHE/QrTLfEiBHy5pQ/yXzf9JG
github.com/tyler-smith/go-bip39 v1.0.0/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@ -414,6 +412,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -447,8 +446,8 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -463,6 +462,8 @@ gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHN
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,258 +0,0 @@
package keys
import (
"bufio"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/crypto/keys"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/go-bip39"
"github.com/tendermint/tendermint/libs/cli"
)
const (
flagInteractive = "interactive"
flagRecover = "recover"
flagNoBackup = "no-backup"
// flagDryRun = "dry-run"
flagAccount = "account"
flagIndex = "index"
// flagNoSort = "nosort"
// DefaultKeyPass contains the default key password for genesis transactions
DefaultKeyPass = "12345678"
mnemonicEntropySize = 256
)
func addKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add <name>",
Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to disk",
Long: `Derive a new private key and encrypt to disk.
Optionally specify a BIP39 mnemonic, a BIP39 passphrase to further secure the mnemonic,
and a bip32 HD path to derive a specific account. The key will be stored under the given name
and encrypted with the given password. The only input that is required is the encryption password.
If run with -i, it will prompt the user for BIP44 path, BIP39 mnemonic, and passphrase.
The flag --recover allows one to recover a key from a seed passphrase.
If run with --dry-run, a key would be generated (or recovered) but not stored to the
local keystore.
Use the --pubkey flag to add arbitrary public keys to the keystore for constructing
multisig transactions.
`,
Args: cobra.ExactArgs(1),
RunE: runAddCmd,
}
// cmd.Flags().StringSlice(flagMultisig, nil, "Construct and store a multisig public key (implies --pubkey)")
// cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures. For use in conjunction with --multisig")
// cmd.Flags().Bool(flagNoSort, false, "Keys passed to --multisig are taken in the order they're supplied")
cmd.Flags().String(FlagPublicKey, "", "Parse a public key in bech32 format and save it to disk")
cmd.Flags().BoolP(flagInteractive, "i", false, "Interactively prompt user for BIP39 passphrase and mnemonic")
// cmd.Flags().Bool(flags.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device")
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
// cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore")
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response")
return cmd
}
/*
input
- bip39 mnemonic
- bip39 passphrase
- bip44 path
- local encryption password
output
- armor encrypted private key (saved to file)
*/
func runAddCmd(cmd *cobra.Command, args []string) error {
var kb cosmosKeys.Keybase
var err error
var encryptPassword string
inBuf := bufio.NewReader(cmd.InOrStdin())
name := args[0]
interactive := viper.GetBool(flagInteractive)
showMnemonic := !viper.GetBool(flagNoBackup)
kb, err = NewKeyBaseFromHomeFlag()
if err != nil {
return err
}
_, err = kb.Get(name)
if err == nil {
// account exists, ask for user confirmation
response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf)
if err2 != nil {
return err2
}
if !response {
return errors.New("aborted")
}
}
// ask for a password when generating a local key
if viper.GetString(FlagPublicKey) == "" && !viper.GetBool(flags.FlagUseLedger) {
encryptPassword, err = input.GetCheckPassword(
"Enter a passphrase to encrypt your key to disk:",
"Repeat the passphrase:", inBuf)
if err != nil {
return err
}
}
// }
if viper.GetString(FlagPublicKey) != "" {
pk, err := sdk.GetAccPubKeyBech32(viper.GetString(FlagPublicKey))
if err != nil {
return err
}
_, err = kb.CreateOffline(name, pk)
if err != nil {
return err
}
return nil
}
account := uint32(viper.GetInt(flagAccount))
index := uint32(viper.GetInt(flagIndex))
// // If we're using ledger, only thing we need is the path and the bech32 prefix.
// if viper.GetBool(flags.FlagUseLedger) {
// bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
// info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index)
// if err != nil {
// return err
// }
// return printCreate(cmd, info, false, "")
// }
// Get bip39 mnemonic
var mnemonic string
var bip39Passphrase string
if interactive || viper.GetBool(flagRecover) {
bip39Message := "Enter your bip39 mnemonic"
if !viper.GetBool(flagRecover) {
bip39Message = "Enter your bip39 mnemonic, or hit enter to generate one."
}
mnemonic, err = input.GetString(bip39Message, inBuf)
if err != nil {
return err
}
if !bip39.IsMnemonicValid(mnemonic) {
return errors.New("invalid mnemonic")
}
}
if len(mnemonic) == 0 {
// read entropy seed straight from crypto.Rand and convert to mnemonic
entropySeed, err := bip39.NewEntropy(mnemonicEntropySize)
if err != nil {
return err
}
mnemonic, err = bip39.NewMnemonic(entropySeed[:])
if err != nil {
return err
}
}
// override bip39 passphrase
if interactive {
bip39Passphrase, err = input.GetString(
"Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. "+
"Most users should just hit enter to use the default, \"\"", inBuf)
if err != nil {
return err
}
// if they use one, make them re-enter it
if len(bip39Passphrase) != 0 {
p2, err := input.GetString("Repeat the passphrase:", inBuf)
if err != nil {
return err
}
if bip39Passphrase != p2 {
return errors.New("passphrases don't match")
}
}
}
info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, encryptPassword, account, index)
if err != nil {
return err
}
// Recover key from seed passphrase
if viper.GetBool(flagRecover) {
// Hide mnemonic from output
showMnemonic = false
mnemonic = ""
}
return printCreate(cmd, info, showMnemonic, mnemonic)
}
func printCreate(cmd *cobra.Command, info cosmosKeys.Info, showMnemonic bool, mnemonic string) error {
output := viper.Get(cli.OutputFlag)
switch output {
case clientkeys.OutputFormatText:
cmd.PrintErrln()
printKeyInfo(info, keys.Bech32KeyOutput)
// print mnemonic unless requested not to.
if showMnemonic {
cmd.PrintErrln("\n**Important** write this mnemonic phrase in a safe place.")
cmd.PrintErrln("It is the only way to recover your account if you ever forget your password.")
cmd.PrintErrln("")
cmd.PrintErrln(mnemonic)
}
case clientkeys.OutputFormatJSON:
out, err := keys.Bech32KeyOutput(info)
if err != nil {
return err
}
if showMnemonic {
out.Mnemonic = mnemonic
}
var jsonString []byte
if viper.GetBool(flags.FlagIndentResponse) {
jsonString, err = cdc.MarshalJSONIndent(out, "", " ")
} else {
jsonString, err = cdc.MarshalJSON(out)
}
if err != nil {
return err
}
cmd.PrintErrln(string(jsonString))
default:
return fmt.Errorf("I can't speak: %s", output)
}
return nil
}

View File

@ -1,48 +0,0 @@
package keys
import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/tests"
)
func TestAddCommandBasic(t *testing.T) {
cmd := addKeyCommand()
assert.NotNil(t, cmd)
mockIn, _, _ := tests.ApplyMockIO(cmd)
kbHome, kbCleanUp := tests.NewTestCaseDir(t)
assert.NotNil(t, kbHome)
defer kbCleanUp()
viper.Set(flags.FlagHome, kbHome)
viper.Set(cli.OutputFlag, OutputFormatText)
mockIn.Reset("test1234\ntest1234\n")
err := runAddCmd(cmd, []string{"keyname1"})
assert.NoError(t, err)
viper.Set(cli.OutputFlag, OutputFormatText)
mockIn.Reset("test1234\ntest1234\n")
err = runAddCmd(cmd, []string{"keyname1"})
assert.Error(t, err)
viper.Set(cli.OutputFlag, OutputFormatText)
mockIn.Reset("y\ntest1234\ntest1234\n")
err = runAddCmd(cmd, []string{"keyname1"})
assert.NoError(t, err)
viper.Set(cli.OutputFlag, OutputFormatJSON)
mockIn.Reset("test1234\ntest1234\n")
err = runAddCmd(cmd, []string{"keyname2"})
assert.NoError(t, err)
}

View File

@ -1,23 +0,0 @@
package keys
import (
"github.com/cosmos/cosmos-sdk/codec"
)
var cdc *codec.Codec
func init() {
cdc = codec.New()
codec.RegisterCrypto(cdc)
cdc.Seal()
}
// marshal keys
func MarshalJSON(o interface{}) ([]byte, error) {
return cdc.MarshalJSON(o)
}
// unmarshal json
func UnmarshalJSON(bz []byte, ptr interface{}) error {
return cdc.UnmarshalJSON(bz, ptr)
}

View File

@ -1,34 +0,0 @@
package keys
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/flags"
)
// Commands registers a sub-tree of commands to interact with
// local private key storage.
func Commands() *cobra.Command {
cmd := &cobra.Command{
Use: "emintkeys",
Short: "Add or view local private keys",
Long: `Keys allows you to manage your local keystore for tendermint.
These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that
needs to sign with a private key.`,
}
cmd.AddCommand(
// mnemonicKeyCommand(),
addKeyCommand(),
// exportKeyCommand(),
// importKeyCommand(),
// listKeysCmd(),
showKeysCmd(),
flags.LineBreak,
// deleteKeyCommand(),
// updateKeyCommand(),
// parseKeyStringCommand(),
)
return cmd
}

View File

@ -1,132 +0,0 @@
package keys
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/crypto/keys"
)
const (
// FlagAddress is the flag for the user's address on the command line.
FlagAddress = "address"
// FlagAddress is the flag for the user's address on the command line.
FlagETHAddress = "ethwallet"
// FlagPublicKey represents the user's public key on the command line.
FlagPublicKey = "pubkey"
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
FlagBechPrefix = "bech"
// FlagDevice indicates that the information should be shown in the device
FlagDevice = "device"
)
func showKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show <name>",
Short: "Show key info for the given name",
Long: `Return public details of a single local key.`,
Args: cobra.MinimumNArgs(1),
RunE: runShowCmd,
}
cmd.Flags().String(FlagBechPrefix, sdk.PrefixAccount, "The Bech32 prefix encoding for a key (acc|val|cons)")
cmd.Flags().BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)")
cmd.Flags().BoolP(FlagETHAddress, "w", false, "Output the Ethereum address only (overrides --output)")
cmd.Flags().BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)")
cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in a ledger device")
cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response")
return cmd
}
func runShowCmd(cmd *cobra.Command, args []string) (err error) {
var info cosmosKeys.Info
if len(args) == 1 {
info, err = GetKeyInfo(args[0])
if err != nil {
return err
}
} else {
return errors.New("Must provide only one name")
}
isShowAddr := viper.GetBool(FlagAddress)
isShowEthAddr := viper.GetBool(FlagETHAddress)
isShowPubKey := viper.GetBool(FlagPublicKey)
isShowDevice := viper.GetBool(FlagDevice)
isOutputSet := false
tmp := cmd.Flag(cli.OutputFlag)
if tmp != nil {
isOutputSet = tmp.Changed
}
isShowEitherAddr := isShowAddr || isShowEthAddr
if isShowEitherAddr && isShowPubKey {
return errors.New("cannot get address, with --address or --ethwallet, and --pubkey at once")
}
if isOutputSet && (isShowEitherAddr || isShowPubKey) {
return errors.New("cannot use --output with --address or --pubkey")
}
keyOutputFunction := keys.Bech32KeyOutput
switch {
case isShowAddr:
printKeyAddress(info, keyOutputFunction)
case isShowEthAddr:
printKeyEthAddress(info, keyOutputFunction)
case isShowPubKey:
printPubKey(info, keyOutputFunction)
default:
printKeyInfo(info, keyOutputFunction)
}
if isShowDevice {
if isShowPubKey {
return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys")
}
if viper.GetString(FlagBechPrefix) != "acc" {
return fmt.Errorf("the device flag (-d) can only be used for accounts")
}
// Override and show in the device
if info.GetType() != cosmosKeys.TypeLedger {
return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices")
}
hdpath, err := info.GetPath()
if err != nil {
return nil
}
return crypto.LedgerShowAddress(*hdpath, info.GetPubKey())
}
return nil
}
// TODO: Determine if the different prefixes are necessary for ethermint
// func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
// switch bechPrefix {
// case sdk.PrefixAccount:
// return keys.Bech32KeyOutput, nil
// case sdk.PrefixValidator:
// return keys.Bech32ValKeyOutput, nil
// case sdk.PrefixConsensus:
// return keys.Bech32ConsKeyOutput, nil
// }
// return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
// }

View File

@ -1,51 +0,0 @@
package keys
import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/tests"
)
func TestShowKeysCmd(t *testing.T) {
cmd := showKeysCmd()
assert.NotNil(t, cmd)
assert.Equal(t, "false", cmd.Flag(FlagAddress).DefValue)
assert.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue)
}
func TestRunShowCmd(t *testing.T) {
cmd := showKeysCmd()
err := runShowCmd(cmd, []string{"invalid"})
assert.EqualError(t, err, "Key invalid not found")
// Prepare a key base
// Now add a temporary keybase
kbHome, cleanUp := tests.NewTestCaseDir(t)
defer cleanUp()
viper.Set(flags.FlagHome, kbHome)
fakeKeyName1 := "runShowCmd_Key1"
fakeKeyName2 := "runShowCmd_Key2"
kb, err := NewKeyBaseFromHomeFlag()
assert.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", 0, 0)
assert.NoError(t, err)
_, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", 0, 1)
assert.NoError(t, err)
// // Now try single key
// err = runShowCmd(cmd, []string{fakeKeyName1})
// assert.EqualError(t, err, "invalid Bech32 prefix encoding provided: ")
// // Now try single key - set bech to acc
// viper.Set(FlagBechPrefix, sdk.PrefixAccount)
err = runShowCmd(cmd, []string{fakeKeyName1})
assert.NoError(t, err)
err = runShowCmd(cmd, []string{fakeKeyName2})
assert.NoError(t, err)
}

View File

@ -1,167 +0,0 @@
package keys
import (
"fmt"
"path/filepath"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
"gopkg.in/yaml.v2"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/flags"
cosmosKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
emintKeys "github.com/cosmos/ethermint/crypto/keys"
)
// available output formats.
const (
OutputFormatText = "text"
OutputFormatJSON = "json"
defaultKeyDBName = "emintkeys"
)
type bechKeyOutFn func(keyInfo cosmosKeys.Info) (emintKeys.KeyOutput, error)
// GetKeyInfo returns key info for a given name. An error is returned if the
// keybase cannot be retrieved or getting the info fails.
func GetKeyInfo(name string) (cosmosKeys.Info, error) {
keybase, err := NewKeyBaseFromHomeFlag()
if err != nil {
return nil, err
}
return keybase.Get(name)
}
// NewKeyBaseFromHomeFlag initializes a Keybase based on the configuration.
func NewKeyBaseFromHomeFlag() (cosmosKeys.Keybase, error) {
rootDir := viper.GetString(flags.FlagHome)
return NewKeyBaseFromDir(rootDir)
}
// NewKeyBaseFromDir initializes a keybase at a particular dir.
func NewKeyBaseFromDir(rootDir string) (cosmosKeys.Keybase, error) {
return getLazyKeyBaseFromDir(rootDir)
}
// NewInMemoryKeyBase returns a storage-less keybase.
func NewInMemoryKeyBase() cosmosKeys.Keybase { return emintKeys.NewInMemory() }
func getLazyKeyBaseFromDir(rootDir string) (cosmosKeys.Keybase, error) {
return emintKeys.New(defaultKeyDBName, filepath.Join(rootDir, defaultKeyDBName)), nil
}
// GetPassphrase returns a passphrase for a given name. It will first retrieve
// the key info for that name if the type is local, it'll fetch input from
// STDIN. Otherwise, an empty passphrase is returned. An error is returned if
// the key info cannot be fetched or reading from STDIN fails.
func GetPassphrase(name string) (string, error) {
var passphrase string
keyInfo, err := GetKeyInfo(name)
if err != nil {
return passphrase, err
}
// we only need a passphrase for locally stored keys
if keyInfo.GetType().String() == emintKeys.TypeLocal.String() {
passphrase, err = clientkeys.ReadPassphraseFromStdin(name)
if err != nil {
return passphrase, err
}
}
return passphrase, nil
}
func printKeyInfo(keyInfo cosmosKeys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(keyInfo)
if err != nil {
panic(err)
}
switch viper.Get(cli.OutputFlag) {
case OutputFormatText:
printTextInfos([]emintKeys.KeyOutput{ko})
case OutputFormatJSON:
var out []byte
var err error
if viper.GetBool(flags.FlagIndentResponse) {
out, err = cdc.MarshalJSONIndent(ko, "", " ")
} else {
out, err = cdc.MarshalJSON(ko)
}
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
}
// func printInfos(infos []keys.Info) {
// kos, err := keys.Bech32KeysOutput(infos)
// if err != nil {
// panic(err)
// }
// switch viper.Get(cli.OutputFlag) {
// case OutputFormatText:
// printTextInfos(kos)
// case OutputFormatJSON:
// var out []byte
// var err error
// if viper.GetBool(flags.FlagIndentResponse) {
// out, err = cdc.MarshalJSONIndent(kos, "", " ")
// } else {
// out, err = cdc.MarshalJSON(kos)
// }
// if err != nil {
// panic(err)
// }
// fmt.Printf("%s", out)
// }
// }
func printTextInfos(kos []emintKeys.KeyOutput) {
out, err := yaml.Marshal(&kos)
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
func printKeyAddress(info cosmosKeys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {
panic(err)
}
fmt.Println(ko.Address)
}
func printKeyEthAddress(info cosmosKeys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {
panic(err)
}
fmt.Println(ko.ETHAddress)
}
func printPubKey(info cosmosKeys.Info, bechKeyOut bechKeyOutFn) {
ko, err := bechKeyOut(info)
if err != nil {
panic(err)
}
fmt.Println(ko.PubKey)
}

View File

@ -4,10 +4,10 @@ import (
"fmt"
"github.com/cosmos/cosmos-sdk/client/flags"
emintkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/codec"
emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
"github.com/ethereum/go-ethereum/rpc"
"github.com/spf13/cobra"

View File

@ -8,8 +8,8 @@ import (
"strconv"
"sync"
"github.com/cosmos/cosmos-sdk/client/keys"
emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
params "github.com/cosmos/ethermint/rpc/args"
emint "github.com/cosmos/ethermint/types"
etypes "github.com/cosmos/ethermint/types"
@ -108,7 +108,7 @@ func (e *PublicEthAPI) Accounts() ([]common.Address, error) {
e.keybaseLock.Lock()
addresses := make([]common.Address, 0) // return [] instead of nil if empty
keybase, err := emintkeys.NewKeyBaseFromHomeFlag()
keybase, err := keys.NewKeyBaseFromHomeFlag()
if err != nil {
return addresses, err
}

View File

@ -6,6 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
ethcmn "github.com/ethereum/go-ethereum/common"
)
@ -23,6 +24,10 @@ const (
// Main Ethermint account
// ----------------------------------------------------------------------------
func init() {
authtypes.RegisterAccountTypeCodec(Account{}, EthermintAccountName)
}
// Account implements the auth.Account interface and embeds an
// auth.BaseAccount type. It is compatible with the auth.AccountMapper.
type Account struct {

View File

@ -10,8 +10,13 @@ func init() {
RegisterCodec(typesCodec)
}
const (
// Amino encoding name
EthermintAccountName = "emint/Account"
)
// RegisterCodec registers all the necessary types with amino for the given
// codec.
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(&Account{}, "types/Account", nil)
cdc.RegisterConcrete(&Account{}, EthermintAccountName, nil)
}

View File

@ -7,13 +7,13 @@ import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
emintkeys "github.com/cosmos/ethermint/keys"
emintTypes "github.com/cosmos/ethermint/types"
emintUtils "github.com/cosmos/ethermint/x/evm/client/utils"
"github.com/cosmos/ethermint/x/evm/types"
ethcmn "github.com/ethereum/go-ethereum/common"
@ -30,9 +30,8 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
}
evmTxCmd.AddCommand(client.PostCommands(
// TODO: Add back generating cosmos tx for Ethereum tx message
// GetCmdGenTx(cdc),
GetCmdGenETHTx(cdc),
// TODO: Add back generating cosmos tx for Ethereum tx message
// GetCmdGenTx(cdc),
)...)
return evmTxCmd
@ -46,11 +45,11 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
Args: cobra.ExactArgs(5),
RunE: func(cmd *cobra.Command, args []string) error {
// TODO: remove inputs and infer based on StdTx
cliCtx := emintUtils.NewETHCLIContext().WithCodec(cdc)
cliCtx := context.NewCLIContext().WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
kb, err := emintkeys.NewKeyBaseFromHomeFlag()
kb, err := keys.NewKeyBaseFromHomeFlag()
if err != nil {
panic(err)
}
@ -80,63 +79,7 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
}
// TODO: possibly overwrite gas values in txBldr
return emintUtils.GenerateOrBroadcastETHMsgs(cliCtx, txBldr.WithKeybase(kb), []sdk.Msg{msg})
},
}
}
// GetCmdGenTx generates an ethereum transaction
func GetCmdGenETHTx(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "generate-eth-tx [amount] [gaslimit] [gasprice] [payload] [<ethereum-address>]",
Short: "generate and broadcast an Ethereum tx. If address is not specified, contract will be created",
Args: cobra.RangeArgs(4, 5),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := emintUtils.NewETHCLIContext().WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
kb, err := emintkeys.NewKeyBaseFromHomeFlag()
if err != nil {
panic(err)
}
coins, err := sdk.ParseCoins(args[0])
if err != nil {
return err
}
gasLimit, err := strconv.ParseUint(args[1], 0, 64)
if err != nil {
return err
}
gasPrice, err := strconv.ParseUint(args[2], 0, 64)
if err != nil {
return err
}
payload := args[3]
txBldr, err = utils.PrepareTxBuilder(txBldr, cliCtx)
if err != nil {
return err
}
var tx *types.EthereumTxMsg
if len(args) == 5 {
addr := ethcmn.HexToAddress(args[4])
tx = types.NewEthereumTxMsg(txBldr.Sequence(), &addr, big.NewInt(coins.AmountOf(emintTypes.DenomDefault).Int64()), gasLimit, new(big.Int).SetUint64(gasPrice), []byte(payload))
} else {
tx = types.NewEthereumTxMsgContract(txBldr.Sequence(), big.NewInt(coins.AmountOf(emintTypes.DenomDefault).Int64()), gasLimit, new(big.Int).SetUint64(gasPrice), []byte(payload))
}
err = tx.ValidateBasic()
if err != nil {
return err
}
return emintUtils.BroadcastETHTx(cliCtx, txBldr.WithKeybase(kb), tx)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr.WithKeybase(kb), []sdk.Msg{msg})
},
}
}

View File

@ -1,344 +0,0 @@
package utils
import (
"bufio"
"fmt"
"math/big"
"os"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
emintcrypto "github.com/cosmos/ethermint/crypto"
emintkeys "github.com/cosmos/ethermint/keys"
emint "github.com/cosmos/ethermint/types"
evmtypes "github.com/cosmos/ethermint/x/evm/types"
"github.com/tendermint/tendermint/libs/cli"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
// * Code from this file is a modified version of cosmos-sdk/auth/client/utils/tx.go
// * to allow for using the Ethermint keybase for signing the transaction
// GenerateOrBroadcastMsgs creates a StdTx given a series of messages. If
// the provided context has generate-only enabled, the tx will only be printed
// to STDOUT in a fully offline manner. Otherwise, the tx will be signed and
// broadcasted.
func GenerateOrBroadcastETHMsgs(cliCtx context.CLIContext, txBldr authtypes.TxBuilder, msgs []sdk.Msg) error {
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, msgs)
}
return completeAndBroadcastETHTxCLI(txBldr, cliCtx, msgs)
}
// BroadcastETHTx Broadcasts an Ethereum Tx not wrapped in a Std Tx
func BroadcastETHTx(cliCtx context.CLIContext, txBldr authtypes.TxBuilder, tx *evmtypes.EthereumTxMsg) error {
fromName := cliCtx.GetFromName()
passphrase, err := emintkeys.GetPassphrase(fromName)
if err != nil {
return err
}
// Sign V, R, S fields for tx
ethTx, err := signEthTx(txBldr.Keybase(), fromName, passphrase, tx, txBldr.ChainID())
if err != nil {
return err
}
// Use default Tx Encoder since it will just be broadcasted to TM node at this point
txEncoder := txBldr.TxEncoder()
txBytes, err := txEncoder(ethTx)
if err != nil {
return err
}
// broadcast to a Tendermint node
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
return err
}
return cliCtx.PrintOutput(res)
}
// completeAndBroadcastETHTxCLI implements a utility function that facilitates
// sending a series of messages in a signed transaction given a TxBuilder and a
// QueryContext. It ensures that the account exists, has a proper number and
// sequence set. In addition, it builds and signs a transaction with the
// supplied messages. Finally, it broadcasts the signed transaction to a node.
// * Modified version from github.com/cosmos/cosmos-sdk/x/auth/client/utils/tx.go
func completeAndBroadcastETHTxCLI(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
txBldr, err := utils.PrepareTxBuilder(txBldr, cliCtx)
if err != nil {
return err
}
fromName := cliCtx.GetFromName()
if txBldr.SimulateAndExecute() || cliCtx.Simulate {
txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, msgs)
if err != nil {
return err
}
gasEst := utils.GasEstimateResponse{GasEstimate: txBldr.Gas()}
_, _ = fmt.Fprintf(os.Stderr, "%s\n", gasEst.String())
}
if cliCtx.Simulate {
return nil
}
if !cliCtx.SkipConfirm {
stdSignMsg, err := txBldr.BuildSignMsg(msgs)
if err != nil {
return err
}
var json []byte
if viper.GetBool(flags.FlagIndentResponse) {
json, err = cliCtx.Codec.MarshalJSONIndent(stdSignMsg, "", " ")
if err != nil {
panic(err)
}
} else {
json = cliCtx.Codec.MustMarshalJSON(stdSignMsg)
}
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json)
buf := bufio.NewReader(os.Stdin)
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf)
if err != nil || !ok {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
return err
}
}
// * This function is overridden to change the keybase reference here
passphrase, err := emintkeys.GetPassphrase(fromName)
if err != nil {
return err
}
// build and sign the transaction
// * needed to be modified also to change how the data is signed
txBytes, err := buildAndSign(txBldr, fromName, passphrase, msgs)
if err != nil {
return err
}
// broadcast to a Tendermint node
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
return err
}
return cliCtx.PrintOutput(res)
}
// BuildAndSign builds a single message to be signed, and signs a transaction
// with the built message given a name, passphrase, and a set of messages.
// * overridden from github.com/cosmos/cosmos-sdk/x/auth/types/txbuilder.go
// * This is just modified to change the functionality in makeSignature, through sign
func buildAndSign(bldr authtypes.TxBuilder, name, passphrase string, msgs []sdk.Msg) ([]byte, error) {
msg, err := bldr.BuildSignMsg(msgs)
if err != nil {
return nil, err
}
return sign(bldr, name, passphrase, msg)
}
// Sign signs a transaction given a name, passphrase, and a single message to
// signed. An error is returned if signing fails.
func sign(bldr authtypes.TxBuilder, name, passphrase string, msg authtypes.StdSignMsg) ([]byte, error) {
sig, err := makeSignature(bldr.Keybase(), name, passphrase, msg)
if err != nil {
return nil, err
}
txEncoder := bldr.TxEncoder()
return txEncoder(authtypes.NewStdTx(msg.Msgs, msg.Fee, []authtypes.StdSignature{sig}, msg.Memo))
}
// MakeSignature builds a StdSignature given keybase, key name, passphrase, and a StdSignMsg.
func makeSignature(keybase crkeys.Keybase, name, passphrase string,
msg authtypes.StdSignMsg) (sig authtypes.StdSignature, err error) {
if keybase == nil {
// * This is overridden to allow ethermint keys, but not used because keybase is set
keybase, err = emintkeys.NewKeyBaseFromHomeFlag()
if err != nil {
return
}
}
// EthereumTxMsg always returns the data in the 0th index so it is safe to do this
var ethTx *evmtypes.EthereumTxMsg
ethTx, ok := msg.Msgs[0].(*evmtypes.EthereumTxMsg)
if !ok {
return sig, fmt.Errorf("Transaction message not an Ethereum Tx")
}
// TODO: Move this logic to after tx is rlp decoded in keybase Sign function
// parse the chainID from a string to a base-10 integer
chainID, ok := new(big.Int).SetString(msg.ChainID, 10)
if !ok {
return sig, emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", msg.ChainID))
}
privKey, err := keybase.ExportPrivateKeyObject(name, passphrase)
if err != nil {
return
}
emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey))
}
ethTx.Sign(chainID, emintKey.ToECDSA())
// * This is needed to be overridden to get bytes to sign (RLPSignBytes) with the chainID
sigBytes, pubkey, err := keybase.Sign(name, passphrase, ethTx.RLPSignBytes(chainID).Bytes())
if err != nil {
return
}
return authtypes.StdSignature{
PubKey: pubkey,
Signature: sigBytes,
}, nil
}
// signEthTx populates the V, R, and S fields of an EthereumTxMsg using an ethermint key
func signEthTx(keybase crkeys.Keybase, name, passphrase string,
ethTx *evmtypes.EthereumTxMsg, chainID string) (_ *evmtypes.EthereumTxMsg, err error) {
if keybase == nil {
keybase, err = emintkeys.NewKeyBaseFromHomeFlag()
if err != nil {
return
}
}
// parse the chainID from a string to a base-10 integer
intChainID, ok := new(big.Int).SetString(chainID, 10)
if !ok {
return ethTx, emint.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", chainID))
}
privKey, err := keybase.ExportPrivateKeyObject(name, passphrase)
if err != nil {
return
}
// Key must be a ethermint key to be able to be converted into an ECDSA private key to sign
emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
panic(fmt.Sprintf("invalid private key type: %T", privKey))
}
ethTx.Sign(intChainID, emintKey.ToECDSA())
return ethTx, err
}
// * This context is needed because the previous GetFromFields function would initialize a
// * default keybase to lookup the address or name. The new one overrides the keybase with the
// * ethereum compatible one
// NewCLIContextWithFrom returns a new initialized CLIContext with parameters from the
// command line using Viper. It takes a key name or address and populates the FromName and
// FromAddress field accordingly.
func NewETHCLIContext() context.CLIContext {
var nodeURI string
var rpc rpcclient.Client
from := viper.GetString(flags.FlagFrom)
genOnly := viper.GetBool(flags.FlagGenerateOnly)
// * This function is needed only to override this call to access correct keybase
fromAddress, fromName, err := getFromFields(from, genOnly)
if err != nil {
fmt.Printf("failed to get from fields: %v", err)
os.Exit(1)
}
if !genOnly {
nodeURI = viper.GetString(flags.FlagNode)
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
}
return context.CLIContext{
Client: rpc,
Output: os.Stdout,
NodeURI: nodeURI,
From: viper.GetString(flags.FlagFrom),
OutputFormat: viper.GetString(cli.OutputFlag),
Height: viper.GetInt64(flags.FlagHeight),
TrustNode: viper.GetBool(flags.FlagTrustNode),
UseLedger: viper.GetBool(flags.FlagUseLedger),
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
// Verifier: verifier,
Simulate: viper.GetBool(flags.FlagDryRun),
GenerateOnly: genOnly,
FromAddress: fromAddress,
FromName: fromName,
Indent: viper.GetBool(flags.FlagIndentResponse),
SkipConfirm: viper.GetBool(flags.FlagSkipConfirmation),
}
}
// GetFromFields returns a from account address and Keybase name given either
// an address or key name. If genOnly is true, only a valid Bech32 cosmos
// address is returned.
func getFromFields(from string, genOnly bool) (sdk.AccAddress, string, error) {
if from == "" {
return nil, "", nil
}
if genOnly {
addr, err := sdk.AccAddressFromBech32(from)
if err != nil {
return nil, "", errors.Wrap(err, "must provide a valid Bech32 address for generate-only")
}
return addr, "", nil
}
// * This is the line that needed to be overridden, change could be to pass in optional keybase?
keybase, err := emintkeys.NewKeyBaseFromHomeFlag()
if err != nil {
return nil, "", err
}
var info crkeys.Info
if addr, err := sdk.AccAddressFromBech32(from); err == nil {
info, err = keybase.GetByAddress(addr)
if err != nil {
return nil, "", err
}
} else {
info, err = keybase.Get(from)
if err != nil {
return nil, "", err
}
}
return info.GetAddress(), info.GetName(), nil
}

View File

@ -2,7 +2,6 @@ package types
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/ethermint/crypto"
)
// ModuleCdc defines the codec to be used by evm module
@ -21,5 +20,4 @@ func init() {
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil)
cdc.RegisterConcrete(&EmintMsg{}, "ethermint/MsgEmint", nil)
crypto.RegisterCodec(cdc)
}