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
30 changed files with 137 additions and 2542 deletions
+7 -64
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})
},
}
}
-344
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
}
-2
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)
}