Migrates gentx command to application (#144)
* Migrates gentx command to application for updated keybase * Remove cosmos keys commands * Fix genutil codec for key type * Remove relevant TODOs
This commit is contained in:
parent
97f73063a5
commit
021c9f440a
@ -170,13 +170,12 @@ func TestSDKInvalidAcc(t *testing.T) {
|
||||
tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
|
||||
// TODO: Reenable broken test when fixed inside cosmos SDK
|
||||
// // require validation failure with invalid sequence (nonce)
|
||||
// accNums = []uint64{acc1.GetAccountNumber()}
|
||||
// accSeqs = []uint64{1}
|
||||
// require validation failure with invalid sequence (nonce)
|
||||
accNums = []uint64{acc1.GetAccountNumber()}
|
||||
accSeqs = []uint64{1}
|
||||
|
||||
// tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
// requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
}
|
||||
|
||||
func TestEthInvalidSig(t *testing.T) {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/ethermint/rpc"
|
||||
"github.com/tendermint/go-amino"
|
||||
|
||||
@ -32,7 +31,6 @@ func main() {
|
||||
|
||||
// Read in the configuration file for the sdk
|
||||
config := sdk.GetConfig()
|
||||
// TODO: Remove or change prefix if usable to generate Ethereum address
|
||||
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||
@ -58,8 +56,6 @@ func main() {
|
||||
// TODO: Set up rest routes (if included, different from web3 api)
|
||||
rpc.Web3RpcCmd(cdc),
|
||||
client.LineBreak,
|
||||
// TODO: Remove these commands once ethermint keys and genesis set up
|
||||
keys.Commands(),
|
||||
emintkeys.Commands(),
|
||||
client.LineBreak,
|
||||
)
|
||||
@ -78,7 +74,6 @@ func queryCmd(cdc *amino.Codec) *cobra.Command {
|
||||
Short: "Querying subcommands",
|
||||
}
|
||||
|
||||
// TODO: Possibly add these query commands from other modules
|
||||
queryCmd.AddCommand(
|
||||
authcmd.GetAccountCmd(cdc),
|
||||
client.LineBreak,
|
||||
|
302
cmd/emintd/gentx.go
Normal file
302
cmd/emintd/gentx.go
Normal file
@ -0,0 +1,302 @@
|
||||
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
|
||||
}
|
@ -15,6 +15,7 @@ import (
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -36,10 +37,10 @@ func main() {
|
||||
cdc := emintapp.MakeCodec()
|
||||
|
||||
genutil.ModuleCdc = cdc
|
||||
genutiltypes.ModuleCdc = cdc
|
||||
authtypes.ModuleCdc = cdc
|
||||
|
||||
config := sdk.GetConfig()
|
||||
// TODO: Remove or change prefix if usable to generate Ethereum address
|
||||
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||
@ -56,7 +57,7 @@ func main() {
|
||||
rootCmd.AddCommand(
|
||||
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, emintapp.ModuleBasics, emintapp.DefaultNodeHome)),
|
||||
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, emintapp.DefaultNodeHome),
|
||||
genutilcli.GenTxCmd(
|
||||
GenTxCmd(
|
||||
ctx, cdc, emintapp.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{}, emintapp.DefaultNodeHome, emintapp.DefaultCLIHome,
|
||||
),
|
||||
genutilcli.ValidateGenesisCmd(ctx, cdc, emintapp.ModuleBasics),
|
||||
|
1
go.mod
1
go.mod
@ -35,6 +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/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 // indirect
|
||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
|
||||
|
Loading…
Reference in New Issue
Block a user