Merge branch 'develop' into dev/lock_tools

This commit is contained in:
Christopher Goes 2018-10-11 08:48:15 +02:00
commit dbd4f30b39
26 changed files with 942 additions and 596 deletions

3
.gitignore vendored
View File

@ -2,6 +2,9 @@
.DS_Store
*.swp
*.swo
*.swl
*.swm
*.swn
.vscode
.idea

View File

@ -87,8 +87,14 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
}
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (err error) {
stdTx, err := buildUnsignedStdTx(txBldr, cliCtx, msgs)
// Don't perform online validation or lookups if offline is true.
func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) {
var stdTx auth.StdTx
if offline {
stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
} else {
stdTx, err = buildUnsignedStdTx(txBldr, cliCtx, msgs)
}
if err != nil {
return
}
@ -204,6 +210,10 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg
if err != nil {
return
}
return buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
}
func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
if txBldr.SimulateGas {
var name string
name, err = cliCtx.GetFromName()

View File

@ -16,6 +16,7 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/server"
)
@ -28,10 +29,12 @@ func main() {
Short: "Gaia Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
appInit := app.GaiaAppInit()
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(),
server.ConstructAppCreator(newApp, "gaia"),
server.ConstructAppExporter(exportAppStateAndTMValidators, "gaia"))
server.AddCommands(ctx, cdc, rootCmd, appInit,
newApp, exportAppStateAndTMValidators)
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome)

321
cmd/gaia/init/init.go Normal file
View File

@ -0,0 +1,321 @@
package init
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
servercfg "github.com/cosmos/cosmos-sdk/server/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
)
// get cmd to initialize all files for tendermint and application
func GenTxCmd(ctx *server.Context, cdc *codec.Codec, appInit server.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(cli.HomeFlag))
ip := viper.GetString(server.FlagIP)
if len(ip) == 0 {
eip, err := server.ExternalIP()
if err != nil {
return err
}
ip = eip
}
genTxConfig := servercfg.GenTx{
viper.GetString(server.FlagName),
viper.GetString(server.FlagClientHome),
viper.GetBool(server.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(server.FlagIP, "", "external facing IP to use if left blank IP will be retrieved from this machine")
cmd.Flags().AddFlagSet(appInit.FlagsAppGenTx)
return cmd
}
// NOTE: This will update (write) the config file with
// updated name (moniker) for node.
func gentxWithConfig(cdc *codec.Codec, appInit server.AppInit, config *cfg.Config, genTxConfig servercfg.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 := server.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 = common.EnsureDir(writePath, 0700)
if err != nil {
return
}
err = common.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
// nolint: golint
func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.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(cli.HomeFlag))
initConfig := server.InitConfig{
viper.GetString(server.FlagChainID),
viper.GetBool(server.FlagWithTxs),
filepath.Join(config.RootDir, "config", "gentx"),
viper.GetBool(server.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(server.FlagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(server.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(server.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 server.AppInit, config *cfg.Config, initConfig server.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", common.RandStr(6))
}
chainID = initConfig.ChainID
genFile := config.GenesisFile()
if !initConfig.Overwrite && common.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 []types.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 := servercfg.GenTx{
viper.GetString(server.FlagName),
viper.GetString(server.FlagClientHome),
viper.GetBool(server.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 = []types.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 []types.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]server.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 server.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 *privval.FilePV
if common.FileExists(privValFile) {
privValidator = privval.LoadFilePV(privValFile)
} else {
privValidator = privval.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 []types.GenesisValidator, appState json.RawMessage) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genesisFile)
}

111
cmd/gaia/init/init_test.go Normal file
View File

@ -0,0 +1,111 @@
package init
import (
"bytes"
"io"
"io/ioutil"
"os"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/mock"
"github.com/stretchr/testify/require"
abciServer "github.com/tendermint/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/libs/log"
)
func TestInitCmd(t *testing.T) {
defer server.SetupViper(t)()
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := codec.New()
appInit := server.AppInit{
AppGenState: mock.AppGenState,
AppGenTx: mock.AppGenTx,
}
cmd := InitCmd(ctx, cdc, appInit)
err = cmd.RunE(nil, nil)
require.NoError(t, err)
}
func TestEmptyState(t *testing.T) {
defer server.SetupViper(t)()
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := codec.New()
appInit := server.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 = server.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")
}
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 := server.NewContext(cfg, logger)
cdc := codec.New()
appInit := server.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 := server.FreeTCPAddr()
require.Nil(t, err)
svr, err := abciServer.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()
}
}

View File

@ -1,7 +1,8 @@
package server
package init
import (
"fmt"
"github.com/cosmos/cosmos-sdk/server"
"net"
"path/filepath"
@ -30,7 +31,7 @@ var (
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 {
func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "testnet",
Short: "Initialize files for a Gaiad testnet",
@ -65,7 +66,7 @@ Example:
return cmd
}
func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit AppInit) error {
func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppInit) error {
outDir := viper.GetString(outputDir)
numValidators := viper.GetInt(nValidators)
@ -133,7 +134,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit AppInit) er
nodeDaemonHomeName := viper.GetString(nodeDaemonHome)
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
gentxsDir := filepath.Join(outDir, "gentxs")
initConfig := InitConfig{
initConfig := server.InitConfig{
chainID,
true,
gentxsDir,
@ -156,7 +157,7 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit AppInit) er
func getIP(i int) (ip string, err error) {
ip = viper.GetString(startingIPAddress)
if len(ip) == 0 {
ip, err = externalIP()
ip, err = server.ExternalIP()
if err != nil {
return "", err
}

View File

@ -8,7 +8,7 @@ The core IBC protocol is payload-agnostic. On top of IBC, developers can impleme
IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically verifiable packets can be sent between them.
IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This document supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation.
IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/blob/master/CosmosIBCSpecification.pdf). This document supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation.
## Contents

View File

@ -6,6 +6,7 @@ import (
"os"
"github.com/cosmos/cosmos-sdk/baseapp"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/server"
@ -28,9 +29,12 @@ func main() {
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
server.ConstructAppCreator(newApp, "basecoin"),
server.ConstructAppExporter(exportAppStateAndTMValidators, "basecoin"))
appInit := server.DefaultAppInit
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit))
server.AddCommands(ctx, cdc, rootCmd, appInit,
newApp, exportAppStateAndTMValidators)
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind")

View File

@ -13,6 +13,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server"
@ -70,9 +71,11 @@ func main() {
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, CoolAppInit))
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, CoolAppInit))
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,
server.ConstructAppCreator(newApp, "democoin"),
server.ConstructAppExporter(exportAppStateAndTMValidators, "democoin"))
newApp, exportAppStateAndTMValidators)
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.democoind")

