Alessio/server refactor (#2472)
* Add arg to PrintUnsignedStdTx() to actually operate in offline mode
* WIP simplifying server module
* Expose ExternalIP()
* Move {GenTx,Init}Cmd into gaia's new init package
This commit is contained in:
committed by
Jae Kwon
parent
e251088672
commit
0f4a03b44e
+17
-60
@@ -13,72 +13,29 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// AppCreator reflects a function that allows us to lazily initialize an
|
||||
// AppCreator is a function that allows us to lazily initialize an
|
||||
// application using various configurations.
|
||||
AppCreator func(home string, logger log.Logger, traceStore string) (abci.Application, error)
|
||||
AppCreator func(log.Logger, dbm.DB, io.Writer) abci.Application
|
||||
|
||||
// AppExporter reflects a function that dumps all app state to
|
||||
// AppExporter is a function that dumps all app state to
|
||||
// JSON-serializable structure and returns the current validator set.
|
||||
AppExporter func(home string, logger log.Logger, traceStore string) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
|
||||
// AppCreatorInit reflects a function that performs initialization of an
|
||||
// AppCreator.
|
||||
AppCreatorInit func(log.Logger, dbm.DB, io.Writer) abci.Application
|
||||
|
||||
// AppExporterInit reflects a function that performs initialization of an
|
||||
// AppExporter.
|
||||
AppExporterInit func(log.Logger, dbm.DB, io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
AppExporter func(log.Logger, dbm.DB, io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
)
|
||||
|
||||
// ConstructAppCreator returns an application generation function.
|
||||
func ConstructAppCreator(appFn AppCreatorInit, name string) AppCreator {
|
||||
return func(rootDir string, logger log.Logger, traceStore string) (abci.Application, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
|
||||
db, err := dbm.NewGoLevelDB(name, dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var traceStoreWriter io.Writer
|
||||
if traceStore != "" {
|
||||
traceStoreWriter, err = os.OpenFile(
|
||||
traceStore,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
app := appFn(logger, db, traceStoreWriter)
|
||||
return app, nil
|
||||
}
|
||||
func openDB(rootDir string) (dbm.DB, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
db, err := dbm.NewGoLevelDB("application", dataDir)
|
||||
return db, err
|
||||
}
|
||||
|
||||
// ConstructAppExporter returns an application export function.
|
||||
func ConstructAppExporter(appFn AppExporterInit, name string) AppExporter {
|
||||
return func(rootDir string, logger log.Logger, traceStore string) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
|
||||
db, err := dbm.NewGoLevelDB(name, dataDir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var traceStoreWriter io.Writer
|
||||
if traceStore != "" {
|
||||
traceStoreWriter, err = os.OpenFile(
|
||||
traceStore,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return appFn(logger, db, traceStoreWriter)
|
||||
func openTraceWriter(traceWriterFile string) (w io.Writer, err error) {
|
||||
if traceWriterFile != "" {
|
||||
w, err = os.OpenFile(
|
||||
traceWriterFile,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+10
-2
@@ -20,7 +20,7 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C
|
||||
Short: "Export state to JSON",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
home := viper.GetString("home")
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
traceWriterFile := viper.GetString(flagTraceStore)
|
||||
emptyState, err := isEmptyState(home)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -37,7 +37,15 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C
|
||||
return nil
|
||||
}
|
||||
|
||||
appState, validators, err := appExporter(home, ctx.Logger, traceStore)
|
||||
db, err := openDB(home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
traceWriter, err := openTraceWriter(traceWriterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appState, validators, err := appExporter(ctx.Logger, db, traceWriter)
|
||||
if err != nil {
|
||||
return errors.Errorf("error exporting state: %v\n", err)
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmptyState(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
logger := log.NewNopLogger()
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
cdc := codec.New()
|
||||
appInit := AppInit{
|
||||
AppGenTx: mock.AppGenTx,
|
||||
AppGenState: mock.AppGenStateEmpty,
|
||||
}
|
||||
cmd := InitCmd(ctx, cdc, appInit)
|
||||
err = cmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
old := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
cmd = ExportCmd(ctx, cdc, nil)
|
||||
err = cmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
outC := make(chan string)
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, r)
|
||||
outC <- buf.String()
|
||||
}()
|
||||
|
||||
w.Close()
|
||||
os.Stdout = old
|
||||
out := <-outC
|
||||
require.Contains(t, out, "WARNING: State is not initialized")
|
||||
require.Contains(t, out, "genesis_time")
|
||||
require.Contains(t, out, "chain_id")
|
||||
require.Contains(t, out, "consensus_params")
|
||||
require.Contains(t, out, "validators")
|
||||
require.Contains(t, out, "app_hash")
|
||||
}
|
||||
+2
-311
@@ -3,26 +3,12 @@ package server
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
tmcli "github.com/tendermint/tendermint/libs/cli"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
clkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
@@ -62,303 +48,8 @@ type InitConfig struct {
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
// get cmd to initialize all files for tendermint and application
|
||||
func GenTxCmd(ctx *Context, cdc *codec.Codec, appInit AppInit) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "gen-tx",
|
||||
Short: "Create genesis transaction file (under [--home]/config/gentx/gentx-[nodeID].json)",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
|
||||
config := ctx.Config
|
||||
config.SetRoot(viper.GetString(tmcli.HomeFlag))
|
||||
|
||||
ip := viper.GetString(FlagIP)
|
||||
if len(ip) == 0 {
|
||||
eip, err := externalIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip = eip
|
||||
}
|
||||
|
||||
genTxConfig := serverconfig.GenTx{
|
||||
viper.GetString(FlagName),
|
||||
viper.GetString(FlagClientHome),
|
||||
viper.GetBool(FlagOWK),
|
||||
ip,
|
||||
}
|
||||
cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toPrint := struct {
|
||||
AppMessage json.RawMessage `json:"app_message"`
|
||||
GenTxFile json.RawMessage `json:"gen_tx_file"`
|
||||
}{
|
||||
cliPrint,
|
||||
genTxFile,
|
||||
}
|
||||
out, err := codec.MarshalJSONIndent(cdc, toPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().String(FlagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine")
|
||||
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func gentxWithConfig(cdc *codec.Codec, appInit AppInit, config *cfg.Config, genTxConfig serverconfig.GenTx) (
|
||||
cliPrint json.RawMessage, genTxFile json.RawMessage, err error) {
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nodeID := string(nodeKey.ID())
|
||||
pubKey := readOrCreatePrivValidator(config)
|
||||
|
||||
appGenTx, cliPrint, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tx := GenesisTx{
|
||||
NodeID: nodeID,
|
||||
IP: genTxConfig.IP,
|
||||
Validator: validator,
|
||||
AppGenTx: appGenTx,
|
||||
}
|
||||
bz, err := codec.MarshalJSONIndent(cdc, tx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
genTxFile = json.RawMessage(bz)
|
||||
name := fmt.Sprintf("gentx-%v.json", nodeID)
|
||||
writePath := filepath.Join(config.RootDir, "config", "gentx")
|
||||
file := filepath.Join(writePath, name)
|
||||
err = cmn.EnsureDir(writePath, 0700)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = cmn.WriteFile(file, bz, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Write updated config with moniker
|
||||
config.Moniker = genTxConfig.Name
|
||||
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
|
||||
cfg.WriteConfigFile(configFilePath, config)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// get cmd to initialize all files for tendermint and application
|
||||
func InitCmd(ctx *Context, cdc *codec.Codec, appInit AppInit) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
|
||||
config := ctx.Config
|
||||
config.SetRoot(viper.GetString(tmcli.HomeFlag))
|
||||
initConfig := InitConfig{
|
||||
viper.GetString(FlagChainID),
|
||||
viper.GetBool(FlagWithTxs),
|
||||
filepath.Join(config.RootDir, "config", "gentx"),
|
||||
viper.GetBool(FlagOverwrite),
|
||||
}
|
||||
|
||||
chainID, nodeID, appMessage, err := initWithConfig(cdc, appInit, config, initConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// print out some key information
|
||||
toPrint := struct {
|
||||
ChainID string `json:"chain_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
AppMessage json.RawMessage `json:"app_message"`
|
||||
}{
|
||||
chainID,
|
||||
nodeID,
|
||||
appMessage,
|
||||
}
|
||||
out, err := codec.MarshalJSONIndent(cdc, toPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file")
|
||||
cmd.Flags().String(FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
|
||||
cmd.Flags().Bool(FlagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/")
|
||||
cmd.Flags().AddFlagSet(appInit.FlagsAppGenState)
|
||||
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx) // need to add this flagset for when no GenTx's provided
|
||||
cmd.AddCommand(GenTxCmd(ctx, cdc, appInit))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func initWithConfig(cdc *codec.Codec, appInit AppInit, config *cfg.Config, initConfig InitConfig) (
|
||||
chainID string, nodeID string, appMessage json.RawMessage, err error) {
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nodeID = string(nodeKey.ID())
|
||||
pubKey := readOrCreatePrivValidator(config)
|
||||
|
||||
if initConfig.ChainID == "" {
|
||||
initConfig.ChainID = fmt.Sprintf("test-chain-%v", cmn.RandStr(6))
|
||||
}
|
||||
chainID = initConfig.ChainID
|
||||
|
||||
genFile := config.GenesisFile()
|
||||
if !initConfig.Overwrite && cmn.FileExists(genFile) {
|
||||
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
|
||||
return
|
||||
}
|
||||
|
||||
// process genesis transactions, or otherwise create one for defaults
|
||||
var appGenTxs []json.RawMessage
|
||||
var validators []tmtypes.GenesisValidator
|
||||
var persistentPeers string
|
||||
|
||||
if initConfig.GenTxs {
|
||||
validators, appGenTxs, persistentPeers, err = processGenTxs(initConfig.GenTxsDir, cdc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config.P2P.PersistentPeers = persistentPeers
|
||||
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
|
||||
cfg.WriteConfigFile(configFilePath, config)
|
||||
} else {
|
||||
genTxConfig := serverconfig.GenTx{
|
||||
viper.GetString(FlagName),
|
||||
viper.GetString(FlagClientHome),
|
||||
viper.GetBool(FlagOWK),
|
||||
"127.0.0.1",
|
||||
}
|
||||
|
||||
// Write updated config with moniker
|
||||
config.Moniker = genTxConfig.Name
|
||||
configFilePath := filepath.Join(config.RootDir, "config", "config.toml")
|
||||
cfg.WriteConfigFile(configFilePath, config)
|
||||
appGenTx, am, validator, err := appInit.AppGenTx(cdc, pubKey, genTxConfig)
|
||||
appMessage = am
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
validators = []tmtypes.GenesisValidator{validator}
|
||||
appGenTxs = []json.RawMessage{appGenTx}
|
||||
}
|
||||
|
||||
appState, err := appInit.AppGenState(cdc, appGenTxs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = writeGenesisFile(cdc, genFile, initConfig.ChainID, validators, appState)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// append a genesis-piece
|
||||
func processGenTxs(genTxsDir string, cdc *codec.Codec) (
|
||||
validators []tmtypes.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) {
|
||||
|
||||
var fos []os.FileInfo
|
||||
fos, err = ioutil.ReadDir(genTxsDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
genTxs := make(map[string]GenesisTx)
|
||||
var nodeIDs []string
|
||||
for _, fo := range fos {
|
||||
filename := path.Join(genTxsDir, fo.Name())
|
||||
if !fo.IsDir() && (path.Ext(filename) != ".json") {
|
||||
continue
|
||||
}
|
||||
|
||||
// get the genTx
|
||||
var bz []byte
|
||||
bz, err = ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var genTx GenesisTx
|
||||
err = cdc.UnmarshalJSON(bz, &genTx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
genTxs[genTx.NodeID] = genTx
|
||||
nodeIDs = append(nodeIDs, genTx.NodeID)
|
||||
}
|
||||
|
||||
sort.Strings(nodeIDs)
|
||||
|
||||
for _, nodeID := range nodeIDs {
|
||||
genTx := genTxs[nodeID]
|
||||
|
||||
// combine some stuff
|
||||
validators = append(validators, genTx.Validator)
|
||||
appGenTxs = append(appGenTxs, genTx.AppGenTx)
|
||||
|
||||
// Add a persistent peer
|
||||
comma := ","
|
||||
if len(persistentPeers) == 0 {
|
||||
comma = ""
|
||||
}
|
||||
persistentPeers += fmt.Sprintf("%s%s@%s:26656", comma, genTx.NodeID, genTx.IP)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//________________________________________________________________________________________
|
||||
|
||||
// read of create the private key file for this config
|
||||
func readOrCreatePrivValidator(tmConfig *cfg.Config) crypto.PubKey {
|
||||
// private validator
|
||||
privValFile := tmConfig.PrivValidatorFile()
|
||||
var privValidator *pvm.FilePV
|
||||
if cmn.FileExists(privValFile) {
|
||||
privValidator = pvm.LoadFilePV(privValFile)
|
||||
} else {
|
||||
privValidator = pvm.GenFilePV(privValFile)
|
||||
privValidator.Save()
|
||||
}
|
||||
return privValidator.GetPubKey()
|
||||
}
|
||||
|
||||
// writeGenesisFile creates and writes the genesis configuration to disk. An
|
||||
// error is returned if building or writing the configuration to file fails.
|
||||
// nolint: unparam
|
||||
func writeGenesisFile(cdc *codec.Codec, genesisFile, chainID string, validators []tmtypes.GenesisValidator, appState json.RawMessage) error {
|
||||
genDoc := tmtypes.GenesisDoc{
|
||||
ChainID: chainID,
|
||||
Validators: validators,
|
||||
AppState: appState,
|
||||
}
|
||||
|
||||
if err := genDoc.ValidateAndComplete(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return genDoc.SaveAs(genesisFile)
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// Core functionality passed from the application to the server init command
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
)
|
||||
|
||||
// TODO update
|
||||
func TestInitCmd(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
cdc := codec.New()
|
||||
appInit := AppInit{
|
||||
AppGenState: mock.AppGenState,
|
||||
AppGenTx: mock.AppGenTx,
|
||||
}
|
||||
cmd := InitCmd(ctx, cdc, appInit)
|
||||
err = cmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGenTxCmd(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestTestnetFilesCmd(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestSimpleAppGenTx(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestSimpleAppGenState(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
+16
-4
@@ -57,12 +57,18 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
||||
func startStandAlone(ctx *Context, appCreator AppCreator) error {
|
||||
addr := viper.GetString(flagAddress)
|
||||
home := viper.GetString("home")
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
traceWriterFile := viper.GetString(flagTraceStore)
|
||||
|
||||
app, err := appCreator(home, ctx.Logger, traceStore)
|
||||
db, err := openDB(home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
traceWriter, err := openTraceWriter(traceWriterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app := appCreator(ctx.Logger, db, traceWriter)
|
||||
|
||||
svr, err := server.NewServer(addr, "socket", app)
|
||||
if err != nil {
|
||||
@@ -91,12 +97,18 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error {
|
||||
func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) {
|
||||
cfg := ctx.Config
|
||||
home := cfg.RootDir
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
traceWriterFile := viper.GetString(flagTraceStore)
|
||||
|
||||
app, err := appCreator(home, ctx.Logger, traceStore)
|
||||
db, err := openDB(home)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
traceWriter, err := openTraceWriter(traceWriterFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
app := appCreator(ctx.Logger, db, traceWriter)
|
||||
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
"github.com/tendermint/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
)
|
||||
|
||||
func TestStartStandAlone(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||
require.Nil(t, err)
|
||||
defer func() {
|
||||
os.RemoveAll(home)
|
||||
}()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
cdc := codec.New()
|
||||
appInit := AppInit{
|
||||
AppGenState: mock.AppGenState,
|
||||
AppGenTx: mock.AppGenTx,
|
||||
}
|
||||
initCmd := InitCmd(ctx, cdc, appInit)
|
||||
err = initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
app, err := mock.NewApp(home, logger)
|
||||
require.Nil(t, err)
|
||||
svrAddr, _, err := FreeTCPAddr()
|
||||
require.Nil(t, err)
|
||||
svr, err := server.NewServer(svrAddr, "socket", app)
|
||||
require.Nil(t, err, "error creating listener")
|
||||
svr.SetLogger(logger.With("module", "abci-server"))
|
||||
svr.Start()
|
||||
|
||||
timer := time.NewTimer(time.Duration(2) * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
svr.Stop()
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,9 @@ func FreeTCPAddr() (addr, port string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// setupViper creates a homedir to run inside,
|
||||
// SetupViper creates a homedir to run inside,
|
||||
// and returns a cleanup function to defer
|
||||
func setupViper(t *testing.T) func() {
|
||||
func SetupViper(t *testing.T) func() {
|
||||
rootDir, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||
require.Nil(t, err)
|
||||
viper.Set(cli.HomeFlag, rootDir)
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
gc "github.com/cosmos/cosmos-sdk/server/config"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/spf13/viper"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeDirPrefix = "node-dir-prefix"
|
||||
nValidators = "v"
|
||||
outputDir = "output-dir"
|
||||
nodeDaemonHome = "node-daemon-home"
|
||||
nodeCliHome = "node-cli-home"
|
||||
|
||||
startingIPAddress = "starting-ip-address"
|
||||
)
|
||||
|
||||
const nodeDirPerm = 0755
|
||||
|
||||
// get cmd to initialize all files for tendermint testnet and application
|
||||
func TestnetFilesCmd(ctx *Context, cdc *codec.Codec, appInit AppInit) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "testnet",
|
||||
Short: "Initialize files for a Gaiad testnet",
|
||||
Long: `testnet will create "v" number of directories and populate each with
|
||||
necessary files (private validator, genesis, config, etc.).
|
||||
|
||||
Note, strict routability for addresses is turned off in the config file.
|
||||
|
||||
Example:
|
||||
|
||||
gaiad testnet --v 4 --o ./output --starting-ip-address 192.168.10.2
|
||||
`,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
config := ctx.Config
|
||||
err := testnetWithConfig(config, cdc, appInit)
|
||||
return err
|
||||
},
|
||||
}
|
||||
cmd.Flags().Int(nValidators, 4,
|
||||
"Number of validators to initialize the testnet with")
|
||||
cmd.Flags().StringP(outputDir, "o", "./mytestnet",
|
||||
"Directory to store initialization data for the testnet")
|
||||
cmd.Flags().String(nodeDirPrefix, "node",
|
||||
"Prefix the directory name for each node with (node results in node0, node1, ...)")
|
||||
cmd.Flags().String(nodeDaemonHome, "gaiad",
|
||||
"Home directory of the node's daemon configuration")
|
||||
cmd.Flags().String(nodeCliHome, "gaiacli",
|
||||
"Home directory of the node's cli configuration")
|
||||
|
||||
cmd.Flags().String(startingIPAddress, "192.168.0.1",
|
||||
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit AppInit) error {
|
||||
outDir := viper.GetString(outputDir)
|
||||
numValidators := viper.GetInt(nValidators)
|
||||
|
||||
// Generate private key, node ID, initial transaction
|
||||
for i := 0; i < numValidators; i++ {
|
||||
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
|
||||
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
|
||||
nodeCliHomeName := viper.GetString(nodeCliHome)
|
||||
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
|
||||
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
|
||||
gentxsDir := filepath.Join(outDir, "gentxs")
|
||||
config.SetRoot(nodeDir)
|
||||
|
||||
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outDir)
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(clientDir, nodeDirPerm)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outDir)
|
||||
return err
|
||||
}
|
||||
|
||||
config.Moniker = nodeDirName
|
||||
ip, err := getIP(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
genTxConfig := gc.GenTx{
|
||||
nodeDirName,
|
||||
clientDir,
|
||||
true,
|
||||
ip,
|
||||
}
|
||||
|
||||
// Run `init gen-tx` and generate initial transactions
|
||||
cliPrint, genTxFile, err := gentxWithConfig(cdc, appInit, config, genTxConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save private key seed words
|
||||
name := fmt.Sprintf("%v.json", "key_seed")
|
||||
err = writeFile(name, clientDir, cliPrint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Gather gentxs folder
|
||||
name = fmt.Sprintf("%v.json", nodeDirName)
|
||||
err = writeFile(name, gentxsDir, genTxFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate genesis.json and config.toml
|
||||
chainID := "chain-" + cmn.RandStr(6)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
|
||||
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i)
|
||||
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
|
||||
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
|
||||
gentxsDir := filepath.Join(outDir, "gentxs")
|
||||
initConfig := InitConfig{
|
||||
chainID,
|
||||
true,
|
||||
gentxsDir,
|
||||
true,
|
||||
}
|
||||
config.Moniker = nodeDirName
|
||||
config.SetRoot(nodeDir)
|
||||
|
||||
// Run `init` and generate genesis.json and config.toml
|
||||
_, _, _, err := initWithConfig(cdc, appInit, config, initConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully initialized %v node directories\n", viper.GetInt(nValidators))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getIP(i int) (ip string, err error) {
|
||||
ip = viper.GetString(startingIPAddress)
|
||||
if len(ip) == 0 {
|
||||
ip, err = externalIP()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
ip, err = calculateIP(ip, i)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func writeFile(name string, dir string, contents []byte) error {
|
||||
writePath := filepath.Join(dir)
|
||||
file := filepath.Join(writePath, name)
|
||||
err := cmn.EnsureDir(writePath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cmn.WriteFile(file, contents, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateIP(ip string, i int) (string, error) {
|
||||
ipv4 := net.ParseIP(ip).To4()
|
||||
if ipv4 == nil {
|
||||
return "", fmt.Errorf("%v: non ipv4 address", ip)
|
||||
}
|
||||
|
||||
for j := 0; j < i; j++ {
|
||||
ipv4[3]++
|
||||
}
|
||||
return ipv4.String(), nil
|
||||
}
|
||||
+4
-6
@@ -42,7 +42,7 @@ func NewContext(config *cfg.Config, logger log.Logger) *Context {
|
||||
|
||||
// PersistentPreRunEFn returns a PersistentPreRunE function for cobra
|
||||
// that initailizes the passed in context with a properly configured
|
||||
// logger and config objecy
|
||||
// logger and config object.
|
||||
func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if cmd.Name() == version.VersionCmd.Name() {
|
||||
@@ -85,7 +85,7 @@ func interceptLoadConfig() (conf *cfg.Config, err error) {
|
||||
|
||||
if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
|
||||
// the following parse config is needed to create directories
|
||||
conf, _ = tcmd.ParseConfig()
|
||||
conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
|
||||
conf.ProfListenAddress = "localhost:6060"
|
||||
conf.P2P.RecvRate = 5120000
|
||||
conf.P2P.SendRate = 5120000
|
||||
@@ -96,7 +96,7 @@ func interceptLoadConfig() (conf *cfg.Config, err error) {
|
||||
}
|
||||
|
||||
if conf == nil {
|
||||
conf, err = tcmd.ParseConfig()
|
||||
conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
|
||||
}
|
||||
|
||||
cosmosConfigFilePath := filepath.Join(rootDir, "config/gaiad.toml")
|
||||
@@ -143,8 +143,6 @@ func AddCommands(
|
||||
)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
InitCmd(ctx, cdc, appInit),
|
||||
TestnetFilesCmd(ctx, cdc, appInit),
|
||||
StartCmd(ctx, appCreator),
|
||||
UnsafeResetAllCmd(ctx),
|
||||
client.LineBreak,
|
||||
@@ -177,7 +175,7 @@ func InsertKeyJSON(cdc *codec.Codec, baseJSON []byte, key string, value json.Raw
|
||||
|
||||
// https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
|
||||
// TODO there must be a better way to get external IP
|
||||
func externalIP() (string, error) {
|
||||
func ExternalIP() (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
Reference in New Issue
Block a user