Release v0.9.1pull/340/merge v0.9.1
@ -0,0 +1,27 @@ | |||||
package commands | |||||
import ( | |||||
"fmt" | |||||
"github.com/spf13/cobra" | |||||
"github.com/tendermint/go-wire" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var genValidatorCmd = &cobra.Command{ | |||||
Use: "gen_validator", | |||||
Short: "Generate new validator keypair", | |||||
Run: genValidator, | |||||
} | |||||
func init() { | |||||
RootCmd.AddCommand(genValidatorCmd) | |||||
} | |||||
func genValidator(cmd *cobra.Command, args []string) { | |||||
privValidator := types.GenPrivValidator() | |||||
privValidatorJSONBytes := wire.JSONBytesPretty(privValidator) | |||||
fmt.Printf(`%v | |||||
`, string(privValidatorJSONBytes)) | |||||
} |
@ -0,0 +1,40 @@ | |||||
package commands | |||||
import ( | |||||
"fmt" | |||||
"github.com/tendermint/tendermint/consensus" | |||||
"github.com/spf13/cobra" | |||||
) | |||||
var replayCmd = &cobra.Command{ | |||||
Use: "replay [walfile]", | |||||
Short: "Replay messages from WAL", | |||||
Run: func(cmd *cobra.Command, args []string) { | |||||
if len(args) > 1 { | |||||
consensus.RunReplayFile(config, args[1], false) | |||||
} else { | |||||
fmt.Println("replay requires an argument (walfile)") | |||||
} | |||||
}, | |||||
} | |||||
var replayConsoleCmd = &cobra.Command{ | |||||
Use: "replay_console [walfile]", | |||||
Short: "Replay messages from WAL in a console", | |||||
Run: func(cmd *cobra.Command, args []string) { | |||||
if len(args) > 1 { | |||||
consensus.RunReplayFile(config, args[1], true) | |||||
} else { | |||||
fmt.Println("replay_console requires an argument (walfile)") | |||||
} | |||||
}, | |||||
} | |||||
func init() { | |||||
RootCmd.AddCommand(replayCmd) | |||||
RootCmd.AddCommand(replayConsoleCmd) | |||||
} |
@ -0,0 +1,62 @@ | |||||
package commands | |||||
import ( | |||||
"os" | |||||
"github.com/spf13/cobra" | |||||
cfg "github.com/tendermint/go-config" | |||||
"github.com/tendermint/log15" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var resetAllCmd = &cobra.Command{ | |||||
Use: "unsafe_reset_all", | |||||
Short: "(unsafe) Remove all the data and WAL, reset this node's validator", | |||||
Run: resetAll, | |||||
} | |||||
var resetPrivValidatorCmd = &cobra.Command{ | |||||
Use: "unsafe_reset_priv_validator", | |||||
Short: "(unsafe) Reset this node's validator", | |||||
Run: resetPrivValidator, | |||||
} | |||||
func init() { | |||||
RootCmd.AddCommand(resetAllCmd) | |||||
RootCmd.AddCommand(resetPrivValidatorCmd) | |||||
} | |||||
// XXX: this is totally unsafe. | |||||
// it's only suitable for testnets. | |||||
func resetAll(cmd *cobra.Command, args []string) { | |||||
ResetAll(config, log) | |||||
} | |||||
// XXX: this is totally unsafe. | |||||
// it's only suitable for testnets. | |||||
func resetPrivValidator(cmd *cobra.Command, args []string) { | |||||
ResetPrivValidator(config, log) | |||||
} | |||||
// Exported so other CLI tools can use it | |||||
func ResetAll(c cfg.Config, l log15.Logger) { | |||||
ResetPrivValidator(c, l) | |||||
os.RemoveAll(c.GetString("db_dir")) | |||||
} | |||||
func ResetPrivValidator(c cfg.Config, l log15.Logger) { | |||||
// Get PrivValidator | |||||
var privValidator *types.PrivValidator | |||||
privValidatorFile := config.GetString("priv_validator_file") | |||||
if _, err := os.Stat(privValidatorFile); err == nil { | |||||
privValidator = types.LoadPrivValidator(privValidatorFile) | |||||
privValidator.Reset() | |||||
log.Notice("Reset PrivValidator", "file", privValidatorFile) | |||||
} else { | |||||
privValidator = types.GenPrivValidator() | |||||
privValidator.SetFile(privValidatorFile) | |||||
privValidator.Save() | |||||
log.Notice("Generated PrivValidator", "file", privValidatorFile) | |||||
} | |||||
} |
@ -0,0 +1,31 @@ | |||||
package commands | |||||
import ( | |||||
"github.com/spf13/cobra" | |||||
"github.com/tendermint/go-logger" | |||||
tmcfg "github.com/tendermint/tendermint/config/tendermint" | |||||
) | |||||
var ( | |||||
config = tmcfg.GetConfig("") | |||||
log = logger.New("module", "main") | |||||
) | |||||
//global flag | |||||
var logLevel string | |||||
var RootCmd = &cobra.Command{ | |||||
Use: "tendermint", | |||||
Short: "Tendermint Core (BFT Consensus) in Go", | |||||
PersistentPreRun: func(cmd *cobra.Command, args []string) { | |||||
// set the log level in the config and logger | |||||
config.Set("log_level", logLevel) | |||||
logger.SetLogLevel(logLevel) | |||||
}, | |||||
} | |||||
func init() { | |||||
//parse flag and set config | |||||
RootCmd.PersistentFlags().StringVar(&logLevel, "log_level", config.GetString("log_level"), "Log level") | |||||
} |
@ -0,0 +1,124 @@ | |||||
package commands | |||||
import ( | |||||
"io/ioutil" | |||||
"time" | |||||
"github.com/spf13/cobra" | |||||
. "github.com/tendermint/go-common" | |||||
"github.com/tendermint/tendermint/node" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var runNodeCmd = &cobra.Command{ | |||||
Use: "node", | |||||
Short: "Run the tendermint node", | |||||
PreRun: setConfigFlags, | |||||
Run: runNode, | |||||
} | |||||
//flags | |||||
var ( | |||||
moniker string | |||||
nodeLaddr string | |||||
seeds string | |||||
fastSync bool | |||||
skipUPNP bool | |||||
rpcLaddr string | |||||
grpcLaddr string | |||||
proxyApp string | |||||
abciTransport string | |||||
pex bool | |||||
) | |||||
func init() { | |||||
// configuration options | |||||
runNodeCmd.Flags().StringVar(&moniker, "moniker", config.GetString("moniker"), | |||||
"Node Name") | |||||
runNodeCmd.Flags().StringVar(&nodeLaddr, "node_laddr", config.GetString("node_laddr"), | |||||
"Node listen address. (0.0.0.0:0 means any interface, any port)") | |||||
runNodeCmd.Flags().StringVar(&seeds, "seeds", config.GetString("seeds"), | |||||
"Comma delimited host:port seed nodes") | |||||
runNodeCmd.Flags().BoolVar(&fastSync, "fast_sync", config.GetBool("fast_sync"), | |||||
"Fast blockchain syncing") | |||||
runNodeCmd.Flags().BoolVar(&skipUPNP, "skip_upnp", config.GetBool("skip_upnp"), | |||||
"Skip UPNP configuration") | |||||
runNodeCmd.Flags().StringVar(&rpcLaddr, "rpc_laddr", config.GetString("rpc_laddr"), | |||||
"RPC listen address. Port required") | |||||
runNodeCmd.Flags().StringVar(&grpcLaddr, "grpc_laddr", config.GetString("grpc_laddr"), | |||||
"GRPC listen address (BroadcastTx only). Port required") | |||||
runNodeCmd.Flags().StringVar(&proxyApp, "proxy_app", config.GetString("proxy_app"), | |||||
"Proxy app address, or 'nilapp' or 'dummy' for local testing.") | |||||
runNodeCmd.Flags().StringVar(&abciTransport, "abci", config.GetString("abci"), | |||||
"Specify abci transport (socket | grpc)") | |||||
// feature flags | |||||
runNodeCmd.Flags().BoolVar(&pex, "pex", config.GetBool("pex_reactor"), | |||||
"Enable Peer-Exchange (dev feature)") | |||||
RootCmd.AddCommand(runNodeCmd) | |||||
} | |||||
func setConfigFlags(cmd *cobra.Command, args []string) { | |||||
// Merge parsed flag values onto config | |||||
config.Set("moniker", moniker) | |||||
config.Set("node_laddr", nodeLaddr) | |||||
config.Set("seeds", seeds) | |||||
config.Set("fast_sync", fastSync) | |||||
config.Set("skip_upnp", skipUPNP) | |||||
config.Set("rpc_laddr", rpcLaddr) | |||||
config.Set("grpc_laddr", grpcLaddr) | |||||
config.Set("proxy_app", proxyApp) | |||||
config.Set("abci", abciTransport) | |||||
config.Set("pex_reactor", pex) | |||||
} | |||||
// Users wishing to: | |||||
// * Use an external signer for their validators | |||||
// * Supply an in-proc abci app | |||||
// should import github.com/tendermint/tendermint/node and implement | |||||
// their own run_node to call node.NewNode (instead of node.NewNodeDefault) | |||||
// with their custom priv validator and/or custom proxy.ClientCreator | |||||
func runNode(cmd *cobra.Command, args []string) { | |||||
// Wait until the genesis doc becomes available | |||||
// This is for Mintnet compatibility. | |||||
// TODO: If Mintnet gets deprecated or genesis_file is | |||||
// always available, remove. | |||||
genDocFile := config.GetString("genesis_file") | |||||
if !FileExists(genDocFile) { | |||||
log.Notice(Fmt("Waiting for genesis file %v...", genDocFile)) | |||||
for { | |||||
time.Sleep(time.Second) | |||||
if !FileExists(genDocFile) { | |||||
continue | |||||
} | |||||
jsonBlob, err := ioutil.ReadFile(genDocFile) | |||||
if err != nil { | |||||
Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) | |||||
} | |||||
genDoc, err := types.GenesisDocFromJSON(jsonBlob) | |||||
if err != nil { | |||||
Exit(Fmt("Error reading GenesisDoc: %v", err)) | |||||
} | |||||
if genDoc.ChainID == "" { | |||||
Exit(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile)) | |||||
} | |||||
config.Set("chain_id", genDoc.ChainID) | |||||
} | |||||
} | |||||
// Create & start node | |||||
n := node.NewNodeDefault(config) | |||||
if _, err := n.Start(); err != nil { | |||||
Exit(Fmt("Failed to start node: %v", err)) | |||||
} else { | |||||
log.Notice("Started node", "nodeInfo", n.Switch().NodeInfo()) | |||||
} | |||||
// Trap signal, run forever. | |||||
n.RunForever() | |||||
} |
@ -0,0 +1,93 @@ | |||||
package commands | |||||
import ( | |||||
"fmt" | |||||
"path" | |||||
"time" | |||||
"github.com/spf13/cobra" | |||||
cmn "github.com/tendermint/go-common" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var testnetFilesCmd = &cobra.Command{ | |||||
Use: "testnet", | |||||
Short: "Initialize files for a Tendermint testnet", | |||||
Run: testnetFiles, | |||||
} | |||||
//flags | |||||
var ( | |||||
nValidators int | |||||
dataDir string | |||||
) | |||||
func init() { | |||||
testnetFilesCmd.Flags().IntVar(&nValidators, "n", 4, | |||||
"Number of validators to initialize the testnet with") | |||||
testnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet", | |||||
"Directory to store initialization data for the testnet") | |||||
RootCmd.AddCommand(testnetFilesCmd) | |||||
} | |||||
func testnetFiles(cmd *cobra.Command, args []string) { | |||||
genVals := make([]types.GenesisValidator, nValidators) | |||||
// Initialize core dir and priv_validator.json's | |||||
for i := 0; i < nValidators; i++ { | |||||
mach := cmn.Fmt("mach%d", i) | |||||
err := initMachCoreDirectory(dataDir, mach) | |||||
if err != nil { | |||||
cmn.Exit(err.Error()) | |||||
} | |||||
// Read priv_validator.json to populate vals | |||||
privValFile := path.Join(dataDir, mach, "priv_validator.json") | |||||
privVal := types.LoadPrivValidator(privValFile) | |||||
genVals[i] = types.GenesisValidator{ | |||||
PubKey: privVal.PubKey, | |||||
Amount: 1, | |||||
Name: mach, | |||||
} | |||||
} | |||||
// Generate genesis doc from generated validators | |||||
genDoc := &types.GenesisDoc{ | |||||
GenesisTime: time.Now(), | |||||
ChainID: "chain-" + cmn.RandStr(6), | |||||
Validators: genVals, | |||||
} | |||||
// Write genesis file. | |||||
for i := 0; i < nValidators; i++ { | |||||
mach := cmn.Fmt("mach%d", i) | |||||
genDoc.SaveAs(path.Join(dataDir, mach, "genesis.json")) | |||||
} | |||||
fmt.Println(cmn.Fmt("Successfully initialized %v node directories", nValidators)) | |||||
} | |||||
// Initialize per-machine core directory | |||||
func initMachCoreDirectory(base, mach string) error { | |||||
dir := path.Join(base, mach) | |||||
err := cmn.EnsureDir(dir, 0777) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
// Create priv_validator.json file if not present | |||||
ensurePrivValidator(path.Join(dir, "priv_validator.json")) | |||||
return nil | |||||
} | |||||
func ensurePrivValidator(file string) { | |||||
if cmn.FileExists(file) { | |||||
return | |||||
} | |||||
privValidator := types.GenPrivValidator() | |||||
privValidator.SetFile(file) | |||||
privValidator.Save() | |||||
} |
@ -0,0 +1,21 @@ | |||||
package commands | |||||
import ( | |||||
"fmt" | |||||
"github.com/spf13/cobra" | |||||
"github.com/tendermint/tendermint/version" | |||||
) | |||||
var versionCmd = &cobra.Command{ | |||||
Use: "version", | |||||
Short: "Show version info", | |||||
Run: func(cmd *cobra.Command, args []string) { | |||||
fmt.Println(version.Version) | |||||
}, | |||||
} | |||||
func init() { | |||||
RootCmd.AddCommand(versionCmd) | |||||
} |
@ -1,66 +0,0 @@ | |||||
package main | |||||
import ( | |||||
flag "github.com/spf13/pflag" | |||||
"os" | |||||
cfg "github.com/tendermint/go-config" | |||||
) | |||||
func parseFlags(config cfg.Config, args []string) { | |||||
var ( | |||||
printHelp bool | |||||
moniker string | |||||
nodeLaddr string | |||||
seeds string | |||||
fastSync bool | |||||
skipUPNP bool | |||||
rpcLaddr string | |||||
grpcLaddr string | |||||
logLevel string | |||||
proxyApp string | |||||
abciTransport string | |||||
pex bool | |||||
) | |||||
// Declare flags | |||||
var flags = flag.NewFlagSet("main", flag.ExitOnError) | |||||
flags.BoolVar(&printHelp, "help", false, "Print this help message.") | |||||
// configuration options | |||||
flags.StringVar(&moniker, "moniker", config.GetString("moniker"), "Node Name") | |||||
flags.StringVar(&nodeLaddr, "node_laddr", config.GetString("node_laddr"), "Node listen address. (0.0.0.0:0 means any interface, any port)") | |||||
flags.StringVar(&seeds, "seeds", config.GetString("seeds"), "Comma delimited host:port seed nodes") | |||||
flags.BoolVar(&fastSync, "fast_sync", config.GetBool("fast_sync"), "Fast blockchain syncing") | |||||
flags.BoolVar(&skipUPNP, "skip_upnp", config.GetBool("skip_upnp"), "Skip UPNP configuration") | |||||
flags.StringVar(&rpcLaddr, "rpc_laddr", config.GetString("rpc_laddr"), "RPC listen address. Port required") | |||||
flags.StringVar(&grpcLaddr, "grpc_laddr", config.GetString("grpc_laddr"), "GRPC listen address (BroadcastTx only). Port required") | |||||
flags.StringVar(&logLevel, "log_level", config.GetString("log_level"), "Log level") | |||||
flags.StringVar(&proxyApp, "proxy_app", config.GetString("proxy_app"), | |||||
"Proxy app address, or 'nilapp' or 'dummy' for local testing.") | |||||
flags.StringVar(&abciTransport, "abci", config.GetString("abci"), "Specify abci transport (socket | grpc)") | |||||
// feature flags | |||||
flags.BoolVar(&pex, "pex", config.GetBool("pex_reactor"), "Enable Peer-Exchange (dev feature)") | |||||
flags.Parse(args) | |||||
if printHelp { | |||||
flags.PrintDefaults() | |||||
os.Exit(0) | |||||
} | |||||
// Merge parsed flag values onto app. | |||||
config.Set("moniker", moniker) | |||||
config.Set("node_laddr", nodeLaddr) | |||||
config.Set("seeds", seeds) | |||||
config.Set("fast_sync", fastSync) | |||||
config.Set("skip_upnp", skipUPNP) | |||||
config.Set("rpc_laddr", rpcLaddr) | |||||
config.Set("grpc_laddr", grpcLaddr) | |||||
config.Set("log_level", logLevel) | |||||
config.Set("proxy_app", proxyApp) | |||||
config.Set("abci", abciTransport) | |||||
config.Set("pex_reactor", pex) | |||||
} |
@ -1,15 +0,0 @@ | |||||
package main | |||||
import ( | |||||
"fmt" | |||||
"github.com/tendermint/go-wire" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
func gen_validator() { | |||||
privValidator := types.GenPrivValidator() | |||||
privValidatorJSONBytes := wire.JSONBytesPretty(privValidator) | |||||
fmt.Printf(`%v | |||||
`, string(privValidatorJSONBytes)) | |||||
} |
@ -1,7 +0,0 @@ | |||||
package main | |||||
import ( | |||||
"github.com/tendermint/go-logger" | |||||
) | |||||
var log = logger.New("module", "main") |
@ -1,33 +0,0 @@ | |||||
package main | |||||
import ( | |||||
"os" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
// XXX: this is totally unsafe. | |||||
// it's only suitable for testnets. | |||||
func reset_all() { | |||||
reset_priv_validator() | |||||
os.RemoveAll(config.GetString("db_dir")) | |||||
os.Remove(config.GetString("cs_wal_file")) | |||||
} | |||||
// XXX: this is totally unsafe. | |||||
// it's only suitable for testnets. | |||||
func reset_priv_validator() { | |||||
// Get PrivValidator | |||||
var privValidator *types.PrivValidator | |||||
privValidatorFile := config.GetString("priv_validator_file") | |||||
if _, err := os.Stat(privValidatorFile); err == nil { | |||||
privValidator = types.LoadPrivValidator(privValidatorFile) | |||||
privValidator.Reset() | |||||
log.Notice("Reset PrivValidator", "file", privValidatorFile) | |||||
} else { | |||||
privValidator = types.GenPrivValidator() | |||||
privValidator.SetFile(privValidatorFile) | |||||
privValidator.Save() | |||||
log.Notice("Generated PrivValidator", "file", privValidatorFile) | |||||
} | |||||
} |
@ -1,59 +0,0 @@ | |||||
package main | |||||
import ( | |||||
"io/ioutil" | |||||
"time" | |||||
. "github.com/tendermint/go-common" | |||||
cfg "github.com/tendermint/go-config" | |||||
"github.com/tendermint/tendermint/node" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
// Users wishing to: | |||||
// * Use an external signer for their validators | |||||
// * Supply an in-proc abci app | |||||
// should import github.com/tendermint/tendermint/node and implement | |||||
// their own run_node to call node.NewNode (instead of node.NewNodeDefault) | |||||
// with their custom priv validator and/or custom proxy.ClientCreator | |||||
func run_node(config cfg.Config) { | |||||
// Wait until the genesis doc becomes available | |||||
// This is for Mintnet compatibility. | |||||
// TODO: If Mintnet gets deprecated or genesis_file is | |||||
// always available, remove. | |||||
genDocFile := config.GetString("genesis_file") | |||||
if !FileExists(genDocFile) { | |||||
log.Notice(Fmt("Waiting for genesis file %v...", genDocFile)) | |||||
for { | |||||
time.Sleep(time.Second) | |||||
if !FileExists(genDocFile) { | |||||
continue | |||||
} | |||||
jsonBlob, err := ioutil.ReadFile(genDocFile) | |||||
if err != nil { | |||||
Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) | |||||
} | |||||
genDoc, err := types.GenesisDocFromJSON(jsonBlob) | |||||
if err != nil { | |||||
Exit(Fmt("Error reading GenesisDoc: %v", err)) | |||||
} | |||||
if genDoc.ChainID == "" { | |||||
Exit(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile)) | |||||
} | |||||
config.Set("chain_id", genDoc.ChainID) | |||||
} | |||||
} | |||||
// Create & start node | |||||
n := node.NewNodeDefault(config) | |||||
if _, err := n.Start(); err != nil { | |||||
Exit(Fmt("Failed to start node: %v", err)) | |||||
} else { | |||||
log.Notice("Started node", "nodeInfo", n.Switch().NodeInfo()) | |||||
} | |||||
// Trap signal, run forever. | |||||
n.RunForever() | |||||
} |
@ -0,0 +1,43 @@ | |||||
package core | |||||
import ( | |||||
"fmt" | |||||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||||
"github.com/tendermint/tendermint/state/txindex/null" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { | |||||
// if index is disabled, return error | |||||
if _, ok := txIndexer.(*null.TxIndex); ok { | |||||
return nil, fmt.Errorf("Transaction indexing is disabled.") | |||||
} | |||||
r, err := txIndexer.Get(hash) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if r == nil { | |||||
return nil, fmt.Errorf("Tx (%X) not found", hash) | |||||
} | |||||
height := int(r.Height) // XXX | |||||
index := int(r.Index) | |||||
var proof types.TxProof | |||||
if prove { | |||||
block := blockStore.LoadBlock(height) | |||||
proof = block.Data.Txs.Proof(index) | |||||
} | |||||
return &ctypes.ResultTx{ | |||||
Height: height, | |||||
Index: index, | |||||
TxResult: r.Result, | |||||
Tx: r.Tx, | |||||
Proof: proof, | |||||
}, nil | |||||
} |
@ -0,0 +1,38 @@ | |||||
package core_types | |||||
import ( | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/tendermint/go-p2p" | |||||
) | |||||
func TestStatusIndexer(t *testing.T) { | |||||
assert := assert.New(t) | |||||
var status *ResultStatus | |||||
assert.False(status.TxIndexEnabled()) | |||||
status = &ResultStatus{} | |||||
assert.False(status.TxIndexEnabled()) | |||||
status.NodeInfo = &p2p.NodeInfo{} | |||||
assert.False(status.TxIndexEnabled()) | |||||
cases := []struct { | |||||
expected bool | |||||
other []string | |||||
}{ | |||||
{false, nil}, | |||||
{false, []string{}}, | |||||
{false, []string{"a=b"}}, | |||||
{false, []string{"tx_indexiskv", "some=dood"}}, | |||||
{true, []string{"tx_index=on", "tx_index=other"}}, | |||||
{true, []string{"^(*^(", "tx_index=on", "a=n=b=d="}}, | |||||
} | |||||
for _, tc := range cases { | |||||
status.NodeInfo.Other = tc.other | |||||
assert.Equal(tc.expected, status.TxIndexEnabled()) | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
#!/usr/bin/env bash | |||||
set -eu | |||||
VERSION=$1 | |||||
DIST_DIR=$2 # ./build/dist | |||||
# Get the version from the environment, or try to figure it out. | |||||
if [ -z $VERSION ]; then | |||||
VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) | |||||
fi | |||||
if [ -z "$VERSION" ]; then | |||||
echo "Please specify a version." | |||||
exit 1 | |||||
fi | |||||
# copy to s3 | |||||
aws s3 cp --recursive ${DIST_DIR} s3://tendermint/${VERSION} --acl public-read --exclude "*" --include "*.zip" | |||||
aws s3 cp ${DIST_DIR}/tendermint_${VERSION}_SHA256SUMS s3://tendermint/0.9.0 --acl public-read | |||||
exit 0 |
@ -0,0 +1,90 @@ | |||||
package state | |||||
import ( | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/stretchr/testify/require" | |||||
"github.com/tendermint/abci/example/dummy" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
dbm "github.com/tendermint/go-db" | |||||
cfg "github.com/tendermint/tendermint/config/tendermint_test" | |||||
"github.com/tendermint/tendermint/mempool" | |||||
"github.com/tendermint/tendermint/proxy" | |||||
"github.com/tendermint/tendermint/state/txindex" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var ( | |||||
privKey = crypto.GenPrivKeyEd25519FromSecret([]byte("execution_test")) | |||||
chainID = "execution_chain" | |||||
testPartSize = 65536 | |||||
nTxsPerBlock = 10 | |||||
) | |||||
func TestApplyBlock(t *testing.T) { | |||||
cc := proxy.NewLocalClientCreator(dummy.NewDummyApplication()) | |||||
config := cfg.ResetConfig("execution_test_") | |||||
proxyApp := proxy.NewAppConns(config, cc, nil) | |||||
_, err := proxyApp.Start() | |||||
require.Nil(t, err) | |||||
defer proxyApp.Stop() | |||||
mempool := mempool.NewMempool(config, proxyApp.Mempool()) | |||||
state := state() | |||||
indexer := &dummyIndexer{0} | |||||
state.TxIndexer = indexer | |||||
// make block | |||||
block := makeBlock(1, state) | |||||
err = state.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool) | |||||
require.Nil(t, err) | |||||
assert.Equal(t, nTxsPerBlock, indexer.Indexed) // test indexing works | |||||
// TODO check state and mempool | |||||
} | |||||
//---------------------------------------------------------------------------- | |||||
// make some bogus txs | |||||
func makeTxs(blockNum int) (txs []types.Tx) { | |||||
for i := 0; i < nTxsPerBlock; i++ { | |||||
txs = append(txs, types.Tx([]byte{byte(blockNum), byte(i)})) | |||||
} | |||||
return txs | |||||
} | |||||
func state() *State { | |||||
return MakeGenesisState(dbm.NewMemDB(), &types.GenesisDoc{ | |||||
ChainID: chainID, | |||||
Validators: []types.GenesisValidator{ | |||||
types.GenesisValidator{privKey.PubKey(), 10000, "test"}, | |||||
}, | |||||
AppHash: nil, | |||||
}) | |||||
} | |||||
func makeBlock(num int, state *State) *types.Block { | |||||
prevHash := state.LastBlockID.Hash | |||||
prevParts := types.PartSetHeader{} | |||||
valHash := state.Validators.Hash() | |||||
prevBlockID := types.BlockID{prevHash, prevParts} | |||||
block, _ := types.MakeBlock(num, chainID, makeTxs(num), new(types.Commit), | |||||
prevBlockID, valHash, state.AppHash, testPartSize) | |||||
return block | |||||
} | |||||
// dummyIndexer increments counter every time we index transaction. | |||||
type dummyIndexer struct { | |||||
Indexed int | |||||
} | |||||
func (indexer *dummyIndexer) Get(hash []byte) (*types.TxResult, error) { | |||||
return nil, nil | |||||
} | |||||
func (indexer *dummyIndexer) AddBatch(batch *txindex.Batch) error { | |||||
indexer.Indexed += batch.Size() | |||||
return nil | |||||
} |
@ -0,0 +1,57 @@ | |||||
package txindex | |||||
import ( | |||||
"errors" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
// Indexer interface defines methods to index and search transactions. | |||||
type TxIndexer interface { | |||||
// Batch analyzes, indexes or stores a batch of transactions. | |||||
// | |||||
// NOTE We do not specify Index method for analyzing a single transaction | |||||
// here because it bears heavy perfomance loses. Almost all advanced indexers | |||||
// support batching. | |||||
AddBatch(b *Batch) error | |||||
// Tx returns specified transaction or nil if the transaction is not indexed | |||||
// or stored. | |||||
Get(hash []byte) (*types.TxResult, error) | |||||
} | |||||
//---------------------------------------------------- | |||||
// Txs are written as a batch | |||||
// A Batch groups together multiple Index operations you would like performed | |||||
// at the same time. The Batch structure is NOT thread-safe. You should only | |||||
// perform operations on a batch from a single thread at a time. Once batch | |||||
// execution has started, you may not modify it. | |||||
type Batch struct { | |||||
Ops []types.TxResult | |||||
} | |||||
// NewBatch creates a new Batch. | |||||
func NewBatch(n int) *Batch { | |||||
return &Batch{ | |||||
Ops: make([]types.TxResult, n), | |||||
} | |||||
} | |||||
// Index adds or updates entry for the given result.Index. | |||||
func (b *Batch) Add(result types.TxResult) error { | |||||
b.Ops[result.Index] = result | |||||
return nil | |||||
} | |||||
// Size returns the total number of operations inside the batch. | |||||
func (b *Batch) Size() int { | |||||
return len(b.Ops) | |||||
} | |||||
//---------------------------------------------------- | |||||
// Errors | |||||
// ErrorEmptyHash indicates empty hash | |||||
var ErrorEmptyHash = errors.New("Transaction hash cannot be empty") |
@ -0,0 +1,56 @@ | |||||
package kv | |||||
import ( | |||||
"bytes" | |||||
"fmt" | |||||
db "github.com/tendermint/go-db" | |||||
"github.com/tendermint/go-wire" | |||||
"github.com/tendermint/tendermint/state/txindex" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
// TxIndex is the simplest possible indexer, backed by Key-Value storage (levelDB). | |||||
// It could only index transaction by its identifier. | |||||
type TxIndex struct { | |||||
store db.DB | |||||
} | |||||
// NewTxIndex returns new instance of TxIndex. | |||||
func NewTxIndex(store db.DB) *TxIndex { | |||||
return &TxIndex{store: store} | |||||
} | |||||
// Get gets transaction from the TxIndex storage and returns it or nil if the | |||||
// transaction is not found. | |||||
func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { | |||||
if len(hash) == 0 { | |||||
return nil, txindex.ErrorEmptyHash | |||||
} | |||||
rawBytes := txi.store.Get(hash) | |||||
if rawBytes == nil { | |||||
return nil, nil | |||||
} | |||||
r := bytes.NewReader(rawBytes) | |||||
var n int | |||||
var err error | |||||
txResult := wire.ReadBinary(&types.TxResult{}, r, 0, &n, &err).(*types.TxResult) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("Error reading TxResult: %v", err) | |||||
} | |||||
return txResult, nil | |||||
} | |||||
// Batch writes a batch of transactions into the TxIndex storage. | |||||
func (txi *TxIndex) AddBatch(b *txindex.Batch) error { | |||||
storeBatch := txi.store.NewBatch() | |||||
for _, result := range b.Ops { | |||||
rawBytes := wire.BinaryBytes(&result) | |||||
storeBatch.Set(result.Tx.Hash(), rawBytes) | |||||
} | |||||
storeBatch.Write() | |||||
return nil | |||||
} |
@ -0,0 +1,63 @@ | |||||
package kv | |||||
import ( | |||||
"io/ioutil" | |||||
"os" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/stretchr/testify/require" | |||||
abci "github.com/tendermint/abci/types" | |||||
db "github.com/tendermint/go-db" | |||||
"github.com/tendermint/tendermint/state/txindex" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
func TestTxIndex(t *testing.T) { | |||||
indexer := &TxIndex{store: db.NewMemDB()} | |||||
tx := types.Tx("HELLO WORLD") | |||||
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}} | |||||
hash := tx.Hash() | |||||
batch := txindex.NewBatch(1) | |||||
batch.Add(*txResult) | |||||
err := indexer.AddBatch(batch) | |||||
require.Nil(t, err) | |||||
loadedTxResult, err := indexer.Get(hash) | |||||
require.Nil(t, err) | |||||
assert.Equal(t, txResult, loadedTxResult) | |||||
} | |||||
func benchmarkTxIndex(txsCount int, b *testing.B) { | |||||
tx := types.Tx("HELLO WORLD") | |||||
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}} | |||||
dir, err := ioutil.TempDir("", "tx_index_db") | |||||
if err != nil { | |||||
b.Fatal(err) | |||||
} | |||||
defer os.RemoveAll(dir) | |||||
store := db.NewDB("tx_index", "leveldb", dir) | |||||
indexer := &TxIndex{store: store} | |||||
batch := txindex.NewBatch(txsCount) | |||||
for i := 0; i < txsCount; i++ { | |||||
txResult.Index += 1 | |||||
batch.Add(*txResult) | |||||
} | |||||
b.ResetTimer() | |||||
for n := 0; n < b.N; n++ { | |||||
err = indexer.AddBatch(batch) | |||||
} | |||||
} | |||||
func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) } | |||||
func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) } | |||||
func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) } | |||||
func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) } | |||||
func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) } |
@ -0,0 +1,21 @@ | |||||
package null | |||||
import ( | |||||
"errors" | |||||
"github.com/tendermint/tendermint/state/txindex" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
// TxIndex acts as a /dev/null. | |||||
type TxIndex struct{} | |||||
// Tx panics. | |||||
func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { | |||||
return nil, errors.New(`Indexing is disabled (set 'tx_index = "kv"' in config)`) | |||||
} | |||||
// Batch returns nil. | |||||
func (txi *TxIndex) AddBatch(batch *txindex.Batch) error { | |||||
return nil | |||||
} |
@ -0,0 +1,122 @@ | |||||
package types | |||||
import ( | |||||
"bytes" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
cmn "github.com/tendermint/go-common" | |||||
ctest "github.com/tendermint/go-common/test" | |||||
wire "github.com/tendermint/go-wire" | |||||
) | |||||
func makeTxs(cnt, size int) Txs { | |||||
txs := make(Txs, cnt) | |||||
for i := 0; i < cnt; i++ { | |||||
txs[i] = cmn.RandBytes(size) | |||||
} | |||||
return txs | |||||
} | |||||
func randInt(low, high int) int { | |||||
off := cmn.RandInt() % (high - low) | |||||
return low + off | |||||
} | |||||
func TestTxIndex(t *testing.T) { | |||||
assert := assert.New(t) | |||||
for i := 0; i < 20; i++ { | |||||
txs := makeTxs(15, 60) | |||||
for j := 0; j < len(txs); j++ { | |||||
tx := txs[j] | |||||
idx := txs.Index(tx) | |||||
assert.Equal(j, idx) | |||||
} | |||||
assert.Equal(-1, txs.Index(nil)) | |||||
assert.Equal(-1, txs.Index(Tx("foodnwkf"))) | |||||
} | |||||
} | |||||
func TestValidTxProof(t *testing.T) { | |||||
assert := assert.New(t) | |||||
cases := []struct { | |||||
txs Txs | |||||
}{ | |||||
{Txs{{1, 4, 34, 87, 163, 1}}}, | |||||
{Txs{{5, 56, 165, 2}, {4, 77}}}, | |||||
{Txs{Tx("foo"), Tx("bar"), Tx("baz")}}, | |||||
{makeTxs(20, 5)}, | |||||
{makeTxs(7, 81)}, | |||||
{makeTxs(61, 15)}, | |||||
} | |||||
for h, tc := range cases { | |||||
txs := tc.txs | |||||
root := txs.Hash() | |||||
// make sure valid proof for every tx | |||||
for i := range txs { | |||||
leaf := txs[i] | |||||
leafHash := leaf.Hash() | |||||
proof := txs.Proof(i) | |||||
assert.Equal(i, proof.Index, "%d: %d", h, i) | |||||
assert.Equal(len(txs), proof.Total, "%d: %d", h, i) | |||||
assert.Equal(root, proof.RootHash, "%d: %d", h, i) | |||||
assert.Equal(leaf, proof.Data, "%d: %d", h, i) | |||||
assert.Equal(leafHash, proof.LeafHash(), "%d: %d", h, i) | |||||
assert.Nil(proof.Validate(root), "%d: %d", h, i) | |||||
assert.NotNil(proof.Validate([]byte("foobar")), "%d: %d", h, i) | |||||
// read-write must also work | |||||
var p2 TxProof | |||||
bin := wire.BinaryBytes(proof) | |||||
err := wire.ReadBinaryBytes(bin, &p2) | |||||
if assert.Nil(err, "%d: %d: %+v", h, i, err) { | |||||
assert.Nil(p2.Validate(root), "%d: %d", h, i) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func TestTxProofUnchangable(t *testing.T) { | |||||
// run the other test a bunch... | |||||
for i := 0; i < 40; i++ { | |||||
testTxProofUnchangable(t) | |||||
} | |||||
} | |||||
func testTxProofUnchangable(t *testing.T) { | |||||
assert := assert.New(t) | |||||
// make some proof | |||||
txs := makeTxs(randInt(2, 100), randInt(16, 128)) | |||||
root := txs.Hash() | |||||
i := randInt(0, len(txs)-1) | |||||
proof := txs.Proof(i) | |||||
// make sure it is valid to start with | |||||
assert.Nil(proof.Validate(root)) | |||||
bin := wire.BinaryBytes(proof) | |||||
// try mutating the data and make sure nothing breaks | |||||
for j := 0; j < 500; j++ { | |||||
bad := ctest.MutateByteSlice(bin) | |||||
if !bytes.Equal(bad, bin) { | |||||
assertBadProof(t, root, bad, proof) | |||||
} | |||||
} | |||||
} | |||||
// this make sure the proof doesn't deserialize into something valid | |||||
func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) { | |||||
var proof TxProof | |||||
err := wire.ReadBinaryBytes(bad, &proof) | |||||
if err == nil { | |||||
err = proof.Validate(root) | |||||
if err == nil { | |||||
// okay, this can happen if we have a slightly different total | |||||
// (where the path ends up the same), if it is something else, we have | |||||
// a real problem | |||||
assert.NotEqual(t, proof.Total, good.Total, "bad: %#v\ngood: %#v", proof, good) | |||||
} | |||||
} | |||||
} |