View File

@ -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
}

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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

View File

@ -1,6 +1,7 @@
package store
import (
"bytes"
"io"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -8,11 +9,27 @@ import (
var _ KVStore = prefixStore{}
// prefixStore is similar with tendermint/tendermint/libs/db/prefix_db
// both gives access only to the limited subset of the store
// for convinience or safety
type prefixStore struct {
parent KVStore
prefix []byte
}
func cloneAppend(bz []byte, tail []byte) (res []byte) {
res = make([]byte, len(bz)+len(tail))
copy(res, bz)
copy(res[len(bz):], tail)
return
}
func (s prefixStore) key(key []byte) (res []byte) {
res = cloneAppend(s.prefix, key)
return
}
// Implements Store
func (s prefixStore) GetStoreType() StoreType {
return s.parent.GetStoreType()
@ -30,22 +47,23 @@ func (s prefixStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap
// Implements KVStore
func (s prefixStore) Get(key []byte) []byte {
return s.parent.Get(append(s.prefix, key...))
res := s.parent.Get(s.key(key))
return res
}
// Implements KVStore
func (s prefixStore) Has(key []byte) bool {
return s.parent.Has(append(s.prefix, key...))
return s.parent.Has(s.key(key))
}
// Implements KVStore
func (s prefixStore) Set(key, value []byte) {
s.parent.Set(append(s.prefix, key...), value)
s.parent.Set(s.key(key), value)
}
// Implements KVStore
func (s prefixStore) Delete(key []byte) {
s.parent.Delete(append(s.prefix, key...))
s.parent.Delete(s.key(key))
}
// Implements KVStore
@ -59,68 +77,147 @@ func (s prefixStore) Gas(meter GasMeter, config GasConfig) KVStore {
}
// Implements KVStore
// Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L106
func (s prefixStore) Iterator(start, end []byte) Iterator {
newstart := cloneAppend(s.prefix, start)
var newend []byte
if end == nil {
end = sdk.PrefixEndBytes(s.prefix)
newend = cpIncr(s.prefix)
} else {
end = append(s.prefix, end...)
}
return prefixIterator{
prefix: s.prefix,
iter: s.parent.Iterator(append(s.prefix, start...), end),
newend = cloneAppend(s.prefix, end)
}
iter := s.parent.Iterator(newstart, newend)
return newPrefixIterator(s.prefix, start, end, iter)
}
// Implements KVStore
// Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L129
func (s prefixStore) ReverseIterator(start, end []byte) Iterator {
if end == nil {
end = sdk.PrefixEndBytes(s.prefix)
var newstart []byte
if start == nil {
newstart = cpIncr(s.prefix)
} else {
end = append(s.prefix, end...)
newstart = cloneAppend(s.prefix, start)
}
return prefixIterator{
prefix: s.prefix,
iter: s.parent.ReverseIterator(start, end),
var newend []byte
if end == nil {
newend = cpDecr(s.prefix)
} else {
newend = cloneAppend(s.prefix, end)
}
iter := s.parent.ReverseIterator(newstart, newend)
if start == nil {
skipOne(iter, cpIncr(s.prefix))
}
return newPrefixIterator(s.prefix, start, end, iter)
}
var _ sdk.Iterator = (*prefixIterator)(nil)
type prefixIterator struct {
prefix []byte
prefix []byte
start, end []byte
iter Iterator
valid bool
}
iter Iterator
func newPrefixIterator(prefix, start, end []byte, parent Iterator) *prefixIterator {
return &prefixIterator{
prefix: prefix,
start: start,
end: end,
iter: parent,
valid: parent.Valid() && bytes.HasPrefix(parent.Key(), prefix),
}
}
// Implements Iterator
func (iter prefixIterator) Domain() (start []byte, end []byte) {
start, end = iter.iter.Domain()
start = start[len(iter.prefix):]
end = end[len(iter.prefix):]
return
func (iter *prefixIterator) Domain() ([]byte, []byte) {
return iter.start, iter.end
}
// Implements Iterator
func (iter prefixIterator) Valid() bool {
return iter.iter.Valid()
func (iter *prefixIterator) Valid() bool {
return iter.valid && iter.iter.Valid()
}
// Implements Iterator
func (iter prefixIterator) Next() {
func (iter *prefixIterator) Next() {
if !iter.valid {
panic("prefixIterator invalid, cannot call Next()")
}
iter.iter.Next()
if !iter.iter.Valid() || !bytes.HasPrefix(iter.iter.Key(), iter.prefix) {
iter.valid = false
}
}
// Implements Iterator
func (iter prefixIterator) Key() (key []byte) {
func (iter *prefixIterator) Key() (key []byte) {
if !iter.valid {
panic("prefixIterator invalid, cannot call Key()")
}
key = iter.iter.Key()
key = key[len(iter.prefix):]
key = stripPrefix(key, iter.prefix)
return
}
// Implements Iterator
func (iter prefixIterator) Value() []byte {
func (iter *prefixIterator) Value() []byte {
if !iter.valid {
panic("prefixIterator invalid, cannot call Value()")
}
return iter.iter.Value()
}
// Implements Iterator
func (iter prefixIterator) Close() {
func (iter *prefixIterator) Close() {
iter.iter.Close()
}
// copied from github.com/tendermint/tendermint/libs/db/prefix_db.go
func stripPrefix(key []byte, prefix []byte) []byte {
if len(key) < len(prefix) || !bytes.Equal(key[:len(prefix)], prefix) {
panic("should not happen")
}
return key[len(prefix):]
}
// wrapping sdk.PrefixEndBytes
func cpIncr(bz []byte) []byte {
return sdk.PrefixEndBytes(bz)
}
// copied from github.com/tendermint/tendermint/libs/db/util.go
func cpDecr(bz []byte) (ret []byte) {
if len(bz) == 0 {
panic("cpDecr expects non-zero bz length")
}
ret = make([]byte, len(bz))
copy(ret, bz)
for i := len(bz) - 1; i >= 0; i-- {
if ret[i] > byte(0x00) {
ret[i]--
return
}
ret[i] = byte(0xFF)
if i == 0 {
return nil
}
}
return nil
}
func skipOne(iter Iterator, skipKey []byte) {
if iter.Valid() {
if bytes.Equal(iter.Key(), skipKey) {
iter.Next()
}
}
}

View File

@ -105,7 +105,286 @@ func TestPrefixStoreIterate(t *testing.T) {
pIter.Next()
}
require.Equal(t, bIter.Valid(), pIter.Valid())
bIter.Close()
pIter.Close()
}
func TestPrefixStoreIteratorEdgeCase(t *testing.T) {
db := dbm.NewMemDB()
baseStore := dbStoreAdapter{db}
// overflow in cpIncr
prefix := []byte{0xAA, 0xFF, 0xFF}
prefixStore := baseStore.Prefix(prefix)
// ascending order
baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFE, 0x00}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFF}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFF, 0x00}, []byte{})
baseStore.Set([]byte{0xAB}, []byte{})
baseStore.Set([]byte{0xAB, 0x00}, []byte{})
baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{})
iter := prefixStore.Iterator(nil, nil)
checkDomain(t, iter, nil, nil)
checkItem(t, iter, []byte{}, bz(""))
checkNext(t, iter, true)
checkItem(t, iter, []byte{0x00}, bz(""))
checkNext(t, iter, false)
checkInvalid(t, iter)
iter.Close()
}
func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) {
db := dbm.NewMemDB()
baseStore := dbStoreAdapter{db}
// overflow in cpIncr
prefix := []byte{0xAA, 0xFF, 0xFF}
prefixStore := baseStore.Prefix(prefix)
// descending order
baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{})
baseStore.Set([]byte{0xAB, 0x00}, []byte{})
baseStore.Set([]byte{0xAB}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFF, 0x00}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFF}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFE, 0x00}, []byte{})
baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{})
iter := prefixStore.ReverseIterator(nil, nil)
checkDomain(t, iter, nil, nil)
checkItem(t, iter, []byte{0x00}, bz(""))
checkNext(t, iter, true)
checkItem(t, iter, []byte{}, bz(""))
checkNext(t, iter, false)
checkInvalid(t, iter)
iter.Close()
db = dbm.NewMemDB()
baseStore = dbStoreAdapter{db}
// underflow in cpDecr
prefix = []byte{0xAA, 0x00, 0x00}
prefixStore = baseStore.Prefix(prefix)
baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00, 0x00}, []byte{})
baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00}, []byte{})
baseStore.Set([]byte{0xAB, 0x00, 0x01}, []byte{})
baseStore.Set([]byte{0xAA, 0x00, 0x00, 0x00}, []byte{})
baseStore.Set([]byte{0xAA, 0x00, 0x00}, []byte{})
baseStore.Set([]byte{0xA9, 0xFF, 0xFF, 0x00}, []byte{})
baseStore.Set([]byte{0xA9, 0xFF, 0xFF}, []byte{})
iter = prefixStore.ReverseIterator(nil, nil)
checkDomain(t, iter, nil, nil)
checkItem(t, iter, []byte{0x00}, bz(""))
checkNext(t, iter, true)
checkItem(t, iter, []byte{}, bz(""))
checkNext(t, iter, false)
checkInvalid(t, iter)
iter.Close()
}
// Tests below are ported from https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db_test.go
func mockStoreWithStuff() sdk.KVStore {
db := dbm.NewMemDB()
store := dbStoreAdapter{db}
// Under "key" prefix
store.Set(bz("key"), bz("value"))
store.Set(bz("key1"), bz("value1"))
store.Set(bz("key2"), bz("value2"))
store.Set(bz("key3"), bz("value3"))
store.Set(bz("something"), bz("else"))
store.Set(bz(""), bz(""))
store.Set(bz("k"), bz("val"))
store.Set(bz("ke"), bz("valu"))
store.Set(bz("kee"), bz("valuu"))
return store
}
func checkValue(t *testing.T, store sdk.KVStore, key []byte, expected []byte) {
bz := store.Get(key)
require.Equal(t, expected, bz)
}
func checkValid(t *testing.T, itr sdk.Iterator, expected bool) {
valid := itr.Valid()
require.Equal(t, expected, valid)
}
func checkNext(t *testing.T, itr sdk.Iterator, expected bool) {
itr.Next()
valid := itr.Valid()
require.Equal(t, expected, valid)
}
func checkDomain(t *testing.T, itr sdk.Iterator, start, end []byte) {
ds, de := itr.Domain()
require.Equal(t, start, ds)
require.Equal(t, end, de)
}
func checkItem(t *testing.T, itr sdk.Iterator, key, value []byte) {
require.Exactly(t, key, itr.Key())
require.Exactly(t, value, itr.Value())
}
func checkInvalid(t *testing.T, itr sdk.Iterator) {
checkValid(t, itr, false)
checkKeyPanics(t, itr)
checkValuePanics(t, itr)
checkNextPanics(t, itr)
}
func checkKeyPanics(t *testing.T, itr sdk.Iterator) {
require.Panics(t, func() { itr.Key() })
}
func checkValuePanics(t *testing.T, itr sdk.Iterator) {
require.Panics(t, func() { itr.Value() })
}
func checkNextPanics(t *testing.T, itr sdk.Iterator) {
require.Panics(t, func() { itr.Next() })
}
func TestPrefixDBSimple(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
checkValue(t, pstore, bz("key"), nil)
checkValue(t, pstore, bz(""), bz("value"))
checkValue(t, pstore, bz("key1"), nil)
checkValue(t, pstore, bz("1"), bz("value1"))
checkValue(t, pstore, bz("key2"), nil)
checkValue(t, pstore, bz("2"), bz("value2"))
checkValue(t, pstore, bz("key3"), nil)
checkValue(t, pstore, bz("3"), bz("value3"))
checkValue(t, pstore, bz("something"), nil)
checkValue(t, pstore, bz("k"), nil)
checkValue(t, pstore, bz("ke"), nil)
checkValue(t, pstore, bz("kee"), nil)
}
func TestPrefixDBIterator1(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.Iterator(nil, nil)
checkDomain(t, itr, nil, nil)
checkItem(t, itr, bz(""), bz("value"))
checkNext(t, itr, true)
checkItem(t, itr, bz("1"), bz("value1"))
checkNext(t, itr, true)
checkItem(t, itr, bz("2"), bz("value2"))
checkNext(t, itr, true)
checkItem(t, itr, bz("3"), bz("value3"))
checkNext(t, itr, false)
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBIterator2(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.Iterator(nil, bz(""))
checkDomain(t, itr, nil, bz(""))
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBIterator3(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.Iterator(bz(""), nil)
checkDomain(t, itr, bz(""), nil)
checkItem(t, itr, bz(""), bz("value"))
checkNext(t, itr, true)
checkItem(t, itr, bz("1"), bz("value1"))
checkNext(t, itr, true)
checkItem(t, itr, bz("2"), bz("value2"))
checkNext(t, itr, true)
checkItem(t, itr, bz("3"), bz("value3"))
checkNext(t, itr, false)
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBIterator4(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.Iterator(bz(""), bz(""))
checkDomain(t, itr, bz(""), bz(""))
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBReverseIterator1(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.ReverseIterator(nil, nil)
checkDomain(t, itr, nil, nil)
checkItem(t, itr, bz("3"), bz("value3"))
checkNext(t, itr, true)
checkItem(t, itr, bz("2"), bz("value2"))
checkNext(t, itr, true)
checkItem(t, itr, bz("1"), bz("value1"))
checkNext(t, itr, true)
checkItem(t, itr, bz(""), bz("value"))
checkNext(t, itr, false)
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBReverseIterator2(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.ReverseIterator(nil, bz(""))
checkDomain(t, itr, nil, bz(""))
checkItem(t, itr, bz("3"), bz("value3"))
checkNext(t, itr, true)
checkItem(t, itr, bz("2"), bz("value2"))
checkNext(t, itr, true)
checkItem(t, itr, bz("1"), bz("value1"))
checkNext(t, itr, false)
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBReverseIterator3(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.ReverseIterator(bz(""), nil)
checkDomain(t, itr, bz(""), nil)
checkItem(t, itr, bz(""), bz("value"))
checkNext(t, itr, false)
checkInvalid(t, itr)
itr.Close()
}
func TestPrefixDBReverseIterator4(t *testing.T) {
store := mockStoreWithStuff()
pstore := store.Prefix(bz("key"))
itr := pstore.ReverseIterator(bz(""), bz(""))
checkInvalid(t, itr)
itr.Close()
}

View File

@ -2,7 +2,7 @@
"Linters": {
"vet": "go tool vet -composites=false :PATH:LINE:MESSAGE"
},
"Enable": ["golint", "vet", "ineffassign", "unparam", "unconvert", "misspell"],
"Enable": ["golint", "vet", "ineffassign", "unconvert", "misspell"],
"Deadline": "500s",
"Vendor": true,
"Cyclo": 11,

View File

@ -66,7 +66,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command {
// build and sign the transaction, then broadcast to Tendermint
msg := client.CreateMsg(from, to, coins)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})

View File

@ -103,7 +103,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// Build and sign the transaction, then broadcast to Tendermint
@ -183,7 +183,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// Build and sign the transaction, then broadcast to a Tendermint
@ -229,7 +229,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command {
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]",

View File

@ -42,7 +42,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command {
return err
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})

View File

@ -31,7 +31,7 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command {
msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr))
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},

View File

@ -88,7 +88,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true)
}
// build and sign the transaction, then broadcast to Tendermint
@ -143,7 +143,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command {
msg := stake.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// build and sign the transaction, then broadcast to Tendermint
@ -186,7 +186,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command {
msg := stake.NewMsgDelegate(delAddr, valAddr, amount)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
@ -256,7 +256,7 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
msg := stake.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, sharesAmount)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
@ -319,7 +319,7 @@ func GetCmdBeginUnbonding(storeName string, cdc *codec.Codec) *cobra.Command {
msg := stake.NewMsgBeginUnbonding(delAddr, valAddr, sharesAmount)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})