diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf90ba87..850721728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 0.10.1 (June 28, 2017) + +FEATURES: +- Use `--trace` to get stack traces for logged errors +- types: GenesisDoc.ValidatorHash returns the hash of the genesis validator set +- types: GenesisDocFromFile parses a GenesiDoc from a JSON file + +IMPROVEMENTS: +- Add a Code of Conduct +- Variety of improvements as suggested by `megacheck` tool +- rpc: deduplicate tests between rpc/client and rpc/tests +- rpc: addresses without a protocol prefix default to `tcp://`. `http://` is also accepted as an alias for `tcp://` +- cmd: commands are more easily reuseable from other tools +- DOCKER: automate build/push + +BUG FIXES: +- Fix log statements using keys with spaces (logger does not currently support spaces) +- rpc: set logger on websocket connection +- rpc: fix ws connection stability by setting write deadline on pings + ## 0.10.0 (June 2, 2017) Includes major updates to configuration, logging, and json serialization. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..d47c0f15e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,56 @@ +# The Tendermint Code of Conduct +This code of conduct applies to all projects run by the Tendermint/COSMOS team and hence to tendermint. + + +---- + + +# Conduct +## Contact: adrian@tendermint.com + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. + +* On Slack, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. + +* Please be kind and courteous. There’s no need to be mean or rude. + +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. + +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. + +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups. + +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel admins or the person mentioned above immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. + +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome. + + +---- + + +# Moderation +These are the policies for upholding our community’s standards of conduct. If you feel that a thread needs moderation, please contact the above mentioned person. + +1. Remarks that violate the Tendermint/COSMOS standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) + +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. + +3. Moderators will first respond to such remarks with a warning. + +4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off. + +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. + +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. + +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, in private. Complaints about bans in-channel are not allowed. + +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. + +In the Tendermint/COSMOS community we strive to go the extra step to look out for each other. Don’t just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they’re off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make your fellow Cosmonauts comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. + +The enforcement policies listed above apply to all official Tendermint/COSMOS venues.For other projects adopting the Tendermint/COSMOS Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling), the [Contributor Covenant v1.3.0](http://contributor-covenant.org/version/1/3/0/) and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index a5f734f19..55f2e14ab 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -1,8 +1,8 @@ -FROM alpine:3.5 +FROM alpine:3.6 # This is the release of tendermint to pull in. -ENV TM_VERSION 0.9.1 -ENV TM_SHA256SUM da34234755937140dcd953afcc965555fad7e05afd546711bc5bdc2df3d54226 +ENV TM_VERSION 0.10.0 +ENV TM_SHA256SUM a29852b8d51c00db93c87c3d148fa419a047abd38f32b2507a905805131acc19 # Tendermint will be looking for genesis file in /tendermint (unless you change # `genesis_file` in config.toml). You can put your config.toml and private @@ -26,7 +26,7 @@ RUN mkdir -p $DATA_ROOT && \ RUN apk add --no-cache bash curl jq RUN apk add --no-cache openssl && \ - wget https://s3-us-west-2.amazonaws.com/tendermint/${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \ + wget https://s3-us-west-2.amazonaws.com/tendermint/binaries/tendermint/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \ echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \ unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \ apk del openssl && \ @@ -42,5 +42,4 @@ EXPOSE 46657 ENTRYPOINT ["tendermint"] -# By default you'll get the dummy app -CMD ["node", "--moniker=`hostname`", "--proxy_app=dummy"] +CMD ["node", "--moniker=`hostname`"] diff --git a/DOCKER/Dockerfile.develop b/DOCKER/Dockerfile.develop index 82cd884ae..2ffb68281 100644 --- a/DOCKER/Dockerfile.develop +++ b/DOCKER/Dockerfile.develop @@ -1,4 +1,4 @@ -FROM alpine:3.5 +FROM alpine:3.6 ENV DATA_ROOT /tendermint ENV TMHOME $DATA_ROOT diff --git a/DOCKER/Makefile b/DOCKER/Makefile index 612b9a694..10c972502 100644 --- a/DOCKER/Makefile +++ b/DOCKER/Makefile @@ -1,12 +1,8 @@ build: - # TAG=0.8.0 TAG_NO_PATCH=0.8 - docker build -t "tendermint/tendermint" -t "tendermint/tendermint:$(TAG)" -t "tendermint/tendermint:$(TAG_NO_PATCH)" . + @sh -c "'$(CURDIR)/build.sh'" push: - # TAG=0.8.0 TAG_NO_PATCH=0.8 - docker push "tendermint/tendermint:latest" - docker push "tendermint/tendermint:$(TAG)" - docker push "tendermint/tendermint:$(TAG_NO_PATCH)" + @sh -c "'$(CURDIR)/push.sh'" build_develop: docker build -t "tendermint/tendermint:develop" -f Dockerfile.develop . diff --git a/DOCKER/README.md b/DOCKER/README.md index a1f9f28c9..e191abc39 100644 --- a/DOCKER/README.md +++ b/DOCKER/README.md @@ -1,6 +1,7 @@ # Supported tags and respective `Dockerfile` links -- `0.9.1`, `0.9`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile) +- `0.10.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile) +- `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile) - `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile) - `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile) - `develop` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/master/DOCKER/Dockerfile.develop) @@ -23,12 +24,12 @@ A very simple example of a built-in app and Tendermint core in one container. ``` docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init -docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint +docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=dummy ``` ## mintnet-kubernetes -If you want to see many containers talking to each other, consider using [mintnet-kubernetes](https://github.com/tendermint/mintnet-kubernetes), which is a tool for running Tendermint-based applications on a Kubernetes cluster. +If you want to see many containers talking to each other, consider using [mintnet-kubernetes](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes), which is a tool for running Tendermint-based applications on a Kubernetes cluster. # Supported Docker versions diff --git a/DOCKER/build.sh b/DOCKER/build.sh new file mode 100755 index 000000000..ee617cc63 --- /dev/null +++ b/DOCKER/build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e + +# Get the tag from the version, or try to figure it out. +if [ -z "$TAG" ]; then + TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go) +fi +if [ -z "$TAG" ]; then + echo "Please specify a tag." + exit 1 +fi + +TAG_NO_PATCH=${TAG%.*} + +read -p "==> Build 3 docker images with the following tags (latest, $TAG, $TAG_NO_PATCH)? y/n" -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]] +then + docker build -t "tendermint/tendermint" -t "tendermint/tendermint:$TAG" -t "tendermint/tendermint:$TAG_NO_PATCH" . +fi diff --git a/DOCKER/push.sh b/DOCKER/push.sh new file mode 100755 index 000000000..32741dce8 --- /dev/null +++ b/DOCKER/push.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -e + +# Get the tag from the version, or try to figure it out. +if [ -z "$TAG" ]; then + TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go) +fi +if [ -z "$TAG" ]; then + echo "Please specify a tag." + exit 1 +fi + +TAG_NO_PATCH=${TAG%.*} + +read -p "==> Push 3 docker images with the following tags (latest, $TAG, $TAG_NO_PATCH)? y/n" -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]] +then + docker push "tendermint/tendermint:latest" + docker push "tendermint/tendermint:$TAG" + docker push "tendermint/tendermint:$TAG_NO_PATCH" +fi diff --git a/Makefile b/Makefile index 7848e4785..8c9c5214d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ GOTOOLS = \ github.com/mitchellh/gox \ - github.com/Masterminds/glide + github.com/Masterminds/glide \ + honnef.co/go/tools/cmd/megacheck + PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_TAGS?=tendermint TMHOME = $${TMHOME:-$$HOME/.tendermint} @@ -72,5 +74,10 @@ tools: ensure_tools: go get $(GOTOOLS) +### Formatting, linting, and vetting + +megacheck: + @for pkg in ${PACKAGES}; do megacheck "$$pkg"; done + .PHONY: install build build_race dist test test_race test_integrations test100 draw_deps list_deps get_deps get_vendor_deps update_deps revision tools diff --git a/README.md b/README.md index d37ef6357..ba99b620e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ For more background, see the [introduction](https://tendermint.com/intro). To get started developing applications, see the [application developers guide](https://tendermint.com/docs/guides/app-development). +### Code of Conduct +Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md). + ## Install To download pre-built binaries, see our [downloads page](https://tendermint.com/intro/getting-started/download). diff --git a/blockchain/pool.go b/blockchain/pool.go index a657b091e..e1288c9fa 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -28,7 +28,7 @@ var peerTimeoutSeconds = time.Duration(15) // not const so we can override with Every so often we ask peers what height they're on so we can keep going. Requests are continuously made for blocks of higher heights until - the limits. If most of the requests have no available peers, and we + the limit is reached. If most of the requests have no available peers, and we are not at peer limits, we can probably switch to consensus reactor */ @@ -129,8 +129,6 @@ func (pool *BlockPool) IsCaughtUp() bool { pool.mtx.Lock() defer pool.mtx.Unlock() - height := pool.height - // Need at least 1 peer to be considered caught up. if len(pool.peers) == 0 { pool.Logger.Debug("Blockpool has no peers") @@ -142,8 +140,11 @@ func (pool *BlockPool) IsCaughtUp() bool { maxPeerHeight = MaxInt(maxPeerHeight, peer.height) } - isCaughtUp := (height > 0 || time.Now().Sub(pool.startTime) > 5*time.Second) && (maxPeerHeight == 0 || height >= maxPeerHeight) - pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", height, "maxPeerHeight", maxPeerHeight) + // some conditions to determine if we're caught up + receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second) + ourChainIsLongestAmongPeers := maxPeerHeight == 0 || pool.height >= maxPeerHeight + isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers + pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", pool.height, "maxPeerHeight", maxPeerHeight) return isCaughtUp } @@ -261,7 +262,6 @@ func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer { if peer.didTimeout { pool.removePeer(peer.id) continue - } else { } if peer.numPending >= maxPendingRequestsPerPeer { continue @@ -303,6 +303,7 @@ func (pool *BlockPool) sendTimeout(peerID string) { pool.timeoutsCh <- peerID } +// unused by tendermint; left for debugging purposes func (pool *BlockPool) debug() string { pool.mtx.Lock() // Lock defer pool.mtx.Unlock() @@ -326,7 +327,6 @@ type bpPeer struct { id string recvMonitor *flow.Monitor - mtx sync.Mutex height int numPending int32 timeout *time.Timer diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 1c0ef3a7d..813d8f6b7 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -19,7 +19,6 @@ const ( BlockchainChannel = byte(0x40) defaultChannelCapacity = 100 - defaultSleepIntervalMS = 500 trySyncIntervalMS = 100 // stop syncing when last block's time is // within this much of the system time. @@ -49,7 +48,6 @@ type BlockchainReactor struct { fastSync bool requestsCh chan BlockRequest timeoutsCh chan string - lastBlock *types.Block evsw types.EventSwitch } @@ -128,12 +126,13 @@ func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) { func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) { _, msg, err := DecodeMessage(msgBytes) if err != nil { - bcR.Logger.Error("Error decoding message", "error", err) + bcR.Logger.Error("Error decoding message", "err", err) return } bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg) + // TODO: improve logic to satisfy megacheck switch msg := msg.(type) { case *bcBlockRequestMessage: // Got a request for a block. Respond with block if we have it. @@ -194,10 +193,10 @@ FOR_LOOP: if peer != nil { bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout")) } - case _ = <-statusUpdateTicker.C: + case <-statusUpdateTicker.C: // ask for status updates go bcR.BroadcastStatusRequest() - case _ = <-switchToConsensusTicker.C: + case <-switchToConsensusTicker.C: height, numPending, _ := bcR.pool.GetStatus() outbound, inbound, _ := bcR.Switch.NumPeers() bcR.Logger.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters), @@ -211,7 +210,7 @@ FOR_LOOP: break FOR_LOOP } - case _ = <-trySyncTicker.C: // chan time + case <-trySyncTicker.C: // chan time // This loop can be slow as long as it's doing syncing work. SYNC_LOOP: for i := 0; i < 10; i++ { @@ -231,7 +230,7 @@ FOR_LOOP: err := bcR.state.Validators.VerifyCommit( bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit) if err != nil { - bcR.Logger.Info("error in validation", "error", err) + bcR.Logger.Info("error in validation", "err", err) bcR.pool.RedoRequest(first.Height) break SYNC_LOOP } else { diff --git a/cmd/tendermint/commands/flags/log_level.go b/cmd/tendermint/commands/flags/log_level.go deleted file mode 100644 index 6821563ad..000000000 --- a/cmd/tendermint/commands/flags/log_level.go +++ /dev/null @@ -1,87 +0,0 @@ -package flags - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tmlibs/log" -) - -const ( - defaultLogLevelKey = "*" -) - -// ParseLogLevel parses complex log level - comma-separated -// list of module:level pairs with an optional *:level pair (* means -// all other modules). -// -// Example: -// ParseLogLevel("consensus:debug,mempool:debug,*:error", log.NewTMLogger(os.Stdout)) -func ParseLogLevel(lvl string, logger log.Logger) (log.Logger, error) { - if lvl == "" { - return nil, errors.New("Empty log level") - } - - l := lvl - - // prefix simple one word levels (e.g. "info") with "*" - if !strings.Contains(l, ":") { - l = defaultLogLevelKey + ":" + l - } - - options := make([]log.Option, 0) - - isDefaultLogLevelSet := false - var option log.Option - var err error - - list := strings.Split(l, ",") - for _, item := range list { - moduleAndLevel := strings.Split(item, ":") - - if len(moduleAndLevel) != 2 { - return nil, fmt.Errorf("Expected list in a form of \"module:level\" pairs, given pair %s, list %s", item, list) - } - - module := moduleAndLevel[0] - level := moduleAndLevel[1] - - if module == defaultLogLevelKey { - option, err = log.AllowLevel(level) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("Failed to parse default log level (pair %s, list %s)", item, l)) - } - options = append(options, option) - isDefaultLogLevelSet = true - } else { - switch level { - case "debug": - option = log.AllowDebugWith("module", module) - case "info": - option = log.AllowInfoWith("module", module) - case "error": - option = log.AllowErrorWith("module", module) - case "none": - option = log.AllowNoneWith("module", module) - default: - return nil, fmt.Errorf("Expected either \"info\", \"debug\", \"error\" or \"none\" log level, given %s (pair %s, list %s)", level, item, list) - } - options = append(options, option) - - } - } - - // if "*" is not provided, set default global level - if !isDefaultLogLevelSet { - option, err = log.AllowLevel(cfg.DefaultLogLevel()) - if err != nil { - return nil, err - } - options = append(options, option) - } - - return log.NewFilter(logger, options...), nil -} diff --git a/cmd/tendermint/commands/flags/log_level_test.go b/cmd/tendermint/commands/flags/log_level_test.go deleted file mode 100644 index c89f3f880..000000000 --- a/cmd/tendermint/commands/flags/log_level_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package flags_test - -import ( - "bytes" - "strings" - "testing" - - tmflags "github.com/tendermint/tendermint/cmd/tendermint/commands/flags" - "github.com/tendermint/tmlibs/log" -) - -func TestParseLogLevel(t *testing.T) { - var buf bytes.Buffer - jsonLogger := log.NewTMJSONLogger(&buf) - - correctLogLevels := []struct { - lvl string - expectedLogLines []string - }{ - {"mempool:error", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, - {"mempool:error,*:debug", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, - {"*:debug,wire:none", []string{ - `{"_msg":"Kingpin","level":"debug","module":"mempool"}`, - `{"_msg":"Kitty Pryde","level":"info","module":"mempool"}`, - `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, - } - - for _, c := range correctLogLevels { - logger, err := tmflags.ParseLogLevel(c.lvl, jsonLogger) - if err != nil { - t.Fatal(err) - } - - logger = logger.With("module", "mempool") - - buf.Reset() - - logger.Debug("Kingpin") - if have := strings.TrimSpace(buf.String()); c.expectedLogLines[0] != have { - t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[0], have, c.lvl) - } - - buf.Reset() - - logger.Info("Kitty Pryde") - if have := strings.TrimSpace(buf.String()); c.expectedLogLines[1] != have { - t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[1], have, c.lvl) - } - - buf.Reset() - - logger.Error("Mesmero") - if have := strings.TrimSpace(buf.String()); c.expectedLogLines[2] != have { - t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[2], have, c.lvl) - } - } - - incorrectLogLevel := []string{"some", "mempool:some", "*:some,mempool:error"} - for _, lvl := range incorrectLogLevel { - if _, err := tmflags.ParseLogLevel(lvl, jsonLogger); err == nil { - t.Fatalf("Expected %s to produce error", lvl) - } - } -} diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index 3565f3bb8..a63d9e46b 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -6,8 +6,9 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - tmflags "github.com/tendermint/tendermint/cmd/tendermint/commands/flags" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tmlibs/cli" + tmflags "github.com/tendermint/tmlibs/cli/flags" "github.com/tendermint/tmlibs/log" ) @@ -20,20 +21,33 @@ func init() { RootCmd.PersistentFlags().String("log_level", config.LogLevel, "Log level") } +// ParseConfig will setup the tendermint configuration properly +func ParseConfig() (*cfg.Config, error) { + conf := cfg.DefaultConfig() + err := viper.Unmarshal(conf) + if err != nil { + return nil, err + } + conf.SetRoot(conf.RootDir) + cfg.EnsureRoot(conf.RootDir) + return conf, err +} + var RootCmd = &cobra.Command{ Use: "tendermint", Short: "Tendermint Core (BFT Consensus) in Go", - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - err := viper.Unmarshal(config) + PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { + config, err = ParseConfig() if err != nil { return err } - config.SetRoot(config.RootDir) - cfg.EnsureRoot(config.RootDir) - logger, err = tmflags.ParseLogLevel(config.LogLevel, logger) + logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) if err != nil { return err } + if viper.GetBool(cli.TraceFlag) { + logger = log.NewTracingLogger(logger) + } return nil }, } diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index d476ccd57..e99b8609a 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -2,7 +2,6 @@ package commands import ( "fmt" - "io/ioutil" "time" "github.com/spf13/cobra" @@ -19,28 +18,33 @@ var runNodeCmd = &cobra.Command{ } func init() { + AddNodeFlags(runNodeCmd) + RootCmd.AddCommand(runNodeCmd) +} + +// AddNodeFlags exposes some common configuration options on the command-line +// These are exposed for convenience of commands embedding a tendermint node +func AddNodeFlags(cmd *cobra.Command) { // bind flags - runNodeCmd.Flags().String("moniker", config.Moniker, "Node Name") + cmd.Flags().String("moniker", config.Moniker, "Node Name") // node flags - runNodeCmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") + cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") // abci flags - runNodeCmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.") - runNodeCmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") + cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.") + cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") // rpc flags - runNodeCmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") - runNodeCmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required") - runNodeCmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods") + cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") + cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required") + cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods") // p2p flags - runNodeCmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") - runNodeCmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") - runNodeCmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") - runNodeCmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)") - - RootCmd.AddCommand(runNodeCmd) + cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") + cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") + cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") + cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)") } // Users wishing to: @@ -56,27 +60,16 @@ func runNode(cmd *cobra.Command, args []string) error { // TODO: If Mintnet gets deprecated or genesis_file is // always available, remove. genDocFile := config.GenesisFile() - if !cmn.FileExists(genDocFile) { + for !cmn.FileExists(genDocFile) { logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile)) - for { - time.Sleep(time.Second) - if !cmn.FileExists(genDocFile) { - continue - } - jsonBlob, err := ioutil.ReadFile(genDocFile) - if err != nil { - return fmt.Errorf("Couldn't read GenesisDoc file: %v", err) - } - genDoc, err := types.GenesisDocFromJSON(jsonBlob) - if err != nil { - return fmt.Errorf("Error reading GenesisDoc: %v", err) - } - if genDoc.ChainID == "" { - return fmt.Errorf("Genesis doc %v must include non-empty chain_id", genDocFile) - } - config.ChainID = genDoc.ChainID - } + time.Sleep(time.Second) + } + + genDoc, err := types.GenesisDocFromFile(genDocFile) + if err != nil { + return err } + config.ChainID = genDoc.ChainID // Create & start node n := node.NewNodeDefault(config, logger.With("module", "node")) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 56aeeeeaa..3a68a2f5d 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -77,6 +77,7 @@ func TestByzantine(t *testing.T) { var conRI p2p.Reactor conRI = conR + if i == 0 { conRI = NewByzantineReactor(conR) } @@ -116,10 +117,7 @@ func TestByzantine(t *testing.T) { p2p.Connect2Switches(switches, ind1, ind2) // wait for someone in the big partition to make a block - - select { - case <-eventChans[ind2]: - } + <-eventChans[ind2] t.Log("A block has been committed. Healing partition") diff --git a/consensus/common.go b/consensus/common.go index 6f76d1887..1e16c4dab 100644 --- a/consensus/common.go +++ b/consensus/common.go @@ -27,3 +27,9 @@ func subscribeToEventRespond(evsw types.EventSwitch, receiver, eventID string) c }) return ch } + +func discardFromChan(ch chan interface{}, n int) { + for i := 0; i < n; i++ { + <-ch + } +} diff --git a/consensus/common_test.go b/consensus/common_test.go index ae6e399d5..103294ab2 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -222,17 +222,6 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { return voteCh } -func readVotes(ch chan interface{}, reads int) chan struct{} { - wg := make(chan struct{}) - go func() { - for i := 0; i < reads; i++ { - <-ch // read the precommit event - } - close(wg) - }() - return wg -} - //------------------------------------------------------------------------------- // consensus states @@ -274,16 +263,6 @@ func loadPrivValidator(config *cfg.Config) *types.PrivValidator { return privValidator } -func fixedConsensusState() *ConsensusState { - stateDB := dbm.NewMemDB() - state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) - state.SetLogger(log.TestingLogger().With("module", "state")) - privValidator := loadPrivValidator(config) - cs := newConsensusState(state, privValidator, counter.NewCounterApplication(true)) - cs.SetLogger(log.TestingLogger()) - return cs -} - func fixedConsensusStateDummy() *ConsensusState { stateDB := dbm.NewMemDB() state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile()) diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index f1ef8b7ff..29751b40a 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/height_vote_set_test.go @@ -30,6 +30,9 @@ func TestPeerCatchupRounds(t *testing.T) { vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0) added, err = hvs.AddVote(vote1001_0, "peer1") + if err != nil { + t.Error("AddVote error", err) + } if added { t.Error("Expected to *not* add vote from peer, too many catchup rounds.") } diff --git a/consensus/reactor.go b/consensus/reactor.go index 3652697b5..50207ed52 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -154,7 +154,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) _, msg, err := DecodeMessage(msgBytes) if err != nil { - conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "error", err, "bytes", msgBytes) + conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) // TODO punish peer? return } @@ -282,7 +282,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) } if err != nil { - conR.Logger.Error("Error in Receive()", "error", err) + conR.Logger.Error("Error in Receive()", "err", err) } } @@ -351,7 +351,7 @@ func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg * Height: rs.Height, Round: rs.Round, Step: rs.Step, - SecondsSinceStartTime: int(time.Now().Sub(rs.StartTime).Seconds()), + SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } if rs.Step == RoundStepCommit { @@ -412,7 +412,7 @@ OUTER_LOOP: // Ensure that the peer's PartSetHeader is correct blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) if blockMeta == nil { - logger.Error("Failed to load block meta", "peer height", prs.Height, "our height", rs.Height, "blockstore height", conR.conS.blockStore.Height(), "pv", conR.conS.privValidator) + logger.Error("Failed to load block meta", "peer height", prs.Height, "ourHeight", rs.Height, "blockstoreHeight", conR.conS.blockStore.Height(), "pv", conR.conS.privValidator) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } else if !blockMeta.BlockID.PartsHeader.Equals(prs.ProposalBlockPartsHeader) { diff --git a/consensus/replay.go b/consensus/replay.go index af30b8894..c4ed684c7 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -132,7 +132,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int) error { if !found { // if we upgraded from 0.9 to 0.9.1, we may have #HEIGHT instead // TODO (0.10.0): remove this - gr, found, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight)) + gr, _, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight)) if err == io.EOF { cs.Logger.Error("Replay: wal.group.Search returned EOF", "#HEIGHT", csHeight) return nil diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 23290a7f5..78cdaf7bc 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -557,67 +557,6 @@ func readPieceFromWAL(msgBytes []byte) (interface{}, error) { return nil, nil } -// make some bogus txs -func txsFunc(blockNum int) (txs []types.Tx) { - for i := 0; i < 10; i++ { - txs = append(txs, types.Tx([]byte{byte(blockNum), byte(i)})) - } - return txs -} - -// sign a commit vote -func signCommit(chainID string, privVal *types.PrivValidator, height, round int, hash []byte, header types.PartSetHeader) *types.Vote { - vote := &types.Vote{ - ValidatorIndex: 0, - ValidatorAddress: privVal.Address, - Height: height, - Round: round, - Type: types.VoteTypePrecommit, - BlockID: types.BlockID{hash, header}, - } - - sig := privVal.Sign(types.SignBytes(chainID, vote)) - vote.Signature = sig - return vote -} - -// make a blockchain with one validator -func makeBlockchain(t *testing.T, chainID string, nBlocks int, privVal *types.PrivValidator, proxyApp proxy.AppConns, state *sm.State) (blockchain []*types.Block, commits []*types.Commit) { - - prevHash := state.LastBlockID.Hash - lastCommit := new(types.Commit) - prevParts := types.PartSetHeader{} - valHash := state.Validators.Hash() - prevBlockID := types.BlockID{prevHash, prevParts} - - for i := 1; i < nBlocks+1; i++ { - block, parts := types.MakeBlock(i, chainID, txsFunc(i), lastCommit, - prevBlockID, valHash, state.AppHash, testPartSize) - fmt.Println(i) - fmt.Println(block.LastBlockID) - err := state.ApplyBlock(nil, proxyApp.Consensus(), block, block.MakePartSet(testPartSize).Header(), mempool) - if err != nil { - t.Fatal(i, err) - } - - voteSet := types.NewVoteSet(chainID, i, 0, types.VoteTypePrecommit, state.Validators) - vote := signCommit(chainID, privVal, i, 0, block.Hash(), parts.Header()) - _, err = voteSet.AddVote(vote) - if err != nil { - t.Fatal(err) - } - - prevHash = block.Hash() - prevParts = parts.Header() - lastCommit = voteSet.MakeCommit() - prevBlockID = types.BlockID{prevHash, prevParts} - - blockchain = append(blockchain, block) - commits = append(commits, lastCommit) - } - return blockchain, commits -} - // fresh state and mock store func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBlockStore) { stateDB := dbm.NewMemDB() diff --git a/consensus/state.go b/consensus/state.go index d4056facf..cc9cd51e7 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -311,7 +311,7 @@ func (cs *ConsensusState) OnStart() error { walFile := cs.config.WalFile() if err := cs.OpenWAL(walFile); err != nil { - cs.Logger.Error("Error loading ConsensusState wal", "error", err.Error()) + cs.Logger.Error("Error loading ConsensusState wal", "err", err.Error()) return err } @@ -325,7 +325,7 @@ func (cs *ConsensusState) OnStart() error { // we may have lost some votes if the process crashed // reload from consensus log to catchup if err := cs.catchupReplay(cs.Height); err != nil { - cs.Logger.Error("Error on catchup replay. Proceeding to start ConsensusState anyway", "error", err.Error()) + cs.Logger.Error("Error on catchup replay. Proceeding to start ConsensusState anyway", "err", err.Error()) // NOTE: if we ever do return an error here, // make sure to stop the timeoutTicker } @@ -368,7 +368,7 @@ func (cs *ConsensusState) Wait() { func (cs *ConsensusState) OpenWAL(walFile string) (err error) { err = cmn.EnsureDir(path.Dir(walFile), 0700) if err != nil { - cs.Logger.Error("Error ensuring ConsensusState wal dir", "error", err.Error()) + cs.Logger.Error("Error ensuring ConsensusState wal dir", "err", err.Error()) return err } @@ -663,7 +663,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo, rs RoundState) { cs.Logger.Error("Unknown msg type", reflect.TypeOf(msg)) } if err != nil { - cs.Logger.Error("Error with msg", "type", reflect.TypeOf(msg), "peer", peerKey, "error", err, "msg", msg) + cs.Logger.Error("Error with msg", "type", reflect.TypeOf(msg), "peer", peerKey, "err", err, "msg", msg) } } @@ -831,7 +831,7 @@ func (cs *ConsensusState) defaultDecideProposal(height, round int) { cs.Logger.Debug(cmn.Fmt("Signed proposal block: %v", block)) } else { if !cs.replayMode { - cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "error", err) + cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "err", err) } } } @@ -930,7 +930,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int, round int) { err := cs.state.ValidateBlock(cs.ProposalBlock) if err != nil { // ProposalBlock is invalid, prevote nil. - cs.Logger.Error("enterPrevote: ProposalBlock is invalid", "error", err) + cs.Logger.Error("enterPrevote: ProposalBlock is invalid", "err", err) cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) return } @@ -939,7 +939,6 @@ func (cs *ConsensusState) defaultDoPrevote(height int, round int) { // NOTE: the proposal signature is validated when it is received, // and the proposal block parts are validated as they are received (against the merkle hash in the proposal) cs.signAddVote(types.VoteTypePrevote, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header()) - return } // Enter: any +2/3 prevotes at next round. @@ -1059,7 +1058,6 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) { } types.FireEventUnlock(cs.evsw, cs.RoundStateEvent()) cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) - return } // Enter: any +2/3 precommits for next round. @@ -1218,7 +1216,7 @@ func (cs *ConsensusState) finalizeCommit(height int) { // NOTE: the block.AppHash wont reflect these txs until the next block err := stateCopy.ApplyBlock(eventCache, cs.proxyAppConn, block, blockParts.Header(), cs.mempool) if err != nil { - cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "error", err) + cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "err", err) return } @@ -1250,7 +1248,6 @@ func (cs *ConsensusState) finalizeCommit(height int) { // * cs.Height has been increment to height+1 // * cs.Step is now RoundStepNewHeight // * cs.StartTime is set to when we will start round0. - return } //----------------------------------------------------------------------------- @@ -1350,7 +1347,7 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error { return err } else { // Probably an invalid signature. Bad peer. - cs.Logger.Error("Error attempting to add vote", "error", err) + cs.Logger.Error("Error attempting to add vote", "err", err) return ErrAddingVote } } @@ -1360,7 +1357,7 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerKey string) error { //----------------------------------------------------------------------------- func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, err error) { - cs.Logger.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "csHeight", cs.Height) + cs.Logger.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "valIndex", vote.ValidatorIndex, "csHeight", cs.Height) // A precommit for the previous height? // These come in while we wait timeoutCommit @@ -1491,11 +1488,11 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part vote, err := cs.signVote(type_, hash, header) if err == nil { cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) - cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) + cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) return vote } else { //if !cs.replayMode { - cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) + cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) //} return nil } diff --git a/consensus/state_test.go b/consensus/state_test.go index 2606685a4..81ef016be 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -523,7 +523,8 @@ func TestLockPOLRelock(t *testing.T) { <-voteCh // prevote signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4) - _, _, _ = <-voteCh, <-voteCh, <-voteCh // prevotes + // prevotes + discardFromChan(voteCh, 3) <-voteCh // our precommit // the proposed block should now be locked and our precommit added @@ -532,7 +533,8 @@ func TestLockPOLRelock(t *testing.T) { // add precommits from the rest signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4) signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3) - _, _, _ = <-voteCh, <-voteCh, <-voteCh // precommits + // precommites + discardFromChan(voteCh, 3) // before we timeout to the new round set the new proposal prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) @@ -544,7 +546,7 @@ func TestLockPOLRelock(t *testing.T) { // timeout to new round <-timeoutWaitCh - //XXX: this isnt gauranteed to get there before the timeoutPropose ... + //XXX: this isnt guaranteed to get there before the timeoutPropose ... cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer") <-newRoundCh @@ -570,7 +572,8 @@ func TestLockPOLRelock(t *testing.T) { // now lets add prevotes from everyone else for the new block signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) - _, _, _ = <-voteCh, <-voteCh, <-voteCh // prevotes + // prevotes + discardFromChan(voteCh, 3) // now either we go to PrevoteWait or Precommit select { @@ -585,7 +588,7 @@ func TestLockPOLRelock(t *testing.T) { validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash) signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propBlockParts.Header(), vs2, vs3) - _, _ = <-voteCh, <-voteCh + discardFromChan(voteCh, 2) be := <-newBlockCh b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader) @@ -655,7 +658,7 @@ func TestLockPOLUnlock(t *testing.T) { rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) lockedBlockHash := rs.LockedBlock.Hash() - //XXX: this isnt gauranteed to get there before the timeoutPropose ... + //XXX: this isnt guaranteed to get there before the timeoutPropose ... cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer") <-newRoundCh @@ -742,7 +745,7 @@ func TestLockPOLSafety1(t *testing.T) { incrementRound(vs2, vs3, vs4) - //XXX: this isnt gauranteed to get there before the timeoutPropose ... + //XXX: this isnt guaranteed to get there before the timeoutPropose ... cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer") <-newRoundCh diff --git a/consensus/ticker.go b/consensus/ticker.go index e869cdef1..e47a3412a 100644 --- a/consensus/ticker.go +++ b/consensus/ticker.go @@ -117,7 +117,7 @@ func (t *timeoutTicker) timeoutRoutine() { t.Logger.Debug("Scheduled timeout", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) case <-t.timer.C: t.Logger.Info("Timed out", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) - // go routine here gaurantees timeoutRoutine doesn't block. + // go routine here guarantees timeoutRoutine doesn't block. // Determinism comes from playback in the receiveRoutine. // We can eliminate it by merging the timeoutRoutine into receiveRoutine // and managing the timeouts ourselves with a millisecond ticker diff --git a/glide.lock b/glide.lock index 195386500..c1bf21c14 100644 --- a/glide.lock +++ b/glide.lock @@ -1,20 +1,18 @@ -hash: 93f15c9766ea826c29a91f545c42172eafd8c61e39c1d81617114ad1a9c9eaf2 -updated: 2017-05-18T06:13:24.295793122-04:00 +hash: 2c988aae9517b386ee911e4da5deb9f5034359b7e2ccf448952a3ddb9771222d +updated: 2017-06-28T13:04:20.907047164+02:00 imports: - name: github.com/btcsuite/btcd - version: 53f55a46349aa8f44b90895047e843666991cf24 + version: b8df516b4b267acf2de46be593a9d948d1d2c420 subpackages: - btcec -- name: github.com/davecgh/go-spew - version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 - subpackages: - - spew +- name: github.com/btcsuite/fastsha256 + version: 637e656429416087660c84436a2a035d69d54e2e - name: github.com/ebuchman/fail-test version: 95f809107225be108efcf10a3509e4ea6ceef3c4 - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/go-kit/kit - version: 6964666de57c88f7d93da127e900d201b632f561 + version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8 subpackages: - log - log/level @@ -22,13 +20,13 @@ imports: - name: github.com/go-logfmt/logfmt version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 - name: github.com/go-stack/stack - version: 7a2f19628aabfe68f0766b59e74d6315f8347d22 + version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 - name: github.com/gogo/protobuf version: 9df9efe4c742f1a2bfdedf1c3b6902fc6e814c6b subpackages: - proto - name: github.com/golang/protobuf - version: fec3b39b059c0f88fa6b20f5ed012b1aa203a8b4 + version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 subpackages: - proto - ptypes/any @@ -60,13 +58,9 @@ imports: - name: github.com/pelletier/go-buffruneio version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml - version: 5c26a6ff6fd178719e15decac1c8196da0d7d6d1 + version: 5ccdfb18c776b740aecaf085c4d9a2779199c279 - name: github.com/pkg/errors - version: c605e284fe17294bda444b34710735b29d1a9d90 -- name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d - subpackages: - - difflib + version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/spf13/afero version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: @@ -81,11 +75,6 @@ imports: version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 - name: github.com/spf13/viper version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 -- name: github.com/stretchr/testify - version: 4d4bfba8f1d1027c4fdbe371823030df51419987 - subpackages: - - assert - - require - name: github.com/syndtr/goleveldb version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4 subpackages: @@ -115,24 +104,25 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-crypto - version: 7dff40942a64cdeefefa9446b2d104750b349f8a + version: 95b7c9e09c49b91bfbb71bb63dd514eb55450f16 - name: github.com/tendermint/go-wire version: 5f88da3dbc1a72844e6dfaf274ce87f851d488eb subpackages: - data - data/base58 - name: github.com/tendermint/merkleeyes - version: a0e73e1ac3e18e12a007520a4ea2c9822256e307 + version: 102aaf5a8ffda1846413fb22805a94def2045b9f subpackages: - app - client - iavl - testutil - name: github.com/tendermint/tmlibs - version: 306795ae1d8e4f4a10dcc8bdb32a00455843c9d5 + version: 7ce4da1eee6004d627e780c8fe91e96d9b99e459 subpackages: - autofile - cli + - cli/flags - clist - common - db @@ -142,7 +132,7 @@ imports: - merkle - test - name: golang.org/x/crypto - version: 0fe963104e9d1877082f8fb38f816fcd97eb1d10 + version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e subpackages: - curve25519 - nacl/box @@ -153,7 +143,7 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/net - version: 513929065c19401a1c7b76ecd942f9f86a0c061b + version: feeb485667d1fdabe727840fe00adc22431bc86e subpackages: - context - http2 @@ -167,18 +157,18 @@ imports: subpackages: - unix - name: golang.org/x/text - version: 19e51611da83d6be54ddafce4a4af510cb3e9ea4 + version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4 subpackages: - secure/bidirule - transform - unicode/bidi - unicode/norm - name: google.golang.org/genproto - version: bb3573be0c484136831138976d444b8754777aff + version: 411e09b969b1170a9f0c467558eb4c4c110d9c77 subpackages: - googleapis/rpc/status - name: google.golang.org/grpc - version: 11d93ecdb918872ee841ba3a2dc391aa6d4f57c3 + version: 844f573616520565fdc6fb4db242321b5456fd6d subpackages: - codes - credentials @@ -195,4 +185,17 @@ imports: - transport - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b -testImports: [] +testImports: +- name: github.com/davecgh/go-spew + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require diff --git a/glide.yaml b/glide.yaml index 63acdcbaa..9bf95b508 100644 --- a/glide.yaml +++ b/glide.yaml @@ -7,13 +7,13 @@ import: - package: github.com/golang/protobuf subpackages: - proto +- package: github.com/pelletier/go-toml + version: ^1.0.0 - package: github.com/gorilla/websocket - package: github.com/pkg/errors + version: ~0.8.0 - package: github.com/spf13/cobra - package: github.com/spf13/viper -- package: github.com/stretchr/testify - subpackages: - - require - package: github.com/tendermint/abci version: v0.5.0 subpackages: @@ -21,13 +21,19 @@ import: - example/dummy - types - package: github.com/tendermint/go-crypto - version: v0.2.0 + version: ~0.2.2 - package: github.com/tendermint/go-wire - version: v0.6.2 + version: ~0.6.2 subpackages: - data +- package: github.com/tendermint/merkleeyes + version: ~0.2.4 + subpackages: + - app + - iavl + - testutil - package: github.com/tendermint/tmlibs - version: v0.2.0 + version: ~0.2.2 subpackages: - autofile - cli @@ -48,9 +54,7 @@ import: - context - package: google.golang.org/grpc testImport: -- package: github.com/tendermint/merkleeyes - version: develop +- package: github.com/stretchr/testify subpackages: - - app - - iavl - - testutil + - assert + - require diff --git a/mempool/mempool.go b/mempool/mempool.go index 9e53108e5..fd922ed8e 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -409,7 +409,7 @@ func (cache *txCache) Push(tx types.Tx) bool { popped := cache.list.Front() poppedTx := popped.Value.(types.Tx) // NOTE: the tx may have already been removed from the map - // but deleting a non-existant element is fine + // but deleting a non-existent element is fine delete(cache.map_, string(poppedTx)) cache.list.Remove(popped) } diff --git a/mempool/reactor.go b/mempool/reactor.go index 842e11538..25806c00f 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -63,7 +63,7 @@ func (memR *MempoolReactor) RemovePeer(peer *p2p.Peer, reason interface{}) { func (memR *MempoolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) { _, msg, err := DecodeMessage(msgBytes) if err != nil { - memR.Logger.Error("Error decoding message", "error", err) + memR.Logger.Error("Error decoding message", "err", err) return } memR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) diff --git a/node/node.go b/node/node.go index 12f683153..672e384b9 100644 --- a/node/node.go +++ b/node/node.go @@ -205,7 +205,7 @@ func NewNode(config *cfg.Config, privValidator *types.PrivValidator, clientCreat if profileHost != "" { go func() { - logger.Error("Profile server", "error", http.ListenAndServe(profileHost, nil)) + logger.Error("Profile server", "err", http.ListenAndServe(profileHost, nil)) }() } @@ -276,7 +276,7 @@ func (n *Node) OnStop() { for _, l := range n.rpcListeners { n.Logger.Info("Closing rpc listener", "listener", l) if err := l.Close(); err != nil { - n.Logger.Error("Error closing listener", "listener", l, "error", err) + n.Logger.Error("Error closing listener", "listener", l, "err", err) } } } diff --git a/p2p/addrbook.go b/p2p/addrbook.go index 1df0817ee..62b25a710 100644 --- a/p2p/addrbook.go +++ b/p2p/addrbook.go @@ -68,9 +68,6 @@ const ( // max addresses returned by GetSelection // NOTE: this must match "maxPexMessageSize" maxGetSelection = 250 - - // current version of the on-disk format. - serializationVersion = 1 ) const ( @@ -340,7 +337,7 @@ func (a *AddrBook) saveToFile(filePath string) { } err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644) if err != nil { - a.Logger.Error("Failed to save AddrBook to file", "file", filePath, "error", err) + a.Logger.Error("Failed to save AddrBook to file", "file", filePath, "err", err) } } diff --git a/p2p/connection.go b/p2p/connection.go index 36f15abb7..67c9d98f9 100644 --- a/p2p/connection.go +++ b/p2p/connection.go @@ -180,7 +180,7 @@ func (c *MConnection) flush() { c.Logger.Debug("Flush", "conn", c) err := c.bufWriter.Flush() if err != nil { - c.Logger.Error("MConnection flush failed", "error", err) + c.Logger.Error("MConnection flush failed", "err", err) } } @@ -318,7 +318,7 @@ FOR_LOOP: break FOR_LOOP } if err != nil { - c.Logger.Error("Connection failed @ sendRoutine", "conn", c, "error", err) + c.Logger.Error("Connection failed @ sendRoutine", "conn", c, "err", err) c.stopForError(err) break FOR_LOOP } @@ -373,7 +373,7 @@ func (c *MConnection) sendMsgPacket() bool { // Make & send a msgPacket from this channel n, err := leastChannel.writeMsgPacketTo(c.bufWriter) if err != nil { - c.Logger.Error("Failed to write msgPacket", "error", err) + c.Logger.Error("Failed to write msgPacket", "err", err) c.stopForError(err) return true } @@ -401,7 +401,7 @@ FOR_LOOP: if err == nil { return bytes } else { - log.Warn("Error peeking connection buffer", "error", err) + log.Warn("Error peeking connection buffer", "err", err) return nil } }}) @@ -415,7 +415,7 @@ FOR_LOOP: c.recvMonitor.Update(int(n)) if err != nil { if c.IsRunning() { - c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "error", err) + c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "err", err) c.stopForError(err) } break FOR_LOOP @@ -436,7 +436,7 @@ FOR_LOOP: c.recvMonitor.Update(int(n)) if err != nil { if c.IsRunning() { - c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "error", err) + c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err) c.stopForError(err) } break FOR_LOOP @@ -448,7 +448,7 @@ FOR_LOOP: msgBytes, err := channel.recvMsgPacket(pkt) if err != nil { if c.IsRunning() { - c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "error", err) + c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err) c.stopForError(err) } break FOR_LOOP @@ -468,7 +468,7 @@ FOR_LOOP: // Cleanup close(c.pong) - for _ = range c.pong { + for range c.pong { // Drain } } diff --git a/p2p/listener.go b/p2p/listener.go index d31f0de83..02d958d61 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -135,7 +135,7 @@ func (l *DefaultListener) listenRoutine() { // Cleanup close(l.connections) - for _ = range l.connections { + for range l.connections { // Drain } } @@ -171,13 +171,13 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) * logger.Info("Getting UPNP external address") nat, err := upnp.Discover() if err != nil { - logger.Info("Could not perform UPNP discover", "error", err) + logger.Info("Could not perform UPNP discover", "err", err) return nil } ext, err := nat.GetExternalAddress() if err != nil { - logger.Info("Could not get UPNP external address", "error", err) + logger.Info("Could not get UPNP external address", "err", err) return nil } @@ -188,7 +188,7 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) * externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "tendermint", 0) if err != nil { - logger.Info("Could not add UPNP port mapping", "error", err) + logger.Info("Could not add UPNP port mapping", "err", err) return nil } diff --git a/p2p/netaddress.go b/p2p/netaddress.go index 09787481c..d424f8c32 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -174,7 +174,6 @@ func (na *NetAddress) ReachabilityTo(o *NetAddress) int { Ipv6_weak Ipv4 Ipv6_strong - Private ) if !na.Routable() { return Unreachable diff --git a/p2p/pex_reactor.go b/p2p/pex_reactor.go index 269a8d006..30ebc3a7e 100644 --- a/p2p/pex_reactor.go +++ b/p2p/pex_reactor.go @@ -44,7 +44,6 @@ const ( type PEXReactor struct { BaseReactor - sw *Switch book *AddrBook ensurePeersPeriod time.Duration @@ -105,7 +104,7 @@ func (r *PEXReactor) AddPeer(p *Peer) { addr, err := NewNetAddressString(p.ListenAddr) if err != nil { // this should never happen - r.Logger.Error("Error in AddPeer: invalid peer address", "addr", p.ListenAddr, "error", err) + r.Logger.Error("Error in AddPeer: invalid peer address", "addr", p.ListenAddr, "err", err) return } r.book.AddAddress(addr, addr) @@ -132,7 +131,7 @@ func (r *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) { _, msg, err := DecodeMessage(msgBytes) if err != nil { - r.Logger.Error("Error decoding message", "error", err) + r.Logger.Error("Error decoding message", "err", err) return } r.Logger.Info("Received message", "msg", msg) diff --git a/p2p/secret_connection.go b/p2p/secret_connection.go index 24cae0f61..06c28317d 100644 --- a/p2p/secret_connection.go +++ b/p2p/secret_connection.go @@ -293,10 +293,6 @@ func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signa return &recvMsg, nil } -func verifyChallengeSignature(challenge *[32]byte, remPubKey crypto.PubKeyEd25519, remSignature crypto.SignatureEd25519) bool { - return remPubKey.VerifyBytes(challenge[:], remSignature.Wrap()) -} - //-------------------------------------------------------------------------------- // sha256 @@ -319,16 +315,6 @@ func hash24(input []byte) (res *[24]byte) { return } -// ripemd160 -func hash20(input []byte) (res *[20]byte) { - hasher := ripemd160.New() - hasher.Write(input) // does not error - resSlice := hasher.Sum(nil) - res = new([20]byte) - copy(res[:], resSlice) - return -} - // increment nonce big-endian by 2 with wraparound. func incr2Nonce(nonce *[24]byte) { incrNonce(nonce) diff --git a/p2p/switch.go b/p2p/switch.go index 5ccdc114e..2d8d34357 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -309,7 +309,7 @@ func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error { func (sw *Switch) dialSeed(addr *NetAddress) { peer, err := sw.DialPeerWithAddress(addr, true) if err != nil { - sw.Logger.Error("Error dialing seed", "error", err) + sw.Logger.Error("Error dialing seed", "err", err) } else { sw.Logger.Info("Connected to seed", "peer", peer) } @@ -322,7 +322,7 @@ func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, sw.Logger.Info("Dialing peer", "address", addr) peer, err := newOutboundPeerWithConfig(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, sw.peerConfig) if err != nil { - sw.Logger.Error("Failed to dial peer", "address", addr, "error", err) + sw.Logger.Error("Failed to dial peer", "address", addr, "err", err) return nil, err } peer.SetLogger(sw.Logger.With("peer", addr)) @@ -331,7 +331,7 @@ func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, } err = sw.AddPeer(peer) if err != nil { - sw.Logger.Error("Failed to add peer", "address", addr, "error", err) + sw.Logger.Error("Failed to add peer", "address", addr, "err", err) peer.CloseConn() return nil, err } @@ -381,7 +381,7 @@ func (sw *Switch) Peers() IPeerSet { // TODO: make record depending on reason. func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) { addr := NewNetAddress(peer.Addr()) - sw.Logger.Info("Stopping peer for error", "peer", peer, "error", reason) + sw.Logger.Error("Stopping peer for error", "peer", peer, "err", reason) sw.stopAndRemovePeer(peer, reason) if peer.IsPersistent() { @@ -395,10 +395,10 @@ func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) { peer, err := sw.DialPeerWithAddress(addr, true) if err != nil { if i == reconnectAttempts { - sw.Logger.Info("Error reconnecting to peer. Giving up", "tries", i, "error", err) + sw.Logger.Info("Error reconnecting to peer. Giving up", "tries", i, "err", err) return } - sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "error", err) + sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err) time.Sleep(reconnectInterval) continue } @@ -442,7 +442,7 @@ func (sw *Switch) listenerRoutine(l Listener) { // New inbound connection! err := sw.addPeerWithConnectionAndConfig(inConn, sw.peerConfig) if err != nil { - sw.Logger.Info("Ignoring inbound connection: error while adding peer", "address", inConn.RemoteAddr().String(), "error", err) + sw.Logger.Info("Ignoring inbound connection: error while adding peer", "address", inConn.RemoteAddr().String(), "err", err) continue } diff --git a/p2p/upnp/probe.go b/p2p/upnp/probe.go index 3537e1c65..74d4d4c51 100644 --- a/p2p/upnp/probe.go +++ b/p2p/upnp/probe.go @@ -1,11 +1,12 @@ package upnp import ( - "errors" "fmt" "net" "time" + "github.com/pkg/errors" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" ) @@ -18,26 +19,26 @@ type UPNPCapabilities struct { func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Listener, net.IP, error) { nat, err := Discover() if err != nil { - return nil, nil, nil, errors.New(fmt.Sprintf("NAT upnp could not be discovered: %v", err)) + return nil, nil, nil, errors.Errorf("NAT upnp could not be discovered: %v", err) } logger.Info(cmn.Fmt("ourIP: %v", nat.(*upnpNAT).ourIP)) ext, err := nat.GetExternalAddress() if err != nil { - return nat, nil, nil, errors.New(fmt.Sprintf("External address error: %v", err)) + return nat, nil, nil, errors.Errorf("External address error: %v", err) } logger.Info(cmn.Fmt("External address: %v", ext)) port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0) if err != nil { - return nat, nil, ext, errors.New(fmt.Sprintf("Port mapping error: %v", err)) + return nat, nil, ext, errors.Errorf("Port mapping error: %v", err) } logger.Info(cmn.Fmt("Port mapping mapped: %v", port)) // also run the listener, open for all remote addresses. listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort)) if err != nil { - return nat, nil, ext, errors.New(fmt.Sprintf("Error establishing listener: %v", err)) + return nat, nil, ext, errors.Errorf("Error establishing listener: %v", err) } return nat, listener, ext, nil } diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index 3d6c55035..29abbe794 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -65,14 +65,14 @@ func Discover() (nat NAT, err error) { return } var n int - n, _, err = socket.ReadFromUDP(answerBytes) + _, _, err = socket.ReadFromUDP(answerBytes) for { n, _, err = socket.ReadFromUDP(answerBytes) if err != nil { break } answer := string(answerBytes[0:n]) - if strings.Index(answer, st) < 0 { + if !strings.Contains(answer, st) { continue } // HTTP header field names are case-insensitive. @@ -153,7 +153,7 @@ type Root struct { func getChildDevice(d *Device, deviceType string) *Device { dl := d.DeviceList.Device for i := 0; i < len(dl); i++ { - if strings.Index(dl[i].DeviceType, deviceType) >= 0 { + if strings.Contains(dl[i].DeviceType, deviceType) { return &dl[i] } } @@ -163,7 +163,7 @@ func getChildDevice(d *Device, deviceType string) *Device { func getChildService(d *Device, serviceType string) *UPNPService { sl := d.ServiceList.Service for i := 0; i < len(sl); i++ { - if strings.Index(sl[i].ServiceType, serviceType) >= 0 { + if strings.Contains(sl[i].ServiceType, serviceType) { return &sl[i] } } @@ -211,7 +211,7 @@ func getServiceURL(rootURL string) (url, urnDomain string, err error) { return } a := &root.Device - if strings.Index(a.DeviceType, "InternetGatewayDevice:1") < 0 { + if !strings.Contains(a.DeviceType, "InternetGatewayDevice:1") { err = errors.New("No InternetGatewayDevice") return } diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index 1b99854cb..ad5522e49 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -31,7 +31,39 @@ func TestHeaderEvents(t *testing.T) { } } -func TestTxEvents(t *testing.T) { +func TestBlockEvents(t *testing.T) { + require := require.New(t) + for i, c := range GetClients() { + // start for this test it if it wasn't already running + if !c.IsRunning() { + // if so, then we start it, listen, and stop it. + st, err := c.Start() + require.Nil(err, "%d: %+v", i, err) + require.True(st, "%d", i) + defer c.Stop() + } + + // listen for a new block; ensure height increases by 1 + var firstBlockHeight int + for i := 0; i < 3; i++ { + evtTyp := types.EventStringNewBlock() + evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) + require.Nil(err, "%d: %+v", i, err) + blockEvent, ok := evt.Unwrap().(types.EventDataNewBlock) + require.True(ok, "%d: %#v", i, evt) + + block := blockEvent.Block + if i == 0 { + firstBlockHeight = block.Header.Height + continue + } + + require.Equal(block.Header.Height, firstBlockHeight+i) + } + } +} + +func TestTxEventsSentWithBroadcastTxAsync(t *testing.T) { require := require.New(t) for i, c := range GetClients() { // start for this test it if it wasn't already running @@ -63,3 +95,36 @@ func TestTxEvents(t *testing.T) { require.True(txe.Code.IsOK()) } } + +func TestTxEventsSentWithBroadcastTxSync(t *testing.T) { + require := require.New(t) + for i, c := range GetClients() { + // start for this test it if it wasn't already running + if !c.IsRunning() { + // if so, then we start it, listen, and stop it. + st, err := c.Start() + require.Nil(err, "%d: %+v", i, err) + require.True(st, "%d", i) + defer c.Stop() + } + + // make the tx + _, _, tx := merktest.MakeTxKV() + evtTyp := types.EventStringTx(types.Tx(tx)) + + // send async + txres, err := c.BroadcastTxSync(tx) + require.Nil(err, "%+v", err) + require.True(txres.Code.IsOK()) + + // and wait for confirmation + evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) + require.Nil(err, "%d: %+v", i, err) + // and make sure it has the proper info + txe, ok := evt.Unwrap().(types.EventDataTx) + require.True(ok, "%d: %#v", i, evt) + // make sure this is the proper tx + require.EqualValues(tx, txe.Tx) + require.True(txe.Code.IsOK()) + } +} diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index 0d1012557..71fa90b2a 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -40,7 +40,7 @@ func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { c := a.App.CheckTx(tx) - // and this gets writen in a background thread... + // and this gets written in a background thread... if c.IsOK() { go func() { a.App.DeliverTx(tx) }() } @@ -49,7 +49,7 @@ func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { c := a.App.CheckTx(tx) - // and this gets writen in a background thread... + // and this gets written in a background thread... if c.IsOK() { go func() { a.App.DeliverTx(tx) }() } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 13b440479..e82f8df42 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -10,6 +10,7 @@ import ( merktest "github.com/tendermint/merkleeyes/testutil" "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" + "github.com/tendermint/tendermint/types" ) func getHTTPClient() *client.HTTP { @@ -182,3 +183,93 @@ func TestAppCalls(t *testing.T) { } } } + +func TestBroadcastTxSync(t *testing.T) { + require := require.New(t) + + mempool := node.MempoolReactor().Mempool + initMempoolSize := mempool.Size() + + for i, c := range GetClients() { + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxSync(tx) + require.Nil(err, "%d: %+v", i, err) + require.True(bres.Code.IsOK()) + + require.Equal(initMempoolSize+1, mempool.Size()) + + txs := mempool.Reap(1) + require.EqualValues(tx, txs[0]) + mempool.Flush() + } +} + +func TestBroadcastTxCommit(t *testing.T) { + require := require.New(t) + + mempool := node.MempoolReactor().Mempool + for i, c := range GetClients() { + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxCommit(tx) + require.Nil(err, "%d: %+v", i, err) + require.True(bres.CheckTx.Code.IsOK()) + require.True(bres.DeliverTx.Code.IsOK()) + + require.Equal(0, mempool.Size()) + } +} + +func TestTx(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // first we broadcast a tx + c := getHTTPClient() + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxCommit(tx) + require.Nil(err, "%+v", err) + + txHeight := bres.Height + txHash := bres.Hash + + anotherTxHash := types.Tx("a different tx").Hash() + + cases := []struct { + valid bool + hash []byte + prove bool + }{ + // only valid if correct hash provided + {true, txHash, false}, + {true, txHash, true}, + {false, anotherTxHash, false}, + {false, anotherTxHash, true}, + {false, nil, false}, + {false, nil, true}, + } + + for i, c := range GetClients() { + for j, tc := range cases { + t.Logf("client %d, case %d", i, j) + + // now we query for the tx. + // since there's only one tx, we know index=0. + ptx, err := c.Tx(tc.hash, tc.prove) + + if !tc.valid { + require.NotNil(err) + } else { + require.Nil(err, "%+v", err) + assert.Equal(txHeight, ptx.Height) + assert.EqualValues(tx, ptx.Tx) + assert.Equal(0, ptx.Index) + assert.True(ptx.TxResult.Code.IsOK()) + + // time to verify the proof + proof := ptx.Proof + if tc.prove && assert.EqualValues(tx, proof.Data) { + assert.True(proof.Proof.Verify(proof.Index, proof.Total, txHash, proof.RootHash)) + } + } + } + } +} diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 1988b05c6..5b794db79 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -75,7 +75,7 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // Wait for the tx to be included in a block, // timeout after something reasonable. - // TODO: configureable? + // TODO: configurable? timer := time.NewTimer(60 * 2 * time.Second) select { case deliverTxRes := <-deliverTxResCh: diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 12cf793a6..755c3e79c 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -13,7 +13,6 @@ import ( "github.com/pkg/errors" types "github.com/tendermint/tendermint/rpc/lib/types" - cmn "github.com/tendermint/tmlibs/common" ) // HTTPClient is a common interface for JSONRPCClient and URIClient. @@ -23,13 +22,23 @@ type HTTPClient interface { // TODO: Deprecate support for IP:PORT or /path/to/socket func makeHTTPDialer(remoteAddr string) (string, func(string, string) (net.Conn, error)) { - parts := strings.SplitN(remoteAddr, "://", 2) var protocol, address string - if len(parts) != 2 { - cmn.PanicSanity(fmt.Sprintf("Expected fully formed listening address, including the tcp:// or unix:// prefix, given %s", remoteAddr)) - } else { + if len(parts) == 1 { + // default to tcp if nothing specified + protocol, address = "tcp", remoteAddr + } else if len(parts) == 2 { protocol, address = parts[0], parts[1] + } else { + // return a invalid message + msg := fmt.Sprintf("Invalid addr: %s", remoteAddr) + return msg, func(_ string, _ string) (net.Conn, error) { + return nil, errors.New(msg) + } + } + // accept http as an alias for tcp + if protocol == "http" { + protocol = "tcp" } trimmedAddress := strings.Replace(address, "/", ".", -1) // replace / with . for http requests (dummy domain) diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index 1ad744e87..f018de67c 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -100,14 +100,14 @@ func (wsc *WSClient) receiveEventsRoutine() { for { _, data, err := wsc.ReadMessage() if err != nil { - wsc.Logger.Info("WSClient failed to read message", "error", err, "data", string(data)) + wsc.Logger.Info("WSClient failed to read message", "err", err, "data", string(data)) wsc.Stop() break } else { var response types.RPCResponse err := json.Unmarshal(data, &response) if err != nil { - wsc.Logger.Info("WSClient failed to parse message", "error", err, "data", string(data)) + wsc.Logger.Info("WSClient failed to parse message", "err", err, "data", string(data)) wsc.ErrorsCh <- err continue } diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index c7bed052f..a79a5270e 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -245,11 +245,11 @@ func TestServersAndClientsBasic(t *testing.T) { fmt.Printf("=== testing server on %s using %v client", addr, cl1) testWithHTTPClient(t, cl1) - cl2 := client.NewJSONRPCClient(tcpAddr) + cl2 := client.NewJSONRPCClient(addr) fmt.Printf("=== testing server on %s using %v client", addr, cl2) testWithHTTPClient(t, cl2) - cl3 := client.NewWSClient(tcpAddr, websocketEndpoint) + cl3 := client.NewWSClient(addr, websocketEndpoint) _, err := cl3.Start() require.Nil(t, err) fmt.Printf("=== testing server on %s using %v client", addr, cl3) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 5745f6fa1..b6431a1ec 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -338,7 +338,7 @@ func nonJsonToArg(ty reflect.Type, arg string) (reflect.Value, error, bool) { const ( writeChanCapacity = 1000 - wsWriteTimeoutSeconds = 30 // each write times out after this + wsWriteTimeoutSeconds = 30 // each write times out after this. wsReadTimeoutSeconds = 30 // connection times out if we haven't received *anything* in this long, not even pings. wsPingTickerSeconds = 10 // send a ping every PingTickerSeconds. ) @@ -510,7 +510,10 @@ func (wsc *wsConnection) readRoutine() { continue } returns := rpcFunc.f.Call(args) - wsc.Logger.Info("WSJSONRPC", "method", request.Method, "args", args, "returns", returns) + + // TODO: Need to encode args/returns to string if we want to log them + wsc.Logger.Info("WSJSONRPC", "method", request.Method) + result, err := unreflectResult(returns) if err != nil { wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error())) @@ -532,20 +535,19 @@ func (wsc *wsConnection) writeRoutine() { case <-wsc.Quit: return case <-wsc.pingTicker.C: - err := wsc.baseConn.WriteMessage(websocket.PingMessage, []byte{}) + err := wsc.writeMessageWithDeadline(websocket.PingMessage, []byte{}) if err != nil { - wsc.Logger.Error("Failed to write ping message on websocket", "error", err) + wsc.Logger.Error("Failed to write ping message on websocket", "err", err) wsc.Stop() return } case msg := <-wsc.writeChan: jsonBytes, err := json.MarshalIndent(msg, "", " ") if err != nil { - wsc.Logger.Error("Failed to marshal RPCResponse to JSON", "error", err) + wsc.Logger.Error("Failed to marshal RPCResponse to JSON", "err", err) } else { - wsc.baseConn.SetWriteDeadline(time.Now().Add(time.Second * wsWriteTimeoutSeconds)) - if err = wsc.baseConn.WriteMessage(websocket.TextMessage, jsonBytes); err != nil { - wsc.Logger.Error("Failed to write response on websocket", "error", err) + if err = wsc.writeMessageWithDeadline(websocket.TextMessage, jsonBytes); err != nil { + wsc.Logger.Error("Failed to write response on websocket", "err", err) wsc.Stop() return } @@ -554,6 +556,13 @@ func (wsc *wsConnection) writeRoutine() { } } +// All writes to the websocket must (re)set the write deadline. +// If some writes don't set it while others do, they may timeout incorrectly (https://github.com/tendermint/tendermint/issues/553) +func (wsc *wsConnection) writeMessageWithDeadline(msgType int, msg []byte) error { + wsc.baseConn.SetWriteDeadline(time.Now().Add(time.Second * wsWriteTimeoutSeconds)) + return wsc.baseConn.WriteMessage(msgType, msg) +} + //---------------------------------------- // Main manager for all websocket connections @@ -591,12 +600,13 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ wsConn, err := wm.Upgrade(w, r, nil) if err != nil { // TODO - return http error - wm.logger.Error("Failed to upgrade to websocket connection", "error", err) + wm.logger.Error("Failed to upgrade to websocket connection", "err", err) return } // register connection con := NewWSConnection(wsConn, wm.funcMap, wm.evsw) + con.SetLogger(wm.logger) wm.logger.Info("New websocket connection", "remote", con.remoteAddr) con.Start() // Blocking } diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go index 3b856b5db..270321b4b 100644 --- a/rpc/lib/server/http_server.go +++ b/rpc/lib/server/http_server.go @@ -97,7 +97,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler WriteRPCResponseHTTP(rww, res) } else { // For the rest, - logger.Error("Panic in RPC HTTP handler", "error", e, "stack", string(debug.Stack())) + logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack())) rww.WriteHeader(http.StatusInternalServerError) WriteRPCResponseHTTP(rww, types.NewRPCResponse("", nil, fmt.Sprintf("Internal Server Error: %v", e))) } diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go deleted file mode 100644 index b7df67841..000000000 --- a/rpc/test/client_test.go +++ /dev/null @@ -1,352 +0,0 @@ -package rpctest - -import ( - "bytes" - crand "crypto/rand" - "fmt" - "math/rand" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire/data" - . "github.com/tendermint/tmlibs/common" - - "github.com/tendermint/tendermint/rpc/core" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpc "github.com/tendermint/tendermint/rpc/lib/client" - "github.com/tendermint/tendermint/state/txindex/null" - "github.com/tendermint/tendermint/types" -) - -//-------------------------------------------------------------------------------- -// Test the HTTP client -// These tests assume the dummy app -//-------------------------------------------------------------------------------- - -//-------------------------------------------------------------------------------- -// status - -func TestURIStatus(t *testing.T) { - testStatus(t, GetURIClient()) -} - -func TestJSONStatus(t *testing.T) { - testStatus(t, GetJSONClient()) -} - -func testStatus(t *testing.T, client rpc.HTTPClient) { - moniker := GetConfig().Moniker - result := new(ctypes.ResultStatus) - _, err := client.Call("status", map[string]interface{}{}, result) - require.Nil(t, err) - assert.Equal(t, moniker, result.NodeInfo.Moniker) -} - -//-------------------------------------------------------------------------------- -// broadcast tx sync - -// random bytes (excluding byte('=')) -func randBytes(t *testing.T) []byte { - n := rand.Intn(10) + 2 - buf := make([]byte, n) - _, err := crand.Read(buf) - require.Nil(t, err) - return bytes.Replace(buf, []byte("="), []byte{100}, -1) -} - -func TestURIBroadcastTxSync(t *testing.T) { - testBroadcastTxSync(t, GetURIClient()) -} - -func TestJSONBroadcastTxSync(t *testing.T) { - testBroadcastTxSync(t, GetJSONClient()) -} - -func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) { - mem := node.MempoolReactor().Mempool - initMemSize := mem.Size() - result := new(ctypes.ResultBroadcastTx) - tx := randBytes(t) - _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) - require.Nil(t, err) - - require.Equal(t, abci.CodeType_OK, result.Code) - require.Equal(t, initMemSize+1, mem.Size()) - txs := mem.Reap(1) - require.EqualValues(t, tx, txs[0]) - mem.Flush() -} - -//-------------------------------------------------------------------------------- -// query - -func testTxKV(t *testing.T) ([]byte, []byte, types.Tx) { - k := randBytes(t) - v := randBytes(t) - return k, v, types.Tx(Fmt("%s=%s", k, v)) -} - -func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { - result := new(ctypes.ResultBroadcastTxCommit) - k, v, tx := testTxKV(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) - require.Nil(t, err) - require.NotNil(t, 0, result.DeliverTx, "%#v", result) - require.EqualValues(t, 0, result.CheckTx.Code, "%#v", result) - require.EqualValues(t, 0, result.DeliverTx.Code, "%#v", result) - return k, v -} - -func TestURIABCIQuery(t *testing.T) { - testABCIQuery(t, GetURIClient()) -} - -func TestJSONABCIQuery(t *testing.T) { - testABCIQuery(t, GetJSONClient()) -} - -func testABCIQuery(t *testing.T, client rpc.HTTPClient) { - k, _ := sendTx(t, client) - time.Sleep(time.Millisecond * 500) - result := new(ctypes.ResultABCIQuery) - _, err := client.Call("abci_query", - map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, result) - require.Nil(t, err) - - require.EqualValues(t, 0, result.Code) - - // XXX: specific to value returned by the dummy - require.NotEqual(t, 0, len(result.Value)) -} - -//-------------------------------------------------------------------------------- -// broadcast tx commit - -func TestURIBroadcastTxCommit(t *testing.T) { - testBroadcastTxCommit(t, GetURIClient()) -} - -func TestJSONBroadcastTxCommit(t *testing.T) { - testBroadcastTxCommit(t, GetJSONClient()) -} - -func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) { - require := require.New(t) - - result := new(ctypes.ResultBroadcastTxCommit) - tx := randBytes(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) - require.Nil(err) - - checkTx := result.CheckTx - require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := result.DeliverTx - require.Equal(abci.CodeType_OK, deliverTx.Code) - mem := node.MempoolReactor().Mempool - require.Equal(0, mem.Size()) - // TODO: find tx in block -} - -//-------------------------------------------------------------------------------- -// query tx - -func TestURITx(t *testing.T) { - testTx(t, GetURIClient(), true) - - core.SetTxIndexer(&null.TxIndex{}) - defer core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer) - - testTx(t, GetURIClient(), false) -} - -func TestJSONTx(t *testing.T) { - testTx(t, GetJSONClient(), true) - - core.SetTxIndexer(&null.TxIndex{}) - testTx(t, GetJSONClient(), false) - core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer) -} - -func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { - assert, require := assert.New(t), require.New(t) - - // first we broadcast a tx - result := new(ctypes.ResultBroadcastTxCommit) - txBytes := randBytes(t) - tx := types.Tx(txBytes) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, result) - require.Nil(err) - - checkTx := result.CheckTx - require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := result.DeliverTx - require.Equal(abci.CodeType_OK, deliverTx.Code) - mem := node.MempoolReactor().Mempool - require.Equal(0, mem.Size()) - - txHash := tx.Hash() - txHash2 := types.Tx("a different tx").Hash() - - cases := []struct { - valid bool - hash []byte - prove bool - }{ - // only valid if correct hash provided - {true, txHash, false}, - {true, txHash, true}, - {false, txHash2, false}, - {false, txHash2, true}, - {false, nil, false}, - {false, nil, true}, - } - - for i, tc := range cases { - idx := fmt.Sprintf("%d", i) - - // now we query for the tx. - // since there's only one tx, we know index=0. - result2 := new(ctypes.ResultTx) - query := map[string]interface{}{ - "hash": tc.hash, - "prove": tc.prove, - } - _, err = client.Call("tx", query, result2) - valid := (withIndexer && tc.valid) - if !valid { - require.NotNil(err, idx) - } else { - require.Nil(err, idx) - assert.Equal(tx, result2.Tx, idx) - assert.Equal(result.Height, result2.Height, idx) - assert.Equal(0, result2.Index, idx) - assert.Equal(abci.CodeType_OK, result2.TxResult.Code, idx) - // time to verify the proof - proof := result2.Proof - if tc.prove && assert.Equal(tx, proof.Data, idx) { - assert.True(proof.Proof.Verify(proof.Index, proof.Total, tx.Hash(), proof.RootHash), idx) - } - } - } - -} - -//-------------------------------------------------------------------------------- -// Test the websocket service - -var wsTyp = "JSONRPC" - -// make a simple connection to the server -func TestWSConnect(t *testing.T) { - wsc := GetWSClient() - wsc.Stop() -} - -// receive a new block message -func TestWSNewBlock(t *testing.T) { - wsc := GetWSClient() - eid := types.EventStringNewBlock() - require.Nil(t, wsc.Subscribe(eid)) - - defer func() { - require.Nil(t, wsc.Unsubscribe(eid)) - wsc.Stop() - }() - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - // fmt.Println("Check:", b) - return nil - }) -} - -// receive a few new block messages in a row, with increasing height -func TestWSBlockchainGrowth(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - wsc := GetWSClient() - eid := types.EventStringNewBlock() - require.Nil(t, wsc.Subscribe(eid)) - - defer func() { - require.Nil(t, wsc.Unsubscribe(eid)) - wsc.Stop() - }() - - // listen for NewBlock, ensure height increases by 1 - - var initBlockN int - for i := 0; i < 3; i++ { - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, eventData interface{}) error { - block := eventData.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block - if i == 0 { - initBlockN = block.Header.Height - } else { - if block.Header.Height != initBlockN+i { - return fmt.Errorf("Expected block %d, got block %d", initBlockN+i, block.Header.Height) - } - } - - return nil - }) - } -} - -func TestWSTxEvent(t *testing.T) { - require := require.New(t) - wsc := GetWSClient() - tx := randBytes(t) - - // listen for the tx I am about to submit - eid := types.EventStringTx(types.Tx(tx)) - require.Nil(wsc.Subscribe(eid)) - - defer func() { - require.Nil(wsc.Unsubscribe(eid)) - wsc.Stop() - }() - - // send an tx - result := new(ctypes.ResultBroadcastTx) - _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) - require.Nil(err) - - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - evt, ok := b.(types.TMEventData).Unwrap().(types.EventDataTx) - require.True(ok, "Got wrong event type: %#v", b) - require.Equal(tx, []byte(evt.Tx), "Returned different tx") - require.Equal(abci.CodeType_OK, evt.Code) - return nil - }) -} - -/* TODO: this with dummy app.. -func TestWSDoubleFire(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - con := newWSCon(t) - eid := types.EventStringAccInput(user[0].Address) - subscribe(t, con, eid) - defer func() { - unsubscribe(t, con, eid) - con.Close() - }() - amt := int64(100) - toAddr := user[1].Address - // broadcast the transaction, wait to hear about it - waitForEvent(t, con, eid, true, func() { - tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt) - broadcastTx(t, wsTyp, tx) - }, func(eid string, b []byte) error { - return nil - }) - // but make sure we don't hear about it twice - waitForEvent(t, con, eid, false, func() { - }, func(eid string, b []byte) error { - return nil - }) -}*/ diff --git a/rpc/test/grpc_test.go b/rpc/test/grpc_test.go index 4935a09d9..5ca40a3b0 100644 --- a/rpc/test/grpc_test.go +++ b/rpc/test/grpc_test.go @@ -6,15 +6,13 @@ import ( "golang.org/x/net/context" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/rpc/grpc" + core_grpc "github.com/tendermint/tendermint/rpc/grpc" ) -//------------------------------------------- - func TestBroadcastTx(t *testing.T) { require := require.New(t) res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")}) - require.Nil(err) + require.Nil(err, "%+v", err) require.EqualValues(0, res.CheckTx.Code) require.EqualValues(0, res.DeliverTx.Code) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 51bb0b8fe..14da15c94 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -1,25 +1,19 @@ package rpctest import ( - "encoding/json" "fmt" "math/rand" "os" "path/filepath" "strings" - "testing" - "time" - "github.com/stretchr/testify/require" "github.com/tendermint/tmlibs/log" abci "github.com/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" - ctypes "github.com/tendermint/tendermint/rpc/core/types" core_grpc "github.com/tendermint/tendermint/rpc/grpc" - client "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/types" ) @@ -65,32 +59,11 @@ func GetConfig() *cfg.Config { return config } -// GetURIClient gets a uri client pointing to the test tendermint rpc -func GetURIClient() *client.URIClient { - rpcAddr := GetConfig().RPC.ListenAddress - return client.NewURIClient(rpcAddr) -} - -// GetJSONClient gets a http/json client pointing to the test tendermint rpc -func GetJSONClient() *client.JSONRPCClient { - rpcAddr := GetConfig().RPC.ListenAddress - return client.NewJSONRPCClient(rpcAddr) -} - func GetGRPCClient() core_grpc.BroadcastAPIClient { grpcAddr := config.RPC.GRPCListenAddress return core_grpc.StartGRPCClient(grpcAddr) } -func GetWSClient() *client.WSClient { - rpcAddr := GetConfig().RPC.ListenAddress - wsc := client.NewWSClient(rpcAddr, "/websocket") - if _, err := wsc.Start(); err != nil { - panic(err) - } - return wsc -} - // StartTendermint starts a test tendermint server in a go routine and returns when it is initialized func StartTendermint(app abci.Application) *nm.Node { node := NewTendermint(app) @@ -111,68 +84,3 @@ func NewTendermint(app abci.Application) *nm.Node { node := nm.NewNode(config, privValidator, papp, logger) return node } - -//-------------------------------------------------------------------------------- -// Utilities for testing the websocket service - -// wait for an event; do things that might trigger events, and check them when they are received -// the check function takes an event id and the byte slice read off the ws -func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) { - // go routine to wait for webscoket msg - goodCh := make(chan interface{}) - errCh := make(chan error) - - // Read message - go func() { - var err error - LOOP: - for { - select { - case r := <-wsc.ResultsCh: - result := new(ctypes.ResultEvent) - err = json.Unmarshal(r, result) - if err != nil { - // cant distinguish between error and wrong type ... - continue - } - if result.Name == eventid { - goodCh <- result.Data - break LOOP - } - case err := <-wsc.ErrorsCh: - errCh <- err - break LOOP - case <-wsc.Quit: - break LOOP - } - } - }() - - // do stuff (transactions) - f() - - // wait for an event or timeout - timeout := time.NewTimer(10 * time.Second) - select { - case <-timeout.C: - if dieOnTimeout { - wsc.Stop() - require.True(t, false, "%s event was not received in time", eventid) - } - // else that's great, we didn't hear the event - // and we shouldn't have - case eventData := <-goodCh: - if dieOnTimeout { - // message was received and expected - // run the check - require.Nil(t, check(eventid, eventData)) - } else { - wsc.Stop() - require.True(t, false, "%s event was not expected", eventid) - } - case err := <-errCh: - panic(err) // Show the stack trace. - } -} - -//-------------------------------------------------------------------------------- diff --git a/scripts/tendermint-builder/Dockerfile b/scripts/tendermint-builder/Dockerfile index 84c198ee0..daf931715 100644 --- a/scripts/tendermint-builder/Dockerfile +++ b/scripts/tendermint-builder/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.7.4 +FROM golang:1.8.3 RUN apt-get update && apt-get install -y --no-install-recommends \ zip \ diff --git a/state/execution.go b/state/execution.go index 768a0b1de..db2091804 100644 --- a/state/execution.go +++ b/state/execution.go @@ -85,7 +85,7 @@ func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnCo // Begin block err := proxyAppConn.BeginBlockSync(block.Hash(), types.TM2PB.Header(block.Header)) if err != nil { - logger.Error("Error in proxyAppConn.BeginBlock", "error", err) + logger.Error("Error in proxyAppConn.BeginBlock", "err", err) return nil, err } @@ -100,13 +100,13 @@ func execBlockOnProxyApp(eventCache types.Fireable, proxyAppConn proxy.AppConnCo // End block abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(uint64(block.Height)) if err != nil { - logger.Error("Error in proxyAppConn.EndBlock", "error", err) + logger.Error("Error in proxyAppConn.EndBlock", "err", err) return nil, err } valDiff := abciResponses.EndBlock.Diffs - logger.Info("Executed block", "height", block.Height, "valid txs", validTxs, "invalid txs", invalidTxs) + logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, "invalidTxs", invalidTxs) if len(valDiff) > 0 { logger.Info("Update to validator set", "updates", abci.ValidatorsString(valDiff)) } @@ -252,7 +252,7 @@ func (s *State) CommitStateUpdateMempool(proxyAppConn proxy.AppConnConsensus, bl // Commit block, get hash back res := proxyAppConn.CommitSync() if res.IsErr() { - s.logger.Error("Error in proxyAppConn.CommitSync", "error", res) + s.logger.Error("Error in proxyAppConn.CommitSync", "err", res) return res } if res.Log != "" { @@ -298,7 +298,7 @@ func ExecCommitBlock(appConnConsensus proxy.AppConnConsensus, block *types.Block // Commit block, get hash back res := appConnConsensus.CommitSync() if res.IsErr() { - logger.Error("Error in proxyAppConn.CommitSync", "error", res) + logger.Error("Error in proxyAppConn.CommitSync", "err", res) return nil, res } if res.Log != "" { diff --git a/state/state.go b/state/state.go index 808fcbe24..e1ccca9b1 100644 --- a/state/state.go +++ b/state/state.go @@ -46,10 +46,6 @@ type State struct { TxIndexer txindex.TxIndexer `json:"-"` // Transaction indexer. - // Intermediate results from processing - // Persisted separately from the state - abciResponses *ABCIResponses - logger log.Logger } @@ -148,7 +144,7 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ // update the validator set with the latest abciResponses err := updateValidators(nextValSet, abciResponses.EndBlock.Diffs) if err != nil { - s.logger.Error("Error changing validator set", "error", err) + s.logger.Error("Error changing validator set", "err", err) // TODO: err or carry on? } diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index 8de9b8cda..903189c2b 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -8,9 +8,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" - db "github.com/tendermint/tmlibs/db" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" + db "github.com/tendermint/tmlibs/db" ) func TestTxIndex(t *testing.T) { @@ -45,8 +45,8 @@ func benchmarkTxIndex(txsCount int, b *testing.B) { batch := txindex.NewBatch(txsCount) for i := 0; i < txsCount; i++ { - txResult.Index += 1 batch.Add(*txResult) + txResult.Index += 1 } b.ResetTimer() @@ -54,6 +54,9 @@ func benchmarkTxIndex(txsCount int, b *testing.B) { for n := 0; n < b.N; n++ { err = indexer.AddBatch(batch) } + if err != nil { + b.Fatal(err) + } } func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) } diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 7c322976d..8a2702fc8 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -1,5 +1,4 @@ -# Pull base image. -FROM golang:1.7.4 +FROM golang:1.8.3 # Add testing deps for curl RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list @@ -9,7 +8,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends \ jq bsdmainutils vim-common psmisc netcat curl -# Setup tendermint repo +# Setup tendermint repo ENV REPO $GOPATH/src/github.com/tendermint/tendermint WORKDIR $REPO diff --git a/types/block.go b/types/block.go index b306d57d0..6592ad5dd 100644 --- a/types/block.go +++ b/types/block.go @@ -282,10 +282,7 @@ func (commit *Commit) GetByIndex(index int) *Vote { } func (commit *Commit) IsCommit() bool { - if len(commit.Precommits) == 0 { - return false - } - return true + return len(commit.Precommits) != 0 } func (commit *Commit) ValidateBasic() error { diff --git a/types/genesis.go b/types/genesis.go index 75999f631..23b14c9e9 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -2,8 +2,11 @@ package types import ( "encoding/json" + "io/ioutil" "time" + "github.com/pkg/errors" + "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" @@ -17,12 +20,14 @@ var GenDocKey = []byte("GenDocKey") //------------------------------------------------------------ // core types for a genesis definition +// GenesisValidator is an initial validator. type GenesisValidator struct { PubKey crypto.PubKey `json:"pub_key"` Amount int64 `json:"amount"` Name string `json:"name"` } +// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { GenesisTime time.Time `json:"genesis_time"` ChainID string `json:"chain_id"` @@ -30,7 +35,7 @@ type GenesisDoc struct { AppHash data.Bytes `json:"app_hash"` } -// Utility method for saving GenensisDoc as JSON file. +// SaveAs is a utility method for saving GenensisDoc as a JSON file. func (genDoc *GenesisDoc) SaveAs(file string) error { genDocBytes, err := json.Marshal(genDoc) if err != nil { @@ -39,11 +44,38 @@ func (genDoc *GenesisDoc) SaveAs(file string) error { return cmn.WriteFile(file, genDocBytes, 0644) } +// ValidatorHash returns the hash of the validator set contained in the GenesisDoc +func (genDoc *GenesisDoc) ValidatorHash() []byte { + vals := make([]*Validator, len(genDoc.Validators)) + for i, v := range genDoc.Validators { + vals[i] = NewValidator(v.PubKey, v.Amount) + } + vset := NewValidatorSet(vals) + return vset.Hash() +} + //------------------------------------------------------------ // Make genesis state from file +// GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc. func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) { genDoc := GenesisDoc{} err := json.Unmarshal(jsonBlob, &genDoc) return &genDoc, err } + +// GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc. +func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) { + jsonBlob, err := ioutil.ReadFile(genDocFile) + if err != nil { + return nil, errors.Wrap(err, "Couldn't read GenesisDoc file") + } + genDoc, err := GenesisDocFromJSON(jsonBlob) + if err != nil { + return nil, errors.Wrap(err, "Error reading GenesisDoc") + } + if genDoc.ChainID == "" { + return nil, errors.Errorf("Genesis doc %v must include non-empty chain_id", genDocFile) + } + return genDoc, nil +} diff --git a/types/validator_set.go b/types/validator_set.go index b374df576..e334ed9f3 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -85,14 +85,14 @@ func (valSet *ValidatorSet) HasAddress(address []byte) bool { idx := sort.Search(len(valSet.Validators), func(i int) bool { return bytes.Compare(address, valSet.Validators[i].Address) <= 0 }) - return idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 + return idx != len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address) } func (valSet *ValidatorSet) GetByAddress(address []byte) (index int, val *Validator) { idx := sort.Search(len(valSet.Validators), func(i int) bool { return bytes.Compare(address, valSet.Validators[i].Address) <= 0 }) - if idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 { + if idx != len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address) { return idx, valSet.Validators[idx].Copy() } else { return 0, nil @@ -159,7 +159,7 @@ func (valSet *ValidatorSet) Add(val *Validator) (added bool) { valSet.Proposer = nil valSet.totalVotingPower = 0 return true - } else if bytes.Compare(valSet.Validators[idx].Address, val.Address) == 0 { + } else if bytes.Equal(valSet.Validators[idx].Address, val.Address) { return false } else { newValidators := make([]*Validator, len(valSet.Validators)+1) @@ -191,7 +191,7 @@ func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool idx := sort.Search(len(valSet.Validators), func(i int) bool { return bytes.Compare(address, valSet.Validators[i].Address) <= 0 }) - if idx == len(valSet.Validators) || bytes.Compare(valSet.Validators[idx].Address, address) != 0 { + if idx == len(valSet.Validators) || !bytes.Equal(valSet.Validators[idx].Address, address) { return nil, false } else { removedVal := valSet.Validators[idx] diff --git a/version/version.go b/version/version.go index 494b6a895..e77f0e40c 100644 --- a/version/version.go +++ b/version/version.go @@ -2,11 +2,11 @@ package version const Maj = "0" const Min = "10" -const Fix = "0" +const Fix = "1" var ( // The full version string - Version = "0.10.0" + Version = "0.10.1" // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)" GitCommit string