mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-24 16:51:58 +00:00
Compare commits
2 Commits
v0.26.3
...
v0.26.1-rc
Author | SHA1 | Date | |
---|---|---|---|
|
48ab899923 | ||
|
58574b7372 |
@@ -92,7 +92,6 @@ jobs:
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make get_tools
|
||||
make get_dev_tools
|
||||
- run:
|
||||
name: dependencies
|
||||
command: |
|
||||
|
100
CHANGELOG.md
100
CHANGELOG.md
@@ -1,97 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## v0.26.3
|
||||
|
||||
*November 17th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@danil-lashin, @kevlubkcm, @krhubert, @srmo
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* Go API
|
||||
- [rpc] [\#2791](https://github.com/tendermint/tendermint/issues/2791) Functions that start HTTP servers are now blocking:
|
||||
- Impacts `StartHTTPServer`, `StartHTTPAndTLSServer`, and `StartGRPCServer`
|
||||
- These functions now take a `net.Listener` instead of an address
|
||||
- [rpc] [\#2767](https://github.com/tendermint/tendermint/issues/2767) Subscribing to events
|
||||
`NewRound` and `CompleteProposal` return new types `EventDataNewRound` and
|
||||
`EventDataCompleteProposal`, respectively, instead of the generic `EventDataRoundState`. (@kevlubkcm)
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [log] [\#2843](https://github.com/tendermint/tendermint/issues/2843) New `log_format` config option, which can be set to 'plain' for colored
|
||||
text or 'json' for JSON output
|
||||
- [types] [\#2767](https://github.com/tendermint/tendermint/issues/2767) New event types EventDataNewRound (with ProposerInfo) and EventDataCompleteProposal (with BlockID). (@kevlubkcm)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [dep] [\#2844](https://github.com/tendermint/tendermint/issues/2844) Dependencies are no longer pinned to an exact version in the
|
||||
Gopkg.toml:
|
||||
- Serialization libs are allowed to vary by patch release
|
||||
- Other libs are allowed to vary by minor release
|
||||
- [p2p] [\#2857](https://github.com/tendermint/tendermint/issues/2857) "Send failed" is logged at debug level instead of error.
|
||||
- [rpc] [\#2780](https://github.com/tendermint/tendermint/issues/2780) Add read and write timeouts to HTTP servers
|
||||
- [state] [\#2848](https://github.com/tendermint/tendermint/issues/2848) Make "Update to validators" msg value pretty (@danil-lashin)
|
||||
|
||||
### BUG FIXES:
|
||||
- [consensus] [\#2819](https://github.com/tendermint/tendermint/issues/2819) Don't send proposalHearbeat if not a validator
|
||||
- [docs] [\#2859](https://github.com/tendermint/tendermint/issues/2859) Fix ConsensusParams details in spec
|
||||
- [libs/autofile] [\#2760](https://github.com/tendermint/tendermint/issues/2760) Comment out autofile permissions check - should fix
|
||||
running Tendermint on Windows
|
||||
- [p2p] [\#2869](https://github.com/tendermint/tendermint/issues/2869) Set connection config properly instead of always using default
|
||||
- [p2p/pex] [\#2802](https://github.com/tendermint/tendermint/issues/2802) Seed mode fixes:
|
||||
- Only disconnect from inbound peers
|
||||
- Use FlushStop instead of Sleep to ensure all messages are sent before
|
||||
disconnecting
|
||||
|
||||
## v0.26.2
|
||||
|
||||
*November 15th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release: @hleb-albau, @zhuzeyu
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [rpc] [\#2582](https://github.com/tendermint/tendermint/issues/2582) Enable CORS on RPC API (@hleb-albau)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Unlock mutex in localClient so even when app panics (e.g. during CheckTx), consensus continue working
|
||||
- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Fix DATA RACE in localClient
|
||||
- [amino] [\#2822](https://github.com/tendermint/tendermint/issues/2822) Update to v0.14.1 to support compiling on 32-bit platforms
|
||||
- [rpc] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Drain channel before calling Unsubscribe(All) in `/broadcast_tx_commit`
|
||||
|
||||
## v0.26.1
|
||||
|
||||
*November 11, 2018*
|
||||
|
||||
Special thanks to external contributors on this release: @katakonst
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [consensus] [\#2704](https://github.com/tendermint/tendermint/issues/2704) Simplify valid POL round logic
|
||||
- [docs] [\#2749](https://github.com/tendermint/tendermint/issues/2749) Deduplicate some ABCI docs
|
||||
- [mempool] More detailed log messages
|
||||
- [\#2724](https://github.com/tendermint/tendermint/issues/2724)
|
||||
- [\#2762](https://github.com/tendermint/tendermint/issues/2762)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [autofile] [\#2703](https://github.com/tendermint/tendermint/issues/2703) Do not panic when checking Head size
|
||||
- [crypto/merkle] [\#2756](https://github.com/tendermint/tendermint/issues/2756) Fix crypto/merkle ProofOperators.Verify to check bounds on keypath parts.
|
||||
- [mempool] fix a bug where we create a WAL despite `wal_dir` being empty
|
||||
- [p2p] [\#2771](https://github.com/tendermint/tendermint/issues/2771) Fix `peer-id` label name to `peer_id` in prometheus metrics
|
||||
- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Fix IDs in peer NodeInfo and require them for addresses
|
||||
in AddressBook
|
||||
- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Do not close conn immediately after sending pex addrs in seed mode. Partial fix for [\#2092](https://github.com/tendermint/tendermint/issues/2092).
|
||||
|
||||
## v0.26.0
|
||||
|
||||
*November 2, 2018*
|
||||
@@ -200,8 +108,6 @@ increasing attention to backwards compatibility. Thanks for bearing with us!
|
||||
- [abci] [\#2557](https://github.com/tendermint/tendermint/issues/2557) Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}`
|
||||
- [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Add `BlockVersion` and `P2PVersion` to `RequestInfo`
|
||||
- [crypto/merkle] [\#2298](https://github.com/tendermint/tendermint/issues/2298) General Merkle Proof scheme for chaining various types of Merkle trees together
|
||||
- [docs/architecture] [\#1181](https://github.com/tendermint/tendermint/issues/1181) S
|
||||
plit immutable and mutable parts of priv_validator.json
|
||||
|
||||
### IMPROVEMENTS:
|
||||
- Additional Metrics
|
||||
@@ -234,6 +140,8 @@ plit immutable and mutable parts of priv_validator.json
|
||||
- [state] [\#2616](https://github.com/tendermint/tendermint/issues/2616) Pass nil to NewValidatorSet() when genesis file's Validators field is nil
|
||||
- [p2p] [\#2555](https://github.com/tendermint/tendermint/issues/2555) Fix p2p switch FlushThrottle value (@goolAdapter)
|
||||
- [p2p] [\#2668](https://github.com/tendermint/tendermint/issues/2668) Reconnect to originally dialed address (not self-reported address) for persistent peers
|
||||
- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) AddressBook requires addresses to have IDs; Do not crap out immediately after sending pex addrs in seed mode
|
||||
|
||||
|
||||
## v0.25.0
|
||||
|
||||
@@ -399,8 +307,8 @@ BUG FIXES:
|
||||
*August 22nd, 2018*
|
||||
|
||||
BUG FIXES:
|
||||
- [libs/autofile] [\#2261](https://github.com/tendermint/tendermint/issues/2261) Fix log rotation so it actually happens.
|
||||
- Fixes issues with consensus WAL growing unbounded ala [\#2259](https://github.com/tendermint/tendermint/issues/2259)
|
||||
- [libs/autofile] \#2261 Fix log rotation so it actually happens.
|
||||
- Fixes issues with consensus WAL growing unbounded ala \#2259
|
||||
|
||||
## 0.23.0
|
||||
|
||||
|
@@ -1,13 +1,12 @@
|
||||
# Pending
|
||||
|
||||
## v0.26.4
|
||||
## v0.26.1
|
||||
|
||||
*TBD*
|
||||
*TBA*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
@@ -26,3 +25,7 @@ program](https://hackerone.com/tendermint).
|
||||
### IMPROVEMENTS:
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [crypto/merkle] [\#2756](https://github.com/tendermint/tendermint/issues/2756) Fix crypto/merkle ProofOperators.Verify to check bounds on keypath parts.
|
||||
- [mempool] fix a bug where we create a WAL despite `wal_dir` being empty
|
||||
- [p2p] \#2771 Fix `peer-id` label name in prometheus metrics
|
||||
|
@@ -6,7 +6,7 @@ This code of conduct applies to all projects run by the Tendermint/COSMOS team a
|
||||
|
||||
|
||||
# Conduct
|
||||
## Contact: conduct@tendermint.com
|
||||
## 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.
|
||||
|
||||
|
@@ -27,8 +27,8 @@ Of course, replace `ebuchman` with your git handle.
|
||||
|
||||
To pull in updates from the origin repo, run
|
||||
|
||||
* `git fetch upstream`
|
||||
* `git rebase upstream/master` (or whatever branch you want)
|
||||
* `git fetch upstream`
|
||||
* `git rebase upstream/master` (or whatever branch you want)
|
||||
|
||||
Please don't make Pull Requests to `master`.
|
||||
|
||||
@@ -50,11 +50,6 @@ as apps, tools, and the core, should use dep.
|
||||
Run `dep status` to get a list of vendor dependencies that may not be
|
||||
up-to-date.
|
||||
|
||||
When updating dependencies, please only update the particular dependencies you
|
||||
need. Instead of running `dep ensure -update`, which will update anything,
|
||||
specify exactly the dependency you want to update, eg.
|
||||
`dep ensure -update github.com/tendermint/go-amino`.
|
||||
|
||||
## Vagrant
|
||||
|
||||
If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started
|
||||
@@ -69,74 +64,43 @@ vagrant ssh
|
||||
make test
|
||||
```
|
||||
|
||||
## Changelog
|
||||
## Testing
|
||||
|
||||
Every fix, improvement, feature, or breaking change should be made in a
|
||||
pull-request that includes an update to the `CHANGELOG_PENDING.md` file.
|
||||
All repos should be hooked up to [CircleCI](https://circleci.com/).
|
||||
|
||||
Changelog entries should be formatted as follows:
|
||||
|
||||
```
|
||||
- [module] \#xxx Some description about the change (@contributor)
|
||||
```
|
||||
|
||||
Here, `module` is the part of the code that changed (typically a
|
||||
top-level Go package), `xxx` is the pull-request number, and `contributor`
|
||||
is the author/s of the change.
|
||||
|
||||
It's also acceptable for `xxx` to refer to the relevent issue number, but pull-request
|
||||
numbers are preferred.
|
||||
Note this means pull-requests should be opened first so the changelog can then
|
||||
be updated with the pull-request's number.
|
||||
There is no need to include the full link, as this will be added
|
||||
automatically during release. But please include the backslash and pound, eg. `\#2313`.
|
||||
|
||||
Changelog entries should be ordered alphabetically according to the
|
||||
`module`, and numerically according to the pull-request number.
|
||||
|
||||
Changes with multiple classifications should be doubly included (eg. a bug fix
|
||||
that is also a breaking change should be recorded under both).
|
||||
|
||||
Breaking changes are further subdivided according to the APIs/users they impact.
|
||||
Any change that effects multiple APIs/users should be recorded multiply - for
|
||||
instance, a change to the `Blockchain Protocol` that removes a field from the
|
||||
header should also be recorded under `CLI/RPC/Config` since the field will be
|
||||
removed from the header in rpc responses as well.
|
||||
If they have `.go` files in the root directory, they will be automatically
|
||||
tested by circle using `go test -v -race ./...`. If not, they will need a
|
||||
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
|
||||
includes its continuous integration status using a badge in the `README.md`.
|
||||
|
||||
## Branching Model and Release
|
||||
|
||||
All repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
|
||||
This means that all pull-requests should be made against develop. Any merge to
|
||||
master constitutes a tagged release.
|
||||
User-facing repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
|
||||
That is, these repos should be well versioned, and any merge to master requires a version bump and tagged release.
|
||||
|
||||
Libraries need not follow the model strictly, but would be wise to,
|
||||
especially `go-p2p` and `go-rpc`, as their versions are referenced in tendermint core.
|
||||
|
||||
### Development Procedure:
|
||||
- the latest state of development is on `develop`
|
||||
- `develop` must never fail `make test`
|
||||
- never --force onto `develop` (except when reverting a broken commit, which should seldom happen)
|
||||
- no --force onto `develop` (except when reverting a broken commit, which should seldom happen)
|
||||
- create a development branch either on github.com/tendermint/tendermint, or your fork (using `git remote add origin`)
|
||||
- make changes and update the `CHANGELOG_PENDING.md` to record your change
|
||||
- before submitting a pull request, run `git rebase` on top of the latest `develop`
|
||||
- before submitting a pull request, begin `git rebase` on top of `develop`
|
||||
|
||||
### Pull Merge Procedure:
|
||||
- ensure pull branch is based on a recent develop
|
||||
- ensure pull branch is rebased on develop
|
||||
- run `make test` to ensure that all tests pass
|
||||
- merge pull request
|
||||
- the `unstable` branch may be used to aggregate pull merges before fixing tests
|
||||
- the `unstable` branch may be used to aggregate pull merges before testing once
|
||||
- push master may request that pull requests be rebased on top of `unstable`
|
||||
|
||||
### Release Procedure:
|
||||
- start on `develop`
|
||||
- run integration tests (see `test_integrations` in Makefile)
|
||||
- prepare changelog:
|
||||
- copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md`
|
||||
- run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for
|
||||
all issues
|
||||
- run `bash ./scripts/authors.sh` to get a list of authors since the latest
|
||||
release, and add the github aliases of external contributors to the top of
|
||||
the changelog. To lookup an alias from an email, try `bash
|
||||
./scripts/authors.sh <email>`
|
||||
- reset the `CHANGELOG_PENDING.md`
|
||||
- prepare changelog/release issue
|
||||
- bump versions
|
||||
- push to release/vX.X.X to run the extended integration tests on the CI
|
||||
- push to release-vX.X.X to run the extended integration tests on the CI
|
||||
- merge to master
|
||||
- merge master back to develop
|
||||
|
||||
@@ -151,13 +115,3 @@ master constitutes a tagged release.
|
||||
- merge hotfix-vX.X.X to master
|
||||
- merge hotfix-vX.X.X to develop
|
||||
- delete the hotfix-vX.X.X branch
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
All repos should be hooked up to [CircleCI](https://circleci.com/).
|
||||
|
||||
If they have `.go` files in the root directory, they will be automatically
|
||||
tested by circle using `go test -v -race ./...`. If not, they will need a
|
||||
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
|
||||
includes its continuous integration status using a badge in the `README.md`.
|
||||
|
21
Gopkg.lock
generated
21
Gopkg.lock
generated
@@ -218,16 +218,14 @@
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23"
|
||||
digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/internal",
|
||||
"prometheus/promhttp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3"
|
||||
version = "v0.9.1"
|
||||
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -269,14 +267,6 @@
|
||||
pruneopts = "UT"
|
||||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758"
|
||||
name = "github.com/rs/cors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9a47f48565a795472d43519dd49aac781f3034fb"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
|
||||
name = "github.com/spf13/afero"
|
||||
@@ -368,12 +358,12 @@
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a"
|
||||
digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431"
|
||||
name = "github.com/tendermint/go-amino"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885"
|
||||
version = "v0.14.1"
|
||||
revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12"
|
||||
version = "v0.14.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
|
||||
@@ -526,7 +516,6 @@
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"github.com/rcrowley/go-metrics",
|
||||
"github.com/rs/cors",
|
||||
"github.com/spf13/cobra",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
|
48
Gopkg.toml
48
Gopkg.toml
@@ -20,60 +20,53 @@
|
||||
# unused-packages = true
|
||||
#
|
||||
###########################################################
|
||||
# NOTE: All packages should be pinned to specific versions.
|
||||
# Packages without releases must pin to a commit.
|
||||
|
||||
|
||||
# Allow only patch releases for serialization libraries
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "~0.14.1"
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "=0.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
version = "~1.1.1"
|
||||
version = "=1.1.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "~1.1.0"
|
||||
|
||||
# Allow only minor releases for other libraries
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "^0.6.0"
|
||||
version = "=1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "^1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rs/cors"
|
||||
version = "^1.6.0"
|
||||
version = "=1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "^0.8.0"
|
||||
version = "=0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "^0.0.1"
|
||||
version = "=0.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/viper"
|
||||
version = "^1.0.0"
|
||||
version = "=1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "^1.2.1"
|
||||
version = "=1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "v0.14.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "^1.13.0"
|
||||
version = "=1.13.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
version = "^1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
version = "^0.9.1"
|
||||
version = "=1.2.0"
|
||||
|
||||
###################################
|
||||
## Some repos dont have releases.
|
||||
@@ -97,6 +90,11 @@
|
||||
name = "github.com/tendermint/btcd"
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
# Haven't made a release since 2016.
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rcrowley/go-metrics"
|
||||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
5
Makefile
5
Makefile
@@ -17,6 +17,7 @@ all: check build test install
|
||||
|
||||
check: check_tools get_vendor_deps
|
||||
|
||||
|
||||
########################################
|
||||
### Build Tendermint
|
||||
|
||||
@@ -78,8 +79,6 @@ check_tools:
|
||||
get_tools:
|
||||
@echo "--> Installing tools"
|
||||
./scripts/get_tools.sh
|
||||
|
||||
get_dev_tools:
|
||||
@echo "--> Downloading linters (this may take awhile)"
|
||||
$(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN)
|
||||
|
||||
@@ -328,4 +327,4 @@ build-slate:
|
||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||
# unless there is a reason not to.
|
||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||
.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all
|
||||
.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all
|
||||
|
15
README.md
15
README.md
@@ -41,7 +41,7 @@ please [contact us](mailto:partners@tendermint.com) and [join the chat](https://
|
||||
## Security
|
||||
|
||||
To report a security vulnerability, see our [bug bounty
|
||||
program](https://hackerone.com/tendermint)
|
||||
program](https://tendermint.com/security).
|
||||
|
||||
For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.md)
|
||||
|
||||
@@ -51,18 +51,14 @@ Requirement|Notes
|
||||
---|---
|
||||
Go version | Go1.10 or higher
|
||||
|
||||
## Documentation
|
||||
|
||||
Complete documentation can be found on the [website](https://tendermint.com/docs/).
|
||||
|
||||
### Install
|
||||
## Install
|
||||
|
||||
See the [install instructions](/docs/introduction/install.md)
|
||||
|
||||
### Quick Start
|
||||
## Quick Start
|
||||
|
||||
- [Single node](/docs/introduction/quick-start.md)
|
||||
- [Local cluster using docker-compose](/docs/networks/docker-compose.md)
|
||||
- [Single node](/docs/tendermint-core/using-tendermint.md)
|
||||
- [Local cluster using docker-compose](/networks/local)
|
||||
- [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md)
|
||||
- [Join the Cosmos testnet](https://cosmos.network/testnet)
|
||||
|
||||
@@ -95,7 +91,6 @@ Additional documentation is found [here](/docs/tools).
|
||||
|
||||
### Research
|
||||
|
||||
* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
|
||||
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
|
||||
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
|
||||
* [Blog](https://blog.cosmos.network/tendermint/home)
|
||||
|
@@ -1,8 +1,7 @@
|
||||
# Security
|
||||
|
||||
As part of our [Coordinated Vulnerability Disclosure
|
||||
Policy](https://tendermint.com/security), we operate a [bug
|
||||
bounty](https://hackerone.com/tendermint).
|
||||
Policy](https://tendermint.com/security), we operate a bug bounty.
|
||||
See the policy for more details on submissions and rewards.
|
||||
|
||||
Here is a list of examples of the kinds of bugs we're most interested in:
|
||||
|
2
Vagrantfile
vendored
2
Vagrantfile
vendored
@@ -53,6 +53,6 @@ Vagrant.configure("2") do |config|
|
||||
|
||||
# get all deps and tools, ready to install/test
|
||||
su - vagrant -c 'source /home/vagrant/.bash_profile'
|
||||
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_dev_tools && make get_vendor_deps'
|
||||
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_vendor_deps'
|
||||
SHELL
|
||||
end
|
||||
|
156
abci/README.md
156
abci/README.md
@@ -1,5 +1,7 @@
|
||||
# Application BlockChain Interface (ABCI)
|
||||
|
||||
[](https://circleci.com/gh/tendermint/abci)
|
||||
|
||||
Blockchains are systems for multi-master state machine replication.
|
||||
**ABCI** is an interface that defines the boundary between the replication engine (the blockchain),
|
||||
and the state machine (the application).
|
||||
@@ -10,28 +12,160 @@ Previously, the ABCI was referred to as TMSP.
|
||||
|
||||
The community has provided a number of addtional implementations, see the [Tendermint Ecosystem](https://tendermint.com/ecosystem)
|
||||
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
To get up and running quickly, see the [getting started guide](../docs/app-dev/getting-started.md) along with the [abci-cli documentation](../docs/app-dev/abci-cli.md) which will go through the examples found in the [examples](./example/) directory.
|
||||
|
||||
## Specification
|
||||
|
||||
A detailed description of the ABCI methods and message types is contained in:
|
||||
|
||||
- [The main spec](../docs/spec/abci/abci.md)
|
||||
- [A protobuf file](./types/types.proto)
|
||||
- [A Go interface](./types/application.go)
|
||||
- [A prose specification](specification.md)
|
||||
- [A protobuf file](https://github.com/tendermint/tendermint/blob/master/abci/types/types.proto)
|
||||
- [A Go interface](https://github.com/tendermint/tendermint/blob/master/abci/types/application.go).
|
||||
|
||||
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](https://tendermint.com/docs/).
|
||||
The two guides to focus on are the `Application Development Guide` and `Using ABCI-CLI`.
|
||||
|
||||
|
||||
## Protocol Buffers
|
||||
|
||||
To compile the protobuf file, run (from the root of the repo):
|
||||
To compile the protobuf file, run:
|
||||
|
||||
```
|
||||
make protoc_abci
|
||||
cd $GOPATH/src/github.com/tendermint/tendermint/; make protoc_abci
|
||||
```
|
||||
|
||||
See `protoc --help` and [the Protocol Buffers site](https://developers.google.com/protocol-buffers)
|
||||
for details on compiling for other languages. Note we also include a [GRPC](https://www.grpc.io/docs)
|
||||
for details on compiling for other languages. Note we also include a [GRPC](http://www.grpc.io/docs)
|
||||
service definition.
|
||||
|
||||
## Install ABCI-CLI
|
||||
|
||||
The `abci-cli` is a simple tool for debugging ABCI servers and running some
|
||||
example apps. To install it:
|
||||
|
||||
```
|
||||
mkdir -p $GOPATH/src/github.com/tendermint
|
||||
cd $GOPATH/src/github.com/tendermint
|
||||
git clone https://github.com/tendermint/tendermint.git
|
||||
cd tendermint
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install_abci
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
We provide three implementations of the ABCI in Go:
|
||||
|
||||
- Golang in-process
|
||||
- ABCI-socket
|
||||
- GRPC
|
||||
|
||||
Note the GRPC version is maintained primarily to simplify onboarding and prototyping and is not receiving the same
|
||||
attention to security and performance as the others
|
||||
|
||||
### In Process
|
||||
|
||||
The simplest implementation just uses function calls within Go.
|
||||
This means ABCI applications written in Golang can be compiled with TendermintCore and run as a single binary.
|
||||
|
||||
See the [examples](#examples) below for more information.
|
||||
|
||||
### Socket (TSP)
|
||||
|
||||
ABCI is best implemented as a streaming protocol.
|
||||
The socket implementation provides for asynchronous, ordered message passing over unix or tcp.
|
||||
Messages are serialized using Protobuf3 and length-prefixed with a [signed Varint](https://developers.google.com/protocol-buffers/docs/encoding?csw=1#signed-integers)
|
||||
|
||||
For example, if the Protobuf3 encoded ABCI message is `0xDEADBEEF` (4 bytes), the length-prefixed message is `0x08DEADBEEF`, since `0x08` is the signed varint
|
||||
encoding of `4`. If the Protobuf3 encoded ABCI message is 65535 bytes long, the length-prefixed message would be like `0xFEFF07...`.
|
||||
|
||||
Note the benefit of using this `varint` encoding over the old version (where integers were encoded as `<len of len><big endian len>` is that
|
||||
it is the standard way to encode integers in Protobuf. It is also generally shorter.
|
||||
|
||||
### GRPC
|
||||
|
||||
GRPC is an rpc framework native to Protocol Buffers with support in many languages.
|
||||
Implementing the ABCI using GRPC can allow for faster prototyping, but is expected to be much slower than
|
||||
the ordered, asynchronous socket protocol. The implementation has also not received as much testing or review.
|
||||
|
||||
Note the length-prefixing used in the socket implementation does not apply for GRPC.
|
||||
|
||||
## Usage
|
||||
|
||||
The `abci-cli` tool wraps an ABCI client and can be used for probing/testing an ABCI server.
|
||||
For instance, `abci-cli test` will run a test sequence against a listening server running the Counter application (see below).
|
||||
It can also be used to run some example applications.
|
||||
See [the documentation](https://tendermint.com/docs/) for more details.
|
||||
|
||||
### Examples
|
||||
|
||||
Check out the variety of example applications in the [example directory](example/).
|
||||
It also contains the code refered to by the `counter` and `kvstore` apps; these apps come
|
||||
built into the `abci-cli` binary.
|
||||
|
||||
#### Counter
|
||||
|
||||
The `abci-cli counter` application illustrates nonce checking in transactions. It's code looks like:
|
||||
|
||||
```golang
|
||||
func cmdCounter(cmd *cobra.Command, args []string) error {
|
||||
|
||||
app := counter.NewCounterApplication(flagSerial)
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
|
||||
// Start the listener
|
||||
srv, err := server.NewServer(flagAddrC, flagAbci, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.SetLogger(logger.With("module", "abci-server"))
|
||||
if err := srv.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
and can be found in [this file](cmd/abci-cli/abci-cli.go).
|
||||
|
||||
#### kvstore
|
||||
|
||||
The `abci-cli kvstore` application, which illustrates a simple key-value Merkle tree
|
||||
|
||||
```golang
|
||||
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
|
||||
// Create the application - in memory or persisted to disk
|
||||
var app types.Application
|
||||
if flagPersist == "" {
|
||||
app = kvstore.NewKVStoreApplication()
|
||||
} else {
|
||||
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
|
||||
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
|
||||
}
|
||||
|
||||
// Start the listener
|
||||
srv, err := server.NewServer(flagAddrD, flagAbci, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.SetLogger(logger.With("module", "abci-server"))
|
||||
if err := srv.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
@@ -105,8 +105,8 @@ func (reqRes *ReqRes) SetCallback(cb func(res *types.Response)) {
|
||||
return
|
||||
}
|
||||
|
||||
defer reqRes.mtx.Unlock()
|
||||
reqRes.cb = cb
|
||||
reqRes.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (reqRes *ReqRes) GetCallback() func(*types.Response) {
|
||||
|
@@ -54,7 +54,7 @@ RETRY_LOOP:
|
||||
if cli.mustConnect {
|
||||
return err
|
||||
}
|
||||
cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err)
|
||||
cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr))
|
||||
time.Sleep(time.Second * dialRetryIntervalSeconds)
|
||||
continue RETRY_LOOP
|
||||
}
|
||||
@@ -111,8 +111,8 @@ func (cli *grpcClient) Error() error {
|
||||
// NOTE: callback may get internally generated flush responses.
|
||||
func (cli *grpcClient) SetResponseCallback(resCb Callback) {
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
cli.resCb = resCb
|
||||
cli.mtx.Unlock()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@@ -9,13 +9,8 @@ import (
|
||||
|
||||
var _ Client = (*localClient)(nil)
|
||||
|
||||
// NOTE: use defer to unlock mutex because Application might panic (e.g., in
|
||||
// case of malicious tx or query). It only makes sense for publicly exposed
|
||||
// methods like CheckTx (/broadcast_tx_* RPC endpoint) or Query (/abci_query
|
||||
// RPC endpoint), but defers are used everywhere for the sake of consistency.
|
||||
type localClient struct {
|
||||
cmn.BaseService
|
||||
|
||||
mtx *sync.Mutex
|
||||
types.Application
|
||||
Callback
|
||||
@@ -35,8 +30,8 @@ func NewLocalClient(mtx *sync.Mutex, app types.Application) *localClient {
|
||||
|
||||
func (app *localClient) SetResponseCallback(cb Callback) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
app.Callback = cb
|
||||
app.mtx.Unlock()
|
||||
}
|
||||
|
||||
// TODO: change types.Application to include Error()?
|
||||
@@ -50,9 +45,6 @@ func (app *localClient) FlushAsync() *ReqRes {
|
||||
}
|
||||
|
||||
func (app *localClient) EchoAsync(msg string) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
return app.callback(
|
||||
types.ToRequestEcho(msg),
|
||||
types.ToResponseEcho(msg),
|
||||
@@ -61,9 +53,8 @@ func (app *localClient) EchoAsync(msg string) *ReqRes {
|
||||
|
||||
func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Info(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestInfo(req),
|
||||
types.ToResponseInfo(res),
|
||||
@@ -72,9 +63,8 @@ func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes {
|
||||
|
||||
func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.SetOption(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestSetOption(req),
|
||||
types.ToResponseSetOption(res),
|
||||
@@ -83,9 +73,8 @@ func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
|
||||
|
||||
func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.DeliverTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestDeliverTx(tx),
|
||||
types.ToResponseDeliverTx(res),
|
||||
@@ -94,9 +83,8 @@ func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
|
||||
func (app *localClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.CheckTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestCheckTx(tx),
|
||||
types.ToResponseCheckTx(res),
|
||||
@@ -105,9 +93,8 @@ func (app *localClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
|
||||
func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Query(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestQuery(req),
|
||||
types.ToResponseQuery(res),
|
||||
@@ -116,9 +103,8 @@ func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes {
|
||||
|
||||
func (app *localClient) CommitAsync() *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Commit()
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestCommit(),
|
||||
types.ToResponseCommit(res),
|
||||
@@ -127,20 +113,19 @@ func (app *localClient) CommitAsync() *ReqRes {
|
||||
|
||||
func (app *localClient) InitChainAsync(req types.RequestInitChain) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.InitChain(req)
|
||||
return app.callback(
|
||||
reqRes := app.callback(
|
||||
types.ToRequestInitChain(req),
|
||||
types.ToResponseInitChain(res),
|
||||
)
|
||||
app.mtx.Unlock()
|
||||
return reqRes
|
||||
}
|
||||
|
||||
func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.BeginBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestBeginBlock(req),
|
||||
types.ToResponseBeginBlock(res),
|
||||
@@ -149,9 +134,8 @@ func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
|
||||
|
||||
func (app *localClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.EndBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestEndBlock(req),
|
||||
types.ToResponseEndBlock(res),
|
||||
@@ -170,73 +154,64 @@ func (app *localClient) EchoSync(msg string) (*types.ResponseEcho, error) {
|
||||
|
||||
func (app *localClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Info(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.SetOption(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.DeliverTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.CheckTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Query(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) CommitSync() (*types.ResponseCommit, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Commit()
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.InitChain(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.BeginBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.EndBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@@ -67,7 +67,7 @@ RETRY_LOOP:
|
||||
if cli.mustConnect {
|
||||
return err
|
||||
}
|
||||
cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr), "err", err)
|
||||
cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr))
|
||||
time.Sleep(time.Second * dialRetryIntervalSeconds)
|
||||
continue RETRY_LOOP
|
||||
}
|
||||
@@ -118,8 +118,8 @@ func (cli *socketClient) Error() error {
|
||||
// NOTE: callback may get internally generated flush responses.
|
||||
func (cli *socketClient) SetResponseCallback(resCb Callback) {
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
cli.resCb = resCb
|
||||
cli.mtx.Unlock()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@@ -197,7 +197,6 @@ func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (tp *bcrTestPeer) FlushStop() {}
|
||||
func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
|
||||
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.DefaultNodeInfo{} }
|
||||
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
||||
|
@@ -54,9 +54,6 @@ var RootCmd = &cobra.Command{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.LogFormat == cfg.LogFormatJSON {
|
||||
logger = log.NewTMJSONLogger(log.NewSyncWriter(os.Stdout))
|
||||
}
|
||||
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -14,11 +14,6 @@ const (
|
||||
FuzzModeDrop = iota
|
||||
// FuzzModeDelay is a mode in which we randomly sleep
|
||||
FuzzModeDelay
|
||||
|
||||
// LogFormatPlain is a format for colored text
|
||||
LogFormatPlain = "plain"
|
||||
// LogFormatJSON is a format for json output
|
||||
LogFormatJSON = "json"
|
||||
)
|
||||
|
||||
// NOTE: Most of the structs & relevant comments + the
|
||||
@@ -99,9 +94,6 @@ func (cfg *Config) SetRoot(root string) *Config {
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *Config) ValidateBasic() error {
|
||||
if err := cfg.BaseConfig.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cfg.RPC.ValidateBasic(); err != nil {
|
||||
return errors.Wrap(err, "Error in [rpc] section")
|
||||
}
|
||||
@@ -153,9 +145,6 @@ type BaseConfig struct {
|
||||
// Output level for logging
|
||||
LogLevel string `mapstructure:"log_level"`
|
||||
|
||||
// Output format: 'plain' (colored text) or 'json'
|
||||
LogFormat string `mapstructure:"log_format"`
|
||||
|
||||
// Path to the JSON file containing the initial validator set and other meta data
|
||||
Genesis string `mapstructure:"genesis_file"`
|
||||
|
||||
@@ -190,7 +179,6 @@ func DefaultBaseConfig() BaseConfig {
|
||||
ProxyApp: "tcp://127.0.0.1:26658",
|
||||
ABCI: "socket",
|
||||
LogLevel: DefaultPackageLogLevels(),
|
||||
LogFormat: LogFormatPlain,
|
||||
ProfListenAddress: "",
|
||||
FastSync: true,
|
||||
FilterPeers: false,
|
||||
@@ -233,17 +221,6 @@ func (cfg BaseConfig) DBDir() string {
|
||||
return rootify(cfg.DBPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg BaseConfig) ValidateBasic() error {
|
||||
switch cfg.LogFormat {
|
||||
case LogFormatPlain, LogFormatJSON:
|
||||
default:
|
||||
return errors.New("unknown log_format (must be 'plain' or 'json')")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultLogLevel returns a default log level of "error"
|
||||
func DefaultLogLevel() string {
|
||||
return "error"
|
||||
@@ -265,18 +242,6 @@ type RPCConfig struct {
|
||||
// TCP or UNIX socket address for the RPC server to listen on
|
||||
ListenAddress string `mapstructure:"laddr"`
|
||||
|
||||
// A list of origins a cross-domain request can be executed from.
|
||||
// If the special '*' value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com).
|
||||
// Only one wildcard can be used per origin.
|
||||
CORSAllowedOrigins []string `mapstructure:"cors_allowed_origins"`
|
||||
|
||||
// A list of methods the client is allowed to use with cross-domain requests.
|
||||
CORSAllowedMethods []string `mapstructure:"cors_allowed_methods"`
|
||||
|
||||
// A list of non simple headers the client is allowed to use with cross-domain requests.
|
||||
CORSAllowedHeaders []string `mapstructure:"cors_allowed_headers"`
|
||||
|
||||
// TCP or UNIX socket address for the gRPC server to listen on
|
||||
// NOTE: This server only supports /broadcast_tx_commit
|
||||
GRPCListenAddress string `mapstructure:"grpc_laddr"`
|
||||
@@ -304,10 +269,8 @@ type RPCConfig struct {
|
||||
// DefaultRPCConfig returns a default configuration for the RPC server
|
||||
func DefaultRPCConfig() *RPCConfig {
|
||||
return &RPCConfig{
|
||||
ListenAddress: "tcp://0.0.0.0:26657",
|
||||
CORSAllowedOrigins: []string{},
|
||||
CORSAllowedMethods: []string{"HEAD", "GET", "POST"},
|
||||
CORSAllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"},
|
||||
ListenAddress: "tcp://0.0.0.0:26657",
|
||||
|
||||
GRPCListenAddress: "",
|
||||
GRPCMaxOpenConnections: 900,
|
||||
|
||||
@@ -337,11 +300,6 @@ func (cfg *RPCConfig) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsCorsEnabled returns true if cross-origin resource sharing is enabled.
|
||||
func (cfg *RPCConfig) IsCorsEnabled() bool {
|
||||
return len(cfg.CORSAllowedOrigins) != 0
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// P2PConfig
|
||||
|
||||
|
@@ -86,9 +86,6 @@ db_dir = "{{ js .BaseConfig.DBPath }}"
|
||||
# Output level for logging, including package level options
|
||||
log_level = "{{ .BaseConfig.LogLevel }}"
|
||||
|
||||
# Output format: 'plain' (colored text) or 'json'
|
||||
log_format = "{{ .BaseConfig.LogFormat }}"
|
||||
|
||||
##### additional base config options #####
|
||||
|
||||
# Path to the JSON file containing the initial validator set and other meta data
|
||||
@@ -122,17 +119,6 @@ filter_peers = {{ .BaseConfig.FilterPeers }}
|
||||
# TCP or UNIX socket address for the RPC server to listen on
|
||||
laddr = "{{ .RPC.ListenAddress }}"
|
||||
|
||||
# A list of origins a cross-domain request can be executed from
|
||||
# Default value '[]' disables cors support
|
||||
# Use '["*"]' to allow any origin
|
||||
cors_allowed_origins = "{{ .RPC.CORSAllowedOrigins }}"
|
||||
|
||||
# A list of methods the client is allowed to use with cross-domain requests
|
||||
cors_allowed_methods = "{{ .RPC.CORSAllowedMethods }}"
|
||||
|
||||
# A list of non simple headers the client is allowed to use with cross-domain requests
|
||||
cors_allowed_headers = "{{ .RPC.CORSAllowedHeaders }}"
|
||||
|
||||
# TCP or UNIX socket address for the gRPC server to listen on
|
||||
# NOTE: This server only supports /broadcast_tx_commit
|
||||
grpc_laddr = "{{ .RPC.GRPCListenAddress }}"
|
||||
|
@@ -405,38 +405,8 @@ func ensureNewVote(voteCh <-chan interface{}, height int64, round int) {
|
||||
}
|
||||
|
||||
func ensureNewRound(roundCh <-chan interface{}, height int64, round int) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewRound event")
|
||||
case ev := <-roundCh:
|
||||
rs, ok := ev.(types.EventDataNewRound)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataNewRound, got %v.Wrong subscription channel?",
|
||||
reflect.TypeOf(rs)))
|
||||
}
|
||||
if rs.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
|
||||
}
|
||||
if rs.Round != round {
|
||||
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureProposalHeartbeat(heartbeatCh <-chan interface{}) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for ProposalHeartbeat event")
|
||||
case ev := <-heartbeatCh:
|
||||
heartbeat, ok := ev.(types.EventDataProposalHeartbeat)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("expected a *types.EventDataProposalHeartbeat, "+
|
||||
"got %v. wrong subscription channel?",
|
||||
reflect.TypeOf(heartbeat)))
|
||||
}
|
||||
}
|
||||
ensureNewEvent(roundCh, height, round, ensureTimeout,
|
||||
"Timeout expired while waiting for NewRound event")
|
||||
}
|
||||
|
||||
func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) {
|
||||
@@ -446,24 +416,8 @@ func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, tim
|
||||
}
|
||||
|
||||
func ensureNewProposal(proposalCh <-chan interface{}, height int64, round int) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewProposal event")
|
||||
case ev := <-proposalCh:
|
||||
rs, ok := ev.(types.EventDataCompleteProposal)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataCompleteProposal, got %v.Wrong subscription channel?",
|
||||
reflect.TypeOf(rs)))
|
||||
}
|
||||
if rs.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
|
||||
}
|
||||
if rs.Round != round {
|
||||
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
|
||||
}
|
||||
}
|
||||
ensureNewEvent(proposalCh, height, round, ensureTimeout,
|
||||
"Timeout expired while waiting for NewProposal event")
|
||||
}
|
||||
|
||||
func ensureNewValidBlock(validBlockCh <-chan interface{}, height int64, round int) {
|
||||
@@ -538,30 +492,6 @@ func ensureVote(voteCh <-chan interface{}, height int64, round int,
|
||||
}
|
||||
}
|
||||
|
||||
func ensureProposal(proposalCh <-chan interface{}, height int64, round int, propId types.BlockID) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewProposal event")
|
||||
case ev := <-proposalCh:
|
||||
rs, ok := ev.(types.EventDataCompleteProposal)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataCompleteProposal, got %v.Wrong subscription channel?",
|
||||
reflect.TypeOf(rs)))
|
||||
}
|
||||
if rs.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
|
||||
}
|
||||
if rs.Round != round {
|
||||
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
|
||||
}
|
||||
if !rs.BlockID.Equals(propId) {
|
||||
panic("Proposed block does not match expected block")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePrecommit(voteCh <-chan interface{}, height int64, round int) {
|
||||
ensureVote(voteCh, height, round, types.PrecommitType)
|
||||
}
|
||||
|
@@ -71,18 +71,18 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
|
||||
}
|
||||
startTestRound(cs, height, round)
|
||||
|
||||
ensureNewRound(newRoundCh, height, round) // first round at first height
|
||||
ensureNewRoundStep(newRoundCh, height, round) // first round at first height
|
||||
ensureNewEventOnChannel(newBlockCh) // first block gets committed
|
||||
|
||||
height = height + 1 // moving to the next height
|
||||
round = 0
|
||||
|
||||
ensureNewRound(newRoundCh, height, round) // first round at next height
|
||||
ensureNewRoundStep(newRoundCh, height, round) // first round at next height
|
||||
deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round
|
||||
ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
|
||||
|
||||
round = round + 1 // moving to the next round
|
||||
ensureNewRound(newRoundCh, height, round) // wait for the next round
|
||||
ensureNewRoundStep(newRoundCh, height, round) // wait for the next round
|
||||
ensureNewEventOnChannel(newBlockCh) // now we can commit the block
|
||||
}
|
||||
|
||||
|
@@ -183,11 +183,7 @@ func (conR *ConsensusReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
|
||||
return
|
||||
}
|
||||
// TODO
|
||||
// ps, ok := peer.Get(PeerStateKey).(*PeerState)
|
||||
// if !ok {
|
||||
// panic(fmt.Sprintf("Peer %v has no state", peer))
|
||||
// }
|
||||
// ps.Disconnect()
|
||||
//peer.Get(PeerStateKey).(*PeerState).Disconnect()
|
||||
}
|
||||
|
||||
// Receive implements Reactor
|
||||
@@ -218,10 +214,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
|
||||
|
||||
// Get peer states
|
||||
ps, ok := src.Get(types.PeerStateKey).(*PeerState)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Peer %v has no state", src))
|
||||
}
|
||||
ps := src.Get(types.PeerStateKey).(*PeerState)
|
||||
|
||||
switch chID {
|
||||
case StateChannel:
|
||||
@@ -300,9 +293,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
switch msg := msg.(type) {
|
||||
case *VoteMessage:
|
||||
cs := conR.conS
|
||||
cs.mtx.RLock()
|
||||
cs.mtx.Lock()
|
||||
height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size()
|
||||
cs.mtx.RUnlock()
|
||||
cs.mtx.Unlock()
|
||||
ps.EnsureVoteBitArrays(height, valSize)
|
||||
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
||||
ps.SetHasVote(msg.Vote)
|
||||
@@ -402,7 +395,7 @@ func (conR *ConsensusReactor) unsubscribeFromBroadcastEvents() {
|
||||
|
||||
func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(hb *types.Heartbeat) {
|
||||
conR.Logger.Debug("Broadcasting proposal heartbeat message",
|
||||
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence, "address", hb.ValidatorAddress)
|
||||
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence)
|
||||
msg := &ProposalHeartbeatMessage{hb}
|
||||
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
|
||||
}
|
||||
@@ -435,10 +428,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
|
||||
/*
|
||||
// TODO: Make this broadcast more selective.
|
||||
for _, peer := range conR.Switch.Peers().List() {
|
||||
ps, ok := peer.Get(PeerStateKey).(*PeerState)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Peer %v has no state", peer))
|
||||
}
|
||||
ps := peer.Get(PeerStateKey).(*PeerState)
|
||||
prs := ps.GetRoundState()
|
||||
if prs.Height == vote.Height {
|
||||
// TODO: Also filter on round?
|
||||
@@ -836,10 +826,7 @@ func (conR *ConsensusReactor) peerStatsRoutine() {
|
||||
continue
|
||||
}
|
||||
// Get peer state
|
||||
ps, ok := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Peer %v has no state", peer))
|
||||
}
|
||||
ps := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
switch msg.Msg.(type) {
|
||||
case *VoteMessage:
|
||||
if numVotes := ps.RecordVote(); numVotes%votesToContributeToBecomeGoodPeer == 0 {
|
||||
@@ -872,10 +859,7 @@ func (conR *ConsensusReactor) StringIndented(indent string) string {
|
||||
s := "ConsensusReactor{\n"
|
||||
s += indent + " " + conR.conS.StringIndented(indent+" ") + "\n"
|
||||
for _, peer := range conR.Switch.Peers().List() {
|
||||
ps, ok := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Peer %v has no state", peer))
|
||||
}
|
||||
ps := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
s += indent + " " + ps.StringIndented(indent+" ") + "\n"
|
||||
}
|
||||
s += indent + "}"
|
||||
@@ -1033,11 +1017,7 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
|
||||
if vote, ok := ps.PickVoteToSend(votes); ok {
|
||||
msg := &VoteMessage{vote}
|
||||
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
|
||||
if ps.peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg)) {
|
||||
ps.SetHasVote(vote)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return ps.peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1066,6 +1046,7 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote
|
||||
return nil, false // Not something worth sending
|
||||
}
|
||||
if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
|
||||
ps.setHasVote(height, round, type_, index)
|
||||
return votes.GetByIndex(index), true
|
||||
}
|
||||
return nil, false
|
||||
|
@@ -58,18 +58,7 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error {
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep)
|
||||
}
|
||||
defer func() {
|
||||
// drain newStepCh to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-newStepCh:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
}()
|
||||
defer cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
|
||||
// just open the file for reading, no need to use wal
|
||||
fp, err := os.OpenFile(file, os.O_RDONLY, 0600)
|
||||
@@ -232,18 +221,7 @@ func (pb *playback) replayConsoleLoop() int {
|
||||
if err != nil {
|
||||
cmn.Exit(fmt.Sprintf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep))
|
||||
}
|
||||
defer func() {
|
||||
// drain newStepCh to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-newStepCh:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
}()
|
||||
defer pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
|
||||
if len(tokens) == 1 {
|
||||
if err := pb.replayReset(1, newStepCh); err != nil {
|
||||
|
@@ -207,16 +207,18 @@ func (cs *ConsensusState) GetState() sm.State {
|
||||
// GetLastHeight returns the last height committed.
|
||||
// If there were no blocks, returns 0.
|
||||
func (cs *ConsensusState) GetLastHeight() int64 {
|
||||
cs.mtx.RLock()
|
||||
defer cs.mtx.RUnlock()
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
|
||||
return cs.RoundState.Height - 1
|
||||
}
|
||||
|
||||
// GetRoundState returns a shallow copy of the internal consensus state.
|
||||
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
|
||||
cs.mtx.RLock()
|
||||
defer cs.mtx.RUnlock()
|
||||
|
||||
rs := cs.RoundState // copy
|
||||
cs.mtx.RUnlock()
|
||||
return &rs
|
||||
}
|
||||
|
||||
@@ -224,6 +226,7 @@ func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
|
||||
func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
|
||||
cs.mtx.RLock()
|
||||
defer cs.mtx.RUnlock()
|
||||
|
||||
return cdc.MarshalJSON(cs.RoundState)
|
||||
}
|
||||
|
||||
@@ -231,6 +234,7 @@ func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
|
||||
func (cs *ConsensusState) GetRoundStateSimpleJSON() ([]byte, error) {
|
||||
cs.mtx.RLock()
|
||||
defer cs.mtx.RUnlock()
|
||||
|
||||
return cdc.MarshalJSON(cs.RoundState.RoundStateSimple())
|
||||
}
|
||||
|
||||
@@ -244,15 +248,15 @@ func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) {
|
||||
// SetPrivValidator sets the private validator account for signing votes.
|
||||
func (cs *ConsensusState) SetPrivValidator(priv types.PrivValidator) {
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
cs.privValidator = priv
|
||||
cs.mtx.Unlock()
|
||||
}
|
||||
|
||||
// SetTimeoutTicker sets the local timer. It may be useful to overwrite for testing.
|
||||
func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker) {
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
cs.timeoutTicker = timeoutTicker
|
||||
cs.mtx.Unlock()
|
||||
}
|
||||
|
||||
// LoadCommit loads the commit for a given height.
|
||||
@@ -772,7 +776,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping
|
||||
cs.triggeredTimeoutPrecommit = false
|
||||
|
||||
cs.eventBus.PublishEventNewRound(cs.NewRoundEvent())
|
||||
cs.eventBus.PublishEventNewRound(cs.RoundStateEvent())
|
||||
cs.metrics.Rounds.Set(float64(round))
|
||||
|
||||
// Wait for txs to be available in the mempool
|
||||
@@ -802,14 +806,8 @@ func (cs *ConsensusState) needProofBlock(height int64) bool {
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
addr := cs.privValidator.GetAddress()
|
||||
|
||||
if !cs.Validators.HasAddress(addr) {
|
||||
logger.Debug("Not sending proposalHearbeat. This node is not a validator", "addr", addr, "vals", cs.Validators)
|
||||
return
|
||||
}
|
||||
counter := 0
|
||||
addr := cs.privValidator.GetAddress()
|
||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||
chainID := cs.state.ChainID
|
||||
for {
|
||||
@@ -1410,7 +1408,7 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
|
||||
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
|
||||
if proposal.POLRound < -1 ||
|
||||
(proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) {
|
||||
return ErrInvalidProposalPOLRound
|
||||
@@ -1468,7 +1466,7 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
|
||||
}
|
||||
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
|
||||
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
|
||||
cs.eventBus.PublishEventCompleteProposal(cs.CompleteProposalEvent())
|
||||
cs.eventBus.PublishEventCompleteProposal(cs.RoundStateEvent())
|
||||
|
||||
// Update Valid* if we can.
|
||||
prevotes := cs.Votes.Prevotes(cs.Round)
|
||||
|
@@ -11,8 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
tmevents "github.com/tendermint/tendermint/libs/events"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
@@ -199,8 +197,9 @@ func TestStateBadProposal(t *testing.T) {
|
||||
stateHash[0] = byte((stateHash[0] + 1) % 255)
|
||||
propBlock.AppHash = stateHash
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
blockID := types.BlockID{propBlock.Hash(), propBlockParts.Header()}
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID)
|
||||
proposal := types.NewProposal(
|
||||
vs2.Height, round, -1,
|
||||
types.BlockID{propBlock.Hash(), propBlockParts.Header()})
|
||||
if err := vs2.SignProposal(config.ChainID(), proposal); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
}
|
||||
@@ -214,7 +213,7 @@ func TestStateBadProposal(t *testing.T) {
|
||||
startTestRound(cs1, height, round)
|
||||
|
||||
// wait for proposal
|
||||
ensureProposal(proposalCh, height, round, blockID)
|
||||
ensureNewProposal(proposalCh, height, round)
|
||||
|
||||
// wait for prevote
|
||||
ensurePrevote(voteCh, height, round)
|
||||
@@ -1029,33 +1028,6 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) {
|
||||
assert.True(t, rs.ValidRound == round)
|
||||
}
|
||||
|
||||
// regression for #2518
|
||||
func TestNoHearbeatWhenNotValidator(t *testing.T) {
|
||||
cs, _ := randConsensusState(4)
|
||||
cs.Validators = types.NewValidatorSet(nil) // make sure we are not in the validator set
|
||||
|
||||
cs.evsw.AddListenerForEvent("testing", types.EventProposalHeartbeat,
|
||||
func(data tmevents.EventData) {
|
||||
t.Errorf("Should not have broadcasted heartbeat")
|
||||
})
|
||||
go cs.proposalHeartbeat(10, 1)
|
||||
|
||||
cs.Stop()
|
||||
|
||||
// if a faulty implementation sends an event, we should wait here a little bit to make sure we don't miss it by prematurely leaving the test method
|
||||
time.Sleep((proposalHeartbeatIntervalSeconds + 1) * time.Second)
|
||||
}
|
||||
|
||||
// regression for #2518
|
||||
func TestHearbeatWhenWeAreValidator(t *testing.T) {
|
||||
cs, _ := randConsensusState(4)
|
||||
heartbeatCh := subscribe(cs.eventBus, types.EventQueryProposalHeartbeat)
|
||||
|
||||
go cs.proposalHeartbeat(10, 1)
|
||||
ensureProposalHeartbeat(heartbeatCh)
|
||||
|
||||
}
|
||||
|
||||
// What we want:
|
||||
// P0 miss to lock B as Proposal Block is missing, but set valid block to B after
|
||||
// receiving delayed Block Proposal.
|
||||
|
@@ -112,50 +112,18 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple {
|
||||
}
|
||||
}
|
||||
|
||||
// NewRoundEvent returns the RoundState with proposer information as an event.
|
||||
func (rs *RoundState) NewRoundEvent() types.EventDataNewRound {
|
||||
addr := rs.Validators.GetProposer().Address
|
||||
idx, _ := rs.Validators.GetByAddress(addr)
|
||||
|
||||
return types.EventDataNewRound{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
Proposer: types.ValidatorInfo{
|
||||
Address: addr,
|
||||
Index: idx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CompleteProposalEvent returns information about a proposed block as an event.
|
||||
func (rs *RoundState) CompleteProposalEvent() types.EventDataCompleteProposal {
|
||||
// We must construct BlockID from ProposalBlock and ProposalBlockParts
|
||||
// cs.Proposal is not guaranteed to be set when this function is called
|
||||
blockId := types.BlockID{
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartsHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
|
||||
return types.EventDataCompleteProposal{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
BlockID: blockId,
|
||||
}
|
||||
}
|
||||
|
||||
// RoundStateEvent returns the H/R/S of the RoundState as an event.
|
||||
func (rs *RoundState) RoundStateEvent() types.EventDataRoundState {
|
||||
// copy the RoundState.
|
||||
// TODO: if we want to avoid this, we may need synchronous events after all
|
||||
rsCopy := *rs
|
||||
return types.EventDataRoundState{
|
||||
edrs := types.EventDataRoundState{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
RoundState: &rsCopy,
|
||||
}
|
||||
return edrs
|
||||
}
|
||||
|
||||
// String returns a string
|
||||
|
@@ -13,7 +13,6 @@ import (
|
||||
amino "github.com/tendermint/go-amino"
|
||||
auto "github.com/tendermint/tendermint/libs/autofile"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
@@ -96,11 +95,6 @@ func (wal *baseWAL) Group() *auto.Group {
|
||||
return wal.group
|
||||
}
|
||||
|
||||
func (wal *baseWAL) SetLogger(l log.Logger) {
|
||||
wal.BaseService.Logger = l
|
||||
wal.group.SetLogger(l)
|
||||
}
|
||||
|
||||
func (wal *baseWAL) OnStart() error {
|
||||
size, err := wal.group.Head.Size()
|
||||
if err != nil {
|
||||
|
@@ -21,7 +21,7 @@ For more details on using Tendermint, see the respective documentation for
|
||||
|
||||
## Contribute
|
||||
|
||||
To contribute to the documentation, see [this file](https://github.com/tendermint/tendermint/blob/master/docs/DOCS_README.md) for details of the build process and
|
||||
To contribute to the documentation, see [this file](./DOCS_README.md) for details of the build process and
|
||||
considerations when making changes.
|
||||
|
||||
## Version
|
||||
|
@@ -47,6 +47,90 @@ The mempool and consensus logic act as clients, and each maintains an
|
||||
open ABCI connection with the application, which hosts an ABCI server.
|
||||
Shown are the request and response types sent on each connection.
|
||||
|
||||
## Message Protocol
|
||||
|
||||
The message protocol consists of pairs of requests and responses. Some
|
||||
messages have no fields, while others may include byte-arrays, strings,
|
||||
or integers. See the `message Request` and `message Response`
|
||||
definitions in [the protobuf definition
|
||||
file](https://github.com/tendermint/tendermint/blob/develop/abci/types/types.proto),
|
||||
and the [protobuf
|
||||
documentation](https://developers.google.com/protocol-buffers/docs/overview)
|
||||
for more details.
|
||||
|
||||
For each request, a server should respond with the corresponding
|
||||
response, where order of requests is preserved in the order of
|
||||
responses.
|
||||
|
||||
## Server
|
||||
|
||||
To use ABCI in your programming language of choice, there must be a ABCI
|
||||
server in that language. Tendermint supports two kinds of implementation
|
||||
of the server:
|
||||
|
||||
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
|
||||
known as TSP or Teaspoon)
|
||||
- GRPC
|
||||
|
||||
Both can be tested using the `abci-cli` by setting the `--abci` flag
|
||||
appropriately (ie. to `socket` or `grpc`).
|
||||
|
||||
See examples, in various stages of maintenance, in
|
||||
[Go](https://github.com/tendermint/tendermint/tree/develop/abci/server),
|
||||
[JavaScript](https://github.com/tendermint/js-abci),
|
||||
[Python](https://github.com/tendermint/tendermint/tree/develop/abci/example/python3/abci),
|
||||
[C++](https://github.com/mdyring/cpp-tmsp), and
|
||||
[Java](https://github.com/jTendermint/jabci).
|
||||
|
||||
### GRPC
|
||||
|
||||
If GRPC is available in your language, this is the easiest approach,
|
||||
though it will have significant performance overhead.
|
||||
|
||||
To get started with GRPC, copy in the [protobuf
|
||||
file](https://github.com/tendermint/tendermint/blob/develop/abci/types/types.proto)
|
||||
and compile it using the GRPC plugin for your language. For instance,
|
||||
for golang, the command is `protoc --go_out=plugins=grpc:. types.proto`.
|
||||
See the [grpc documentation for more details](http://www.grpc.io/docs/).
|
||||
`protoc` will autogenerate all the necessary code for ABCI client and
|
||||
server in your language, including whatever interface your application
|
||||
must satisfy to be used by the ABCI server for handling requests.
|
||||
|
||||
### TSP
|
||||
|
||||
If GRPC is not available in your language, or you require higher
|
||||
performance, or otherwise enjoy programming, you may implement your own
|
||||
ABCI server using the Tendermint Socket Protocol, known affectionately
|
||||
as Teaspoon. The first step is still to auto-generate the relevant data
|
||||
types and codec in your language using `protoc`. Messages coming over
|
||||
the socket are proto3 encoded, but additionally length-prefixed to
|
||||
facilitate use as a streaming protocol. proto3 doesn't have an
|
||||
official length-prefix standard, so we use our own. The first byte in
|
||||
the prefix represents the length of the Big Endian encoded length. The
|
||||
remaining bytes in the prefix are the Big Endian encoded length.
|
||||
|
||||
For example, if the proto3 encoded ABCI message is 0xDEADBEEF (4
|
||||
bytes), the length-prefixed message is 0x0104DEADBEEF. If the proto3
|
||||
encoded ABCI message is 65535 bytes long, the length-prefixed message
|
||||
would be like 0x02FFFF....
|
||||
|
||||
Note this prefixing does not apply for grpc.
|
||||
|
||||
An ABCI server must also be able to support multiple connections, as
|
||||
Tendermint uses three connections.
|
||||
|
||||
## Client
|
||||
|
||||
There are currently two use-cases for an ABCI client. One is a testing
|
||||
tool, as in the `abci-cli`, which allows ABCI requests to be sent via
|
||||
command line. The other is a consensus engine, such as Tendermint Core,
|
||||
which makes requests to the application every time a new transaction is
|
||||
received or a block is committed.
|
||||
|
||||
It is unlikely that you will need to implement a client. For details of
|
||||
our client, see
|
||||
[here](https://github.com/tendermint/tendermint/tree/develop/abci/client).
|
||||
|
||||
Most of the examples below are from [kvstore
|
||||
application](https://github.com/tendermint/tendermint/blob/develop/abci/example/kvstore/kvstore.go),
|
||||
which is a part of the abci repo. [persistent_kvstore
|
||||
|
@@ -175,5 +175,35 @@
|
||||
"language": "Javascript",
|
||||
"author": "Dennis McKinnon"
|
||||
}
|
||||
],
|
||||
"deploymentTools": [
|
||||
{
|
||||
"name": "mintnet-kubernetes",
|
||||
"url": "https://github.com/tendermint/tools",
|
||||
"technology": "Docker and Kubernetes",
|
||||
"author": "Tendermint",
|
||||
"description": "Deploy a Tendermint test network using Google's kubernetes"
|
||||
},
|
||||
{
|
||||
"name": "terraforce",
|
||||
"url": "https://github.com/tendermint/tools",
|
||||
"technology": "Terraform",
|
||||
"author": "Tendermint",
|
||||
"description": "Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones"
|
||||
},
|
||||
{
|
||||
"name": "ansible-tendermint",
|
||||
"url": "https://github.com/tendermint/tools",
|
||||
"technology": "Ansible",
|
||||
"author": "Tendermint",
|
||||
"description": "Ansible playbooks + Tendermint"
|
||||
},
|
||||
{
|
||||
"name": "brooklyn-tendermint",
|
||||
"url": "https://github.com/cloudsoft/brooklyn-tendermint",
|
||||
"technology": "Clocker for Apache Brooklyn ",
|
||||
"author": "Cloudsoft",
|
||||
"description": "Deploy a tendermint test network in docker containers "
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -9,3 +9,13 @@ We thank the community for their contributions thus far and welcome the
|
||||
addition of new projects. A pull request can be submitted to [this
|
||||
file](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/ecosystem.json)
|
||||
to include your project.
|
||||
|
||||
## Other Tools
|
||||
|
||||
See [deploy testnets](./deploy-testnets) for information about all
|
||||
the tools built by Tendermint. We have Kubernetes, Ansible, and
|
||||
Terraform integrations.
|
||||
|
||||
For upgrading from older to newer versions of tendermint and to migrate
|
||||
your chain data, see [tm-migrator](https://github.com/hxzqlh/tm-tools)
|
||||
written by @hxzqlh.
|
||||
|
@@ -1,72 +0,0 @@
|
||||
# ADR 034: PrivValidator file structure
|
||||
|
||||
## Changelog
|
||||
|
||||
03-11-2018: Initial Draft
|
||||
|
||||
## Context
|
||||
|
||||
For now, the PrivValidator file `priv_validator.json` contains mutable and immutable parts.
|
||||
Even in an insecure mode which does not encrypt private key on disk, it is reasonable to separate
|
||||
the mutable part and immutable part.
|
||||
|
||||
References:
|
||||
[#1181](https://github.com/tendermint/tendermint/issues/1181)
|
||||
[#2657](https://github.com/tendermint/tendermint/issues/2657)
|
||||
[#2313](https://github.com/tendermint/tendermint/issues/2313)
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
We can split mutable and immutable parts with two structs:
|
||||
```go
|
||||
// FilePVKey stores the immutable part of PrivValidator
|
||||
type FilePVKey struct {
|
||||
Address types.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
|
||||
filePath string
|
||||
}
|
||||
|
||||
// FilePVState stores the mutable part of PrivValidator
|
||||
type FilePVLastSignState struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Step int8 `json:"step"`
|
||||
Signature []byte `json:"signature,omitempty"`
|
||||
SignBytes cmn.HexBytes `json:"signbytes,omitempty"`
|
||||
|
||||
filePath string
|
||||
mtx sync.Mutex
|
||||
}
|
||||
```
|
||||
|
||||
Then we can combine `FilePVKey` with `FilePVLastSignState` and will get the original `FilePV`.
|
||||
|
||||
```go
|
||||
type FilePV struct {
|
||||
Key FilePVKey
|
||||
LastSignState FilePVLastSignState
|
||||
}
|
||||
```
|
||||
|
||||
As discussed, `FilePV` should be located in `config`, and `FilePVLastSignState` should be stored in `data`. The
|
||||
store path of each file should be specified in `config.yml`.
|
||||
|
||||
What we need to do next is changing the methods of `FilePV`.
|
||||
|
||||
## Status
|
||||
|
||||
Draft.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- separate the mutable and immutable of PrivValidator
|
||||
|
||||
### Negative
|
||||
|
||||
- need to add more config for file path
|
||||
|
||||
### Neutral
|
@@ -1,40 +0,0 @@
|
||||
# ADR 035: Documentation
|
||||
|
||||
Author: @zramsay (Zach Ramsay)
|
||||
|
||||
## Changelog
|
||||
|
||||
### November 2nd 2018
|
||||
|
||||
- initial write-up
|
||||
|
||||
## Context
|
||||
|
||||
The Tendermint documentation has undergone several changes until settling on the current model. Originally, the documentation was hosted on the website and had to be updated asynchronously from the code. Along with the other repositories requiring documentation, the whole stack moved to using Read The Docs to automatically generate, publish, and host the documentation. This, however, was insufficient; the RTD site had advertisement, it wasn't easily accessible to devs, didn't collect metrics, was another set of external links, etc.
|
||||
|
||||
## Decision
|
||||
|
||||
For two reasons, the decision was made to use VuePress:
|
||||
|
||||
1) ability to get metrics (implemented on both Tendermint and SDK)
|
||||
2) host the documentation on the website as a `/docs` endpoint.
|
||||
|
||||
This is done while maintaining synchrony between the docs and code, i.e., the website is built whenever the docs are updated.
|
||||
|
||||
## Status
|
||||
|
||||
The two points above have been implemented; the `config.js` has a Google Analytics identifier and the documentation workflow has been up and running largely without problems for several months. Details about the documentation build & workflow can be found [here](../DOCS_README.md)
|
||||
|
||||
## Consequences
|
||||
|
||||
Because of the organizational seperation between Tendermint & Cosmos, there is a challenge of "what goes where" for certain aspects of documentation.
|
||||
|
||||
### Positive
|
||||
|
||||
This architecture is largely positive relative to prior docs arrangements.
|
||||
|
||||
### Negative
|
||||
|
||||
A significant portion of the docs automation / build process is in private repos with limited access/visibility to devs. However, these tasks are handled by the SRE team.
|
||||
|
||||
### Neutral
|
@@ -1,36 +1,19 @@
|
||||
# ADR {ADR-NUMBER}: {TITLE}
|
||||
# ADR 000: Template for an ADR
|
||||
|
||||
Author:
|
||||
|
||||
## Changelog
|
||||
* {date}: {changelog}
|
||||
|
||||
## Context
|
||||
|
||||
> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution.
|
||||
## Decision
|
||||
|
||||
> This section explains all of the details of the proposed solution, including implementation details.
|
||||
It should also describe affects / corollary items that may need to be changed as a part of this.
|
||||
If the proposed change will be large, please also indicate a way to do the change to maximize ease of review.
|
||||
(e.g. the optimal split of things to do between separate PR's)
|
||||
|
||||
## Status
|
||||
|
||||
> A decision may be "proposed" if it hasn't been agreed upon yet, or "accepted" once it is agreed upon. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement.
|
||||
|
||||
{Deprecated|Proposed|Accepted}
|
||||
|
||||
## Consequences
|
||||
|
||||
> This section describes the consequences, after applying the decision. All consequences should be summarized here, not just the "positive" ones.
|
||||
|
||||
### Positive
|
||||
|
||||
### Negative
|
||||
|
||||
### Neutral
|
||||
|
||||
## References
|
||||
|
||||
> Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here!
|
||||
|
||||
* {reference link}
|
||||
|
@@ -3,8 +3,12 @@
|
||||
This section is for those looking to implement their own ABCI Server, perhaps in
|
||||
a new programming language.
|
||||
|
||||
You are expected to have read [ABCI Methods and Types](./abci.md) and [ABCI
|
||||
Applications](./apps.md).
|
||||
You are expected to have read [ABCI Methods and Types](abci.md) and [ABCI
|
||||
Applications](apps.md).
|
||||
|
||||
See additional details in the [ABCI
|
||||
readme](https://github.com/tendermint/tendermint/blob/develop/abci/README.md)(TODO: deduplicate
|
||||
those details).
|
||||
|
||||
## Message Protocol
|
||||
|
||||
@@ -20,16 +24,17 @@ For each request, a server should respond with the corresponding
|
||||
response, where the order of requests is preserved in the order of
|
||||
responses.
|
||||
|
||||
## Server Implementations
|
||||
## Server
|
||||
|
||||
To use ABCI in your programming language of choice, there must be a ABCI
|
||||
server in that language. Tendermint supports three implementations of the ABCI, written in Go:
|
||||
server in that language. Tendermint supports two kinds of implementation
|
||||
of the server:
|
||||
|
||||
- In-process (Golang only)
|
||||
- ABCI-socket
|
||||
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
|
||||
known as TSP or Teaspoon)
|
||||
- GRPC
|
||||
|
||||
The latter two can be tested using the `abci-cli` by setting the `--abci` flag
|
||||
Both can be tested using the `abci-cli` by setting the `--abci` flag
|
||||
appropriately (ie. to `socket` or `grpc`).
|
||||
|
||||
See examples, in various stages of maintenance, in
|
||||
@@ -39,12 +44,6 @@ See examples, in various stages of maintenance, in
|
||||
[C++](https://github.com/mdyring/cpp-tmsp), and
|
||||
[Java](https://github.com/jTendermint/jabci).
|
||||
|
||||
### In Process
|
||||
|
||||
The simplest implementation uses function calls within Golang.
|
||||
This means ABCI applications written in Golang can be compiled with TendermintCore and run as a single binary.
|
||||
|
||||
|
||||
### GRPC
|
||||
|
||||
If GRPC is available in your language, this is the easiest approach,
|
||||
@@ -59,18 +58,15 @@ See the [grpc documentation for more details](http://www.grpc.io/docs/).
|
||||
server in your language, including whatever interface your application
|
||||
must satisfy to be used by the ABCI server for handling requests.
|
||||
|
||||
Note the length-prefixing used in the socket implementation (TSP) does not apply for GRPC.
|
||||
|
||||
### TSP
|
||||
|
||||
Tendermint Socket Protocol is an asynchronous, raw socket server which provides ordered message passing over unix or tcp.
|
||||
Messages are serialized using Protobuf3 and length-prefixed with a [signed Varint](https://developers.google.com/protocol-buffers/docs/encoding?csw=1#signed-integers)
|
||||
|
||||
If GRPC is not available in your language, or you require higher
|
||||
performance, or otherwise enjoy programming, you may implement your own
|
||||
ABCI server using the Tendermint Socket Protocol. The first step is still to auto-generate the relevant data
|
||||
types and codec in your language using `protoc`. In addition to being proto3 encoded, messages coming over
|
||||
the socket are length-prefixed to facilitate use as a streaming protocol. proto3 doesn't have an
|
||||
ABCI server using the Tendermint Socket Protocol, known affectionately
|
||||
as Teaspoon. The first step is still to auto-generate the relevant data
|
||||
types and codec in your language using `protoc`. Messages coming over
|
||||
the socket are proto3 encoded, but additionally length-prefixed to
|
||||
facilitate use as a streaming protocol. proto3 doesn't have an
|
||||
official length-prefix standard, so we use our own. The first byte in
|
||||
the prefix represents the length of the Big Endian encoded length. The
|
||||
remaining bytes in the prefix are the Big Endian encoded length.
|
||||
@@ -80,14 +76,12 @@ bytes), the length-prefixed message is 0x0104DEADBEEF. If the proto3
|
||||
encoded ABCI message is 65535 bytes long, the length-prefixed message
|
||||
would be like 0x02FFFF....
|
||||
|
||||
The benefit of using this `varint` encoding over the old version (where integers were encoded as `<len of len><big endian len>` is that
|
||||
it is the standard way to encode integers in Protobuf. It is also generally shorter.
|
||||
|
||||
As noted above, this prefixing does not apply for GRPC.
|
||||
Note this prefixing does not apply for grpc.
|
||||
|
||||
An ABCI server must also be able to support multiple connections, as
|
||||
Tendermint uses three connections.
|
||||
|
||||
|
||||
### Async vs Sync
|
||||
|
||||
The main ABCI server (ie. non-GRPC) provides ordered asynchronous messages.
|
||||
|
@@ -79,24 +79,30 @@ func TotalVotingPower(vals []Validators) int64{
|
||||
ConsensusParams define various limits for blockchain data structures.
|
||||
Like validator sets, they are set during genesis and can be updated by the application through ABCI.
|
||||
|
||||
```go
|
||||
```
|
||||
type ConsensusParams struct {
|
||||
BlockSize
|
||||
Evidence
|
||||
Validator
|
||||
TxSize
|
||||
BlockGossip
|
||||
EvidenceParams
|
||||
}
|
||||
|
||||
type BlockSize struct {
|
||||
MaxBytes int64
|
||||
MaxBytes int
|
||||
MaxGas int64
|
||||
}
|
||||
|
||||
type Evidence struct {
|
||||
MaxAge int64
|
||||
type TxSize struct {
|
||||
MaxBytes int
|
||||
MaxGas int64
|
||||
}
|
||||
|
||||
type Validator struct {
|
||||
PubKeyTypes []string
|
||||
type BlockGossip struct {
|
||||
BlockPartSizeBytes int
|
||||
}
|
||||
|
||||
type EvidenceParams struct {
|
||||
MaxAge int64
|
||||
}
|
||||
```
|
||||
|
||||
@@ -109,15 +115,20 @@ otherwise.
|
||||
Blocks should additionally be limited by the amount of "gas" consumed by the
|
||||
transactions in the block, though this is not yet implemented.
|
||||
|
||||
#### Evidence
|
||||
#### TxSize
|
||||
|
||||
These parameters are not yet enforced and may disappear. See [issue
|
||||
#2347](https://github.com/tendermint/tendermint/issues/2347).
|
||||
|
||||
#### BlockGossip
|
||||
|
||||
When gossipping blocks in the consensus, they are first split into parts. The
|
||||
size of each part is `ConsensusParams.BlockGossip.BlockPartSizeBytes`.
|
||||
|
||||
#### EvidenceParams
|
||||
|
||||
For evidence in a block to be valid, it must satisfy:
|
||||
|
||||
```
|
||||
block.Header.Height - evidence.Height < ConsensusParams.Evidence.MaxAge
|
||||
block.Header.Height - evidence.Height < ConsensusParams.EvidenceParams.MaxAge
|
||||
```
|
||||
|
||||
#### Validator
|
||||
|
||||
Validators from genesis file and `ResponseEndBlock` must have pubkeys of type ∈
|
||||
`ConsensusParams.Validator.PubKeyTypes`.
|
||||
|
@@ -39,9 +39,6 @@ db_dir = "data"
|
||||
# Output level for logging
|
||||
log_level = "state:info,*:error"
|
||||
|
||||
# Output format: 'plain' (colored text) or 'json'
|
||||
log_format = "plain"
|
||||
|
||||
##### additional base config options #####
|
||||
|
||||
# The ID of the chain to join (should be signed with every transaction and vote)
|
||||
@@ -71,17 +68,6 @@ filter_peers = false
|
||||
# TCP or UNIX socket address for the RPC server to listen on
|
||||
laddr = "tcp://0.0.0.0:26657"
|
||||
|
||||
# A list of origins a cross-domain request can be executed from
|
||||
# Default value '[]' disables cors support
|
||||
# Use '["*"]' to allow any origin
|
||||
cors_allowed_origins = "[]"
|
||||
|
||||
# A list of methods the client is allowed to use with cross-domain requests
|
||||
cors_allowed_methods = "[HEAD GET POST]"
|
||||
|
||||
# A list of non simple headers the client is allowed to use with cross-domain requests
|
||||
cors_allowed_headers = "[Origin Accept Content-Type X-Requested-With X-Server-Time]"
|
||||
|
||||
# TCP or UNIX socket address for the gRPC server to listen on
|
||||
# NOTE: This server only supports /broadcast_tx_commit
|
||||
grpc_laddr = ""
|
||||
|
@@ -60,34 +60,42 @@ definition](https://github.com/tendermint/tendermint/blob/master/types/genesis.g
|
||||
|
||||
```
|
||||
{
|
||||
"genesis_time": "2018-11-13T18:11:50.277637Z",
|
||||
"chain_id": "test-chain-s4ui7D",
|
||||
"consensus_params": {
|
||||
"block_size": {
|
||||
"max_bytes": "22020096",
|
||||
"max_gas": "-1"
|
||||
},
|
||||
"evidence": {
|
||||
"max_age": "100000"
|
||||
},
|
||||
"validator": {
|
||||
"pub_key_types": [
|
||||
"ed25519"
|
||||
]
|
||||
}
|
||||
},
|
||||
"genesis_time": "2018-07-09T22:43:06.255718641Z",
|
||||
"chain_id": "chain-IAkWsK",
|
||||
"validators": [
|
||||
{
|
||||
"address": "39C04A480B54AB258A45355A5E48ADDED9956C65",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "DMEMMj1+thrkUCGocbvvKzXeaAtRslvX9MWtB+smuIA="
|
||||
"value": "oX8HhKsErMluxI0QWNSR8djQMSupDvHdAYrHwP7n73k="
|
||||
},
|
||||
"power": "10",
|
||||
"name": ""
|
||||
"power": "1",
|
||||
"name": "node0"
|
||||
},
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "UZNSJA9zmeFQj36Rs296lY+WFQ4Rt6s7snPpuKypl5I="
|
||||
},
|
||||
"power": "1",
|
||||
"name": "node1"
|
||||
},
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "i9GrM6/MHB4zjCelMZBUYHNXYIzl4n0RkDCVmmLhS/o="
|
||||
},
|
||||
"power": "1",
|
||||
"name": "node2"
|
||||
},
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "0qq7954l87trEqbQV9c7d1gurnjTGMxreXc848ZZ5aw="
|
||||
},
|
||||
"power": "1",
|
||||
"name": "node3"
|
||||
}
|
||||
],
|
||||
"app_hash": ""
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -160,15 +160,12 @@ func (evR *EvidenceReactor) broadcastEvidenceRoutine(peer p2p.Peer) {
|
||||
// Returns the message to send the peer, or nil if the evidence is invalid for the peer.
|
||||
// If message is nil, return true if we should sleep and try again.
|
||||
func (evR EvidenceReactor) checkSendEvidenceMessage(peer p2p.Peer, ev types.Evidence) (msg EvidenceMessage, retry bool) {
|
||||
|
||||
// make sure the peer is up to date
|
||||
evHeight := ev.Height()
|
||||
peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
|
||||
if !ok {
|
||||
// Peer does not have a state yet. We set it in the consensus reactor, but
|
||||
// when we add peer in Switch, the order we call reactors#AddPeer is
|
||||
// different every time due to us using a map. Sometimes other reactors
|
||||
// will be initialized before the consensus reactor. We should wait a few
|
||||
// milliseconds and retry.
|
||||
if !ok {
|
||||
evR.Logger.Info("Found peer without PeerState", "peer", peer)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
@@ -165,16 +164,6 @@ func TestReactorSelectiveBroadcast(t *testing.T) {
|
||||
|
||||
// make reactors from statedb
|
||||
reactors := makeAndConnectEvidenceReactors(config, []dbm.DB{stateDB1, stateDB2})
|
||||
|
||||
// set the peer height on each reactor
|
||||
for _, r := range reactors {
|
||||
for _, peer := range r.Switch.Peers().List() {
|
||||
ps := peerState{height1}
|
||||
peer.Set(types.PeerStateKey, ps)
|
||||
}
|
||||
}
|
||||
|
||||
// update the first reactor peer's height to be very small
|
||||
peer := reactors[0].Switch.Peers().List()[0]
|
||||
ps := peerState{height2}
|
||||
peer.Set(types.PeerStateKey, ps)
|
||||
@@ -189,30 +178,3 @@ func TestReactorSelectiveBroadcast(t *testing.T) {
|
||||
peers := reactors[1].Switch.Peers().List()
|
||||
assert.Equal(t, 1, len(peers))
|
||||
}
|
||||
func TestEvidenceListMessageValidationBasic(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateEvListMsg func(*EvidenceListMessage)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good EvidenceListMessage", func(evList *EvidenceListMessage) {}, false},
|
||||
{"Invalid EvidenceListMessage", func(evList *EvidenceListMessage) {
|
||||
evList.Evidence = append(evList.Evidence,
|
||||
&types.DuplicateVoteEvidence{PubKey: secp256k1.GenPrivKey().PubKey()})
|
||||
}, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
evListMsg := &EvidenceListMessage{}
|
||||
n := 3
|
||||
valAddr := []byte("myval")
|
||||
evListMsg.Evidence = make([]types.Evidence, n)
|
||||
for i := 0; i < n; i++ {
|
||||
evListMsg.Evidence[i] = types.NewMockGoodEvidence(int64(i+1), 0, valAddr)
|
||||
}
|
||||
tc.malleateEvListMsg(evListMsg)
|
||||
assert.Equal(t, tc.expectErr, evListMsg.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/errors"
|
||||
)
|
||||
|
||||
/* AutoFile usage
|
||||
@@ -82,8 +83,6 @@ func OpenAutoFile(path string) (*AutoFile, error) {
|
||||
return af, nil
|
||||
}
|
||||
|
||||
// Close shuts down the closing goroutine, SIGHUP handler and closes the
|
||||
// AutoFile.
|
||||
func (af *AutoFile) Close() error {
|
||||
af.closeTicker.Stop()
|
||||
close(af.closeTickerStopc)
|
||||
@@ -117,10 +116,6 @@ func (af *AutoFile) closeFile() (err error) {
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the AutoFile. It returns the number of bytes
|
||||
// written and an error, if any. Write returns a non-nil error when n !=
|
||||
// len(b).
|
||||
// Opens AutoFile if needed.
|
||||
func (af *AutoFile) Write(b []byte) (n int, err error) {
|
||||
af.mtx.Lock()
|
||||
defer af.mtx.Unlock()
|
||||
@@ -135,10 +130,6 @@ func (af *AutoFile) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sync commits the current contents of the file to stable storage. Typically,
|
||||
// this means flushing the file system's in-memory copy of recently written
|
||||
// data to disk.
|
||||
// Opens AutoFile if needed.
|
||||
func (af *AutoFile) Sync() error {
|
||||
af.mtx.Lock()
|
||||
defer af.mtx.Unlock()
|
||||
@@ -156,33 +147,34 @@ func (af *AutoFile) openFile() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// fileInfo, err := file.Stat()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if fileInfo.Mode() != autoFilePerms {
|
||||
// return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
|
||||
// }
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fileInfo.Mode() != autoFilePerms {
|
||||
return errors.NewErrPermissionsChanged(file.Name(), fileInfo.Mode(), autoFilePerms)
|
||||
}
|
||||
af.file = file
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns the size of the AutoFile. It returns -1 and an error if fails
|
||||
// get stats or open file.
|
||||
// Opens AutoFile if needed.
|
||||
func (af *AutoFile) Size() (int64, error) {
|
||||
af.mtx.Lock()
|
||||
defer af.mtx.Unlock()
|
||||
|
||||
if af.file == nil {
|
||||
if err := af.openFile(); err != nil {
|
||||
err := af.openFile()
|
||||
if err != nil {
|
||||
if err == os.ErrNotExist {
|
||||
return 0, nil
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
stat, err := af.file.Stat()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return stat.Size(), nil
|
||||
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/errors"
|
||||
)
|
||||
|
||||
func TestSIGHUP(t *testing.T) {
|
||||
@@ -57,66 +58,29 @@ func TestSIGHUP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// // Manually modify file permissions, close, and reopen using autofile:
|
||||
// // We expect the file permissions to be changed back to the intended perms.
|
||||
// func TestOpenAutoFilePerms(t *testing.T) {
|
||||
// file, err := ioutil.TempFile("", "permission_test")
|
||||
// require.NoError(t, err)
|
||||
// err = file.Close()
|
||||
// require.NoError(t, err)
|
||||
// name := file.Name()
|
||||
|
||||
// // open and change permissions
|
||||
// af, err := OpenAutoFile(name)
|
||||
// require.NoError(t, err)
|
||||
// err = af.file.Chmod(0755)
|
||||
// require.NoError(t, err)
|
||||
// err = af.Close()
|
||||
// require.NoError(t, err)
|
||||
|
||||
// // reopen and expect an ErrPermissionsChanged as Cause
|
||||
// af, err = OpenAutoFile(name)
|
||||
// require.Error(t, err)
|
||||
// if e, ok := err.(*errors.ErrPermissionsChanged); ok {
|
||||
// t.Logf("%v", e)
|
||||
// } else {
|
||||
// t.Errorf("unexpected error %v", e)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestAutoFileSize(t *testing.T) {
|
||||
// First, create an AutoFile writing to a tempfile dir
|
||||
f, err := ioutil.TempFile("", "sighup_test")
|
||||
// Manually modify file permissions, close, and reopen using autofile:
|
||||
// We expect the file permissions to be changed back to the intended perms.
|
||||
func TestOpenAutoFilePerms(t *testing.T) {
|
||||
file, err := ioutil.TempFile("", "permission_test")
|
||||
require.NoError(t, err)
|
||||
err = f.Close()
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
name := file.Name()
|
||||
|
||||
// Here is the actual AutoFile.
|
||||
af, err := OpenAutoFile(f.Name())
|
||||
// open and change permissions
|
||||
af, err := OpenAutoFile(name)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 1. Empty file
|
||||
size, err := af.Size()
|
||||
require.Zero(t, size)
|
||||
err = af.file.Chmod(0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 2. Not empty file
|
||||
data := []byte("Maniac\n")
|
||||
_, err = af.Write(data)
|
||||
require.NoError(t, err)
|
||||
size, err = af.Size()
|
||||
require.EqualValues(t, len(data), size)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 3. Not existing file
|
||||
err = af.Close()
|
||||
require.NoError(t, err)
|
||||
err = os.Remove(f.Name())
|
||||
require.NoError(t, err)
|
||||
size, err = af.Size()
|
||||
require.EqualValues(t, 0, size, "Expected a new file to be empty")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cleanup
|
||||
_ = os.Remove(f.Name())
|
||||
}
|
||||
// reopen and expect an ErrPermissionsChanged as Cause
|
||||
af, err = OpenAutoFile(name)
|
||||
require.Error(t, err)
|
||||
if e, ok := err.(*errors.ErrPermissionsChanged); ok {
|
||||
t.Logf("%v", e)
|
||||
} else {
|
||||
t.Errorf("unexpected error %v", e)
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -230,8 +231,7 @@ func (g *Group) checkHeadSizeLimit() {
|
||||
}
|
||||
size, err := g.Head.Size()
|
||||
if err != nil {
|
||||
g.Logger.Error("Group's head may grow without bound", "head", g.Head.Path, "err", err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
if size >= limit {
|
||||
g.RotateFile()
|
||||
@@ -253,21 +253,21 @@ func (g *Group) checkTotalSizeLimit() {
|
||||
}
|
||||
if index == gInfo.MaxIndex {
|
||||
// Special degenerate case, just do nothing.
|
||||
g.Logger.Error("Group's head may grow without bound", "head", g.Head.Path)
|
||||
log.Println("WARNING: Group's head " + g.Head.Path + "may grow without bound")
|
||||
return
|
||||
}
|
||||
pathToRemove := filePathForIndex(g.Head.Path, index, gInfo.MaxIndex)
|
||||
fInfo, err := os.Stat(pathToRemove)
|
||||
fileInfo, err := os.Stat(pathToRemove)
|
||||
if err != nil {
|
||||
g.Logger.Error("Failed to fetch info for file", "file", pathToRemove)
|
||||
log.Println("WARNING: Failed to fetch info for file @" + pathToRemove)
|
||||
continue
|
||||
}
|
||||
err = os.Remove(pathToRemove)
|
||||
if err != nil {
|
||||
g.Logger.Error("Failed to remove path", "path", pathToRemove)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
totalSize -= fInfo.Size()
|
||||
totalSize -= fileInfo.Size()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmerrors "github.com/tendermint/tendermint/libs/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -206,13 +207,13 @@ func write(path string, d []byte) error {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
// fInfo, err := f.Stat()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if fInfo.Mode() != keyPerm {
|
||||
// return tmerrors.NewErrPermissionsChanged(f.Name(), keyPerm, fInfo.Mode())
|
||||
// }
|
||||
fInfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fInfo.Mode() != keyPerm {
|
||||
return tmerrors.NewErrPermissionsChanged(f.Name(), keyPerm, fInfo.Mode())
|
||||
}
|
||||
_, err = f.Write(d)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -1,21 +1,26 @@
|
||||
// Package errors contains errors that are thrown across packages.
|
||||
package errors
|
||||
|
||||
// // ErrPermissionsChanged occurs if the file permission have changed since the file was created.
|
||||
// type ErrPermissionsChanged struct {
|
||||
// name string
|
||||
// got, want os.FileMode
|
||||
// }
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// func NewErrPermissionsChanged(name string, got, want os.FileMode) *ErrPermissionsChanged {
|
||||
// return &ErrPermissionsChanged{name: name, got: got, want: want}
|
||||
// }
|
||||
// ErrPermissionsChanged occurs if the file permission have changed since the file was created.
|
||||
type ErrPermissionsChanged struct {
|
||||
name string
|
||||
got, want os.FileMode
|
||||
}
|
||||
|
||||
// func (e ErrPermissionsChanged) Error() string {
|
||||
// return fmt.Sprintf(
|
||||
// "file: [%v]\nexpected file permissions: %v, got: %v",
|
||||
// e.name,
|
||||
// e.want,
|
||||
// e.got,
|
||||
// )
|
||||
// }
|
||||
func NewErrPermissionsChanged(name string, got, want os.FileMode) *ErrPermissionsChanged {
|
||||
return &ErrPermissionsChanged{name: name, got: got, want: want}
|
||||
}
|
||||
|
||||
func (e ErrPermissionsChanged) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"file: [%v]\nexpected file permissions: %v, got: %v",
|
||||
e.name,
|
||||
e.want,
|
||||
e.got,
|
||||
)
|
||||
}
|
||||
|
@@ -30,15 +30,9 @@
|
||||
//
|
||||
// s.Subscribe(ctx, sub, qry, out)
|
||||
// defer func() {
|
||||
// // drain out to make sure we don't block
|
||||
// LOOP:
|
||||
// for {
|
||||
// select {
|
||||
// case <-out:
|
||||
// default:
|
||||
// break LOOP
|
||||
// }
|
||||
// }
|
||||
// for range out {
|
||||
// // drain out to make sure we don't block
|
||||
// }
|
||||
// s.UnsubscribeAll(ctx, sub)
|
||||
// }()
|
||||
// for msg := range out {
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
"github.com/tendermint/tendermint/rpc/core"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
rpc "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,7 +19,6 @@ const (
|
||||
// StartProxy will start the websocket manager on the client,
|
||||
// set up the rpc routes to proxy via the given client,
|
||||
// and start up an http/rpc server on the location given by bind (eg. :1234)
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpenConnections int) error {
|
||||
err := c.Start()
|
||||
if err != nil {
|
||||
@@ -32,49 +31,47 @@ func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpe
|
||||
|
||||
// build the handler...
|
||||
mux := http.NewServeMux()
|
||||
rpcserver.RegisterRPCFuncs(mux, r, cdc, logger)
|
||||
rpc.RegisterRPCFuncs(mux, r, cdc, logger)
|
||||
|
||||
wm := rpcserver.NewWebsocketManager(r, cdc, rpcserver.EventSubscriber(c))
|
||||
wm := rpc.NewWebsocketManager(r, cdc, rpc.EventSubscriber(c))
|
||||
wm.SetLogger(logger)
|
||||
core.SetLogger(logger)
|
||||
mux.HandleFunc(wsEndpoint, wm.WebsocketHandler)
|
||||
|
||||
l, err := rpcserver.Listen(listenAddr, rpcserver.Config{MaxOpenConnections: maxOpenConnections})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rpcserver.StartHTTPServer(l, mux, logger)
|
||||
_, err = rpc.StartHTTPServer(listenAddr, mux, logger, rpc.Config{MaxOpenConnections: maxOpenConnections})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// RPCRoutes just routes everything to the given client, as if it were
|
||||
// a tendermint fullnode.
|
||||
//
|
||||
// if we want security, the client must implement it as a secure client
|
||||
func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc {
|
||||
func RPCRoutes(c rpcclient.Client) map[string]*rpc.RPCFunc {
|
||||
|
||||
return map[string]*rpcserver.RPCFunc{
|
||||
return map[string]*rpc.RPCFunc{
|
||||
// Subscribe/unsubscribe are reserved for websocket events.
|
||||
// We can just use the core tendermint impl, which uses the
|
||||
// EventSwitch we registered in NewWebsocketManager above
|
||||
"subscribe": rpcserver.NewWSRPCFunc(core.Subscribe, "query"),
|
||||
"unsubscribe": rpcserver.NewWSRPCFunc(core.Unsubscribe, "query"),
|
||||
"subscribe": rpc.NewWSRPCFunc(core.Subscribe, "query"),
|
||||
"unsubscribe": rpc.NewWSRPCFunc(core.Unsubscribe, "query"),
|
||||
|
||||
// info API
|
||||
"status": rpcserver.NewRPCFunc(c.Status, ""),
|
||||
"blockchain": rpcserver.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
|
||||
"genesis": rpcserver.NewRPCFunc(c.Genesis, ""),
|
||||
"block": rpcserver.NewRPCFunc(c.Block, "height"),
|
||||
"commit": rpcserver.NewRPCFunc(c.Commit, "height"),
|
||||
"tx": rpcserver.NewRPCFunc(c.Tx, "hash,prove"),
|
||||
"validators": rpcserver.NewRPCFunc(c.Validators, ""),
|
||||
"status": rpc.NewRPCFunc(c.Status, ""),
|
||||
"blockchain": rpc.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"),
|
||||
"genesis": rpc.NewRPCFunc(c.Genesis, ""),
|
||||
"block": rpc.NewRPCFunc(c.Block, "height"),
|
||||
"commit": rpc.NewRPCFunc(c.Commit, "height"),
|
||||
"tx": rpc.NewRPCFunc(c.Tx, "hash,prove"),
|
||||
"validators": rpc.NewRPCFunc(c.Validators, ""),
|
||||
|
||||
// broadcast API
|
||||
"broadcast_tx_commit": rpcserver.NewRPCFunc(c.BroadcastTxCommit, "tx"),
|
||||
"broadcast_tx_sync": rpcserver.NewRPCFunc(c.BroadcastTxSync, "tx"),
|
||||
"broadcast_tx_async": rpcserver.NewRPCFunc(c.BroadcastTxAsync, "tx"),
|
||||
"broadcast_tx_commit": rpc.NewRPCFunc(c.BroadcastTxCommit, "tx"),
|
||||
"broadcast_tx_sync": rpc.NewRPCFunc(c.BroadcastTxSync, "tx"),
|
||||
"broadcast_tx_async": rpc.NewRPCFunc(c.BroadcastTxAsync, "tx"),
|
||||
|
||||
// abci API
|
||||
"abci_query": rpcserver.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
|
||||
"abci_info": rpcserver.NewRPCFunc(c.ABCIInfo, ""),
|
||||
"abci_query": rpc.NewRPCFunc(c.ABCIQuery, "path,data,prove"),
|
||||
"abci_info": rpc.NewRPCFunc(c.ABCIInfo, ""),
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
auto "github.com/tendermint/tendermint/libs/autofile"
|
||||
@@ -87,12 +88,8 @@ func IsPreCheckError(err error) bool {
|
||||
func PreCheckAminoMaxBytes(maxBytes int64) PreCheckFunc {
|
||||
return func(tx types.Tx) error {
|
||||
// We have to account for the amino overhead in the tx size as well
|
||||
// NOTE: fieldNum = 1 as types.Block.Data contains Txs []Tx as first field.
|
||||
// If this field order ever changes this needs to updated here accordingly.
|
||||
// NOTE: if some []Tx are encoded without a parenting struct, the
|
||||
// fieldNum is also equal to 1.
|
||||
aminoOverhead := types.ComputeAminoOverhead(tx, 1)
|
||||
txSize := int64(len(tx)) + aminoOverhead
|
||||
aminoOverhead := amino.UvarintSize(uint64(len(tx)))
|
||||
txSize := int64(len(tx) + aminoOverhead)
|
||||
if txSize > maxBytes {
|
||||
return fmt.Errorf("Tx size (including amino overhead) is too big: %d, max: %d",
|
||||
txSize, maxBytes)
|
||||
@@ -300,7 +297,6 @@ func (mem *Mempool) TxsWaitChan() <-chan struct{} {
|
||||
// CONTRACT: Either cb will get called, or err returned.
|
||||
func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) {
|
||||
mem.proxyMtx.Lock()
|
||||
// use defer to unlock mutex because application (*local client*) might panic
|
||||
defer mem.proxyMtx.Unlock()
|
||||
|
||||
if mem.Size() >= mem.config.Size {
|
||||
@@ -486,7 +482,7 @@ func (mem *Mempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
|
||||
for e := mem.txs.Front(); e != nil; e = e.Next() {
|
||||
memTx := e.Value.(*mempoolTx)
|
||||
// Check total size requirement
|
||||
aminoOverhead := types.ComputeAminoOverhead(memTx.tx, 1)
|
||||
aminoOverhead := int64(amino.UvarintSize(uint64(len(memTx.tx))))
|
||||
if maxBytes > -1 && totalBytes+int64(len(memTx.tx))+aminoOverhead > maxBytes {
|
||||
return txs
|
||||
}
|
||||
|
@@ -107,11 +107,11 @@ func TestReapMaxBytesMaxGas(t *testing.T) {
|
||||
{20, 0, -1, 0},
|
||||
{20, 0, 10, 0},
|
||||
{20, 10, 10, 0},
|
||||
{20, 22, 10, 1},
|
||||
{20, 220, -1, 10},
|
||||
{20, 220, 5, 5},
|
||||
{20, 220, 10, 10},
|
||||
{20, 220, 15, 10},
|
||||
{20, 21, 10, 1},
|
||||
{20, 210, -1, 10},
|
||||
{20, 210, 5, 5},
|
||||
{20, 210, 10, 10},
|
||||
{20, 210, 15, 10},
|
||||
{20, 20000, -1, 20},
|
||||
{20, 20000, 5, 5},
|
||||
{20, 20000, 30, 20},
|
||||
@@ -145,15 +145,15 @@ func TestMempoolFilters(t *testing.T) {
|
||||
{10, nopPreFilter, nopPostFilter, 10},
|
||||
{10, PreCheckAminoMaxBytes(10), nopPostFilter, 0},
|
||||
{10, PreCheckAminoMaxBytes(20), nopPostFilter, 0},
|
||||
{10, PreCheckAminoMaxBytes(22), nopPostFilter, 10},
|
||||
{10, PreCheckAminoMaxBytes(21), nopPostFilter, 10},
|
||||
{10, nopPreFilter, PostCheckMaxGas(-1), 10},
|
||||
{10, nopPreFilter, PostCheckMaxGas(0), 0},
|
||||
{10, nopPreFilter, PostCheckMaxGas(1), 10},
|
||||
{10, nopPreFilter, PostCheckMaxGas(3000), 10},
|
||||
{10, PreCheckAminoMaxBytes(10), PostCheckMaxGas(20), 0},
|
||||
{10, PreCheckAminoMaxBytes(30), PostCheckMaxGas(20), 10},
|
||||
{10, PreCheckAminoMaxBytes(22), PostCheckMaxGas(1), 10},
|
||||
{10, PreCheckAminoMaxBytes(22), PostCheckMaxGas(0), 0},
|
||||
{10, PreCheckAminoMaxBytes(21), PostCheckMaxGas(1), 10},
|
||||
{10, PreCheckAminoMaxBytes(21), PostCheckMaxGas(0), 0},
|
||||
}
|
||||
for tcIndex, tt := range tests {
|
||||
mempool.Update(1, emptyTxArr, tt.preFilter, tt.postFilter)
|
||||
|
@@ -133,23 +133,16 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) {
|
||||
}
|
||||
|
||||
memTx := next.Value.(*mempoolTx)
|
||||
|
||||
// make sure the peer is up to date
|
||||
peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
|
||||
if !ok {
|
||||
// Peer does not have a state yet. We set it in the consensus reactor, but
|
||||
// when we add peer in Switch, the order we call reactors#AddPeer is
|
||||
// different every time due to us using a map. Sometimes other reactors
|
||||
// will be initialized before the consensus reactor. We should wait a few
|
||||
// milliseconds and retry.
|
||||
time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond)
|
||||
continue
|
||||
height := memTx.Height()
|
||||
if peerState_i := peer.Get(types.PeerStateKey); peerState_i != nil {
|
||||
peerState := peerState_i.(PeerState)
|
||||
peerHeight := peerState.GetHeight()
|
||||
if peerHeight < height-1 { // Allow for a lag of 1 block
|
||||
time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if peerState.GetHeight() < memTx.Height()-1 { // Allow for a lag of 1 block
|
||||
time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
|
||||
// send memTx
|
||||
msg := &TxMessage{Tx: memTx.tx}
|
||||
success := peer.Send(MempoolChannel, cdc.MustMarshalBinaryBare(msg))
|
||||
|
@@ -21,14 +21,6 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
type peerState struct {
|
||||
height int64
|
||||
}
|
||||
|
||||
func (ps peerState) GetHeight() int64 {
|
||||
return ps.height
|
||||
}
|
||||
|
||||
// mempoolLogger is a TestingLogger which uses a different
|
||||
// color for each validator ("validator" key must exist).
|
||||
func mempoolLogger() log.Logger {
|
||||
@@ -115,11 +107,6 @@ func TestReactorBroadcastTxMessage(t *testing.T) {
|
||||
r.Stop()
|
||||
}
|
||||
}()
|
||||
for _, r := range reactors {
|
||||
for _, peer := range r.Switch.Peers().List() {
|
||||
peer.Set(types.PeerStateKey, peerState{1})
|
||||
}
|
||||
}
|
||||
|
||||
// send a bunch of txs to the first reactor's mempool
|
||||
// and wait for them all to be received in the others
|
||||
|
49
node/node.go
49
node/node.go
@@ -13,9 +13,8 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/rs/cors"
|
||||
|
||||
"github.com/tendermint/go-amino"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
@@ -266,8 +265,17 @@ func NewNode(config *cfg.Config,
|
||||
proxyApp.Mempool(),
|
||||
state.LastBlockHeight,
|
||||
mempl.WithMetrics(memplMetrics),
|
||||
mempl.WithPreCheck(sm.TxPreCheck(state)),
|
||||
mempl.WithPostCheck(sm.TxPostCheck(state)),
|
||||
mempl.WithPreCheck(
|
||||
mempl.PreCheckAminoMaxBytes(
|
||||
types.MaxDataBytesUnknownEvidence(
|
||||
state.ConsensusParams.BlockSize.MaxBytes,
|
||||
state.Validators.Size(),
|
||||
),
|
||||
),
|
||||
),
|
||||
mempl.WithPostCheck(
|
||||
mempl.PostCheckMaxGas(state.ConsensusParams.BlockSize.MaxGas),
|
||||
),
|
||||
)
|
||||
mempoolLogger := logger.With("module", "mempool")
|
||||
mempool.SetLogger(mempoolLogger)
|
||||
@@ -371,8 +379,7 @@ func NewNode(config *cfg.Config,
|
||||
|
||||
// Setup Transport.
|
||||
var (
|
||||
mConnConfig = p2p.MConnConfig(config.P2P)
|
||||
transport = p2p.NewMultiplexTransport(nodeInfo, *nodeKey, mConnConfig)
|
||||
transport = p2p.NewMultiplexTransport(nodeInfo, *nodeKey)
|
||||
connFilters = []p2p.ConnFilterFunc{}
|
||||
peerFilters = []p2p.PeerFilterFunc{}
|
||||
)
|
||||
@@ -653,42 +660,30 @@ func (n *Node) startRPC() ([]net.Listener, error) {
|
||||
wm.SetLogger(rpcLogger.With("protocol", "websocket"))
|
||||
mux.HandleFunc("/websocket", wm.WebsocketHandler)
|
||||
rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, coreCodec, rpcLogger)
|
||||
|
||||
listener, err := rpcserver.Listen(
|
||||
listener, err := rpcserver.StartHTTPServer(
|
||||
listenAddr,
|
||||
mux,
|
||||
rpcLogger,
|
||||
rpcserver.Config{MaxOpenConnections: n.config.RPC.MaxOpenConnections},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rootHandler http.Handler = mux
|
||||
if n.config.RPC.IsCorsEnabled() {
|
||||
corsMiddleware := cors.New(cors.Options{
|
||||
AllowedOrigins: n.config.RPC.CORSAllowedOrigins,
|
||||
AllowedMethods: n.config.RPC.CORSAllowedMethods,
|
||||
AllowedHeaders: n.config.RPC.CORSAllowedHeaders,
|
||||
})
|
||||
rootHandler = corsMiddleware.Handler(mux)
|
||||
}
|
||||
|
||||
go rpcserver.StartHTTPServer(
|
||||
listener,
|
||||
rootHandler,
|
||||
rpcLogger,
|
||||
)
|
||||
listeners[i] = listener
|
||||
}
|
||||
|
||||
// we expose a simplified api over grpc for convenience to app devs
|
||||
grpcListenAddr := n.config.RPC.GRPCListenAddress
|
||||
if grpcListenAddr != "" {
|
||||
listener, err := rpcserver.Listen(
|
||||
grpcListenAddr, rpcserver.Config{MaxOpenConnections: n.config.RPC.GRPCMaxOpenConnections})
|
||||
listener, err := grpccore.StartGRPCServer(
|
||||
grpcListenAddr,
|
||||
grpccore.Config{
|
||||
MaxOpenConnections: n.config.RPC.GRPCMaxOpenConnections,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go grpccore.StartGRPCServer(listener)
|
||||
listeners = append(listeners, listener)
|
||||
}
|
||||
|
||||
|
@@ -84,11 +84,7 @@ type MConnection struct {
|
||||
errored uint32
|
||||
config MConnConfig
|
||||
|
||||
// Closing quitSendRoutine will cause
|
||||
// doneSendRoutine to close.
|
||||
quitSendRoutine chan struct{}
|
||||
doneSendRoutine chan struct{}
|
||||
|
||||
quit chan struct{}
|
||||
flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled.
|
||||
pingTimer *cmn.RepeatTimer // send pings periodically
|
||||
|
||||
@@ -194,8 +190,7 @@ func (c *MConnection) OnStart() error {
|
||||
if err := c.BaseService.OnStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.quitSendRoutine = make(chan struct{})
|
||||
c.doneSendRoutine = make(chan struct{})
|
||||
c.quit = make(chan struct{})
|
||||
c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle)
|
||||
c.pingTimer = cmn.NewRepeatTimer("ping", c.config.PingInterval)
|
||||
c.pongTimeoutCh = make(chan bool, 1)
|
||||
@@ -205,59 +200,15 @@ func (c *MConnection) OnStart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlushStop replicates the logic of OnStop.
|
||||
// It additionally ensures that all successful
|
||||
// .Send() calls will get flushed before closing
|
||||
// the connection.
|
||||
// NOTE: it is not safe to call this method more than once.
|
||||
func (c *MConnection) FlushStop() {
|
||||
c.BaseService.OnStop()
|
||||
c.flushTimer.Stop()
|
||||
c.pingTimer.Stop()
|
||||
c.chStatsTimer.Stop()
|
||||
if c.quitSendRoutine != nil {
|
||||
close(c.quitSendRoutine)
|
||||
// wait until the sendRoutine exits
|
||||
// so we dont race on calling sendSomePacketMsgs
|
||||
<-c.doneSendRoutine
|
||||
}
|
||||
|
||||
// Send and flush all pending msgs.
|
||||
// By now, IsRunning == false,
|
||||
// so any concurrent attempts to send will fail.
|
||||
// Since sendRoutine has exited, we can call this
|
||||
// safely
|
||||
eof := c.sendSomePacketMsgs()
|
||||
for !eof {
|
||||
eof = c.sendSomePacketMsgs()
|
||||
}
|
||||
c.flush()
|
||||
|
||||
// Now we can close the connection
|
||||
c.conn.Close() // nolint: errcheck
|
||||
|
||||
// We can't close pong safely here because
|
||||
// recvRoutine may write to it after we've stopped.
|
||||
// Though it doesn't need to get closed at all,
|
||||
// we close it @ recvRoutine.
|
||||
|
||||
// c.Stop()
|
||||
}
|
||||
|
||||
// OnStop implements BaseService
|
||||
func (c *MConnection) OnStop() {
|
||||
select {
|
||||
case <-c.quitSendRoutine:
|
||||
// already quit via FlushStop
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
c.BaseService.OnStop()
|
||||
c.flushTimer.Stop()
|
||||
c.pingTimer.Stop()
|
||||
c.chStatsTimer.Stop()
|
||||
close(c.quitSendRoutine)
|
||||
if c.quit != nil {
|
||||
close(c.quit)
|
||||
}
|
||||
c.conn.Close() // nolint: errcheck
|
||||
|
||||
// We can't close pong safely here because
|
||||
@@ -318,7 +269,7 @@ func (c *MConnection) Send(chID byte, msgBytes []byte) bool {
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
c.Logger.Debug("Send failed", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes))
|
||||
c.Logger.Error("Send failed", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes))
|
||||
}
|
||||
return success
|
||||
}
|
||||
@@ -414,8 +365,7 @@ FOR_LOOP:
|
||||
}
|
||||
c.sendMonitor.Update(int(_n))
|
||||
c.flush()
|
||||
case <-c.quitSendRoutine:
|
||||
close(c.doneSendRoutine)
|
||||
case <-c.quit:
|
||||
break FOR_LOOP
|
||||
case <-c.send:
|
||||
// Send some PacketMsgs
|
||||
|
@@ -36,43 +36,6 @@ func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msg
|
||||
return c
|
||||
}
|
||||
|
||||
func TestMConnectionSendFlushStop(t *testing.T) {
|
||||
server, client := NetPipe()
|
||||
defer server.Close() // nolint: errcheck
|
||||
defer client.Close() // nolint: errcheck
|
||||
|
||||
clientConn := createTestMConnection(client)
|
||||
err := clientConn.Start()
|
||||
require.Nil(t, err)
|
||||
defer clientConn.Stop()
|
||||
|
||||
msg := []byte("abc")
|
||||
assert.True(t, clientConn.Send(0x01, msg))
|
||||
|
||||
aminoMsgLength := 14
|
||||
|
||||
// start the reader in a new routine, so we can flush
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
msgB := make([]byte, aminoMsgLength)
|
||||
_, err := server.Read(msgB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
// stop the conn - it should flush all conns
|
||||
clientConn.FlushStop()
|
||||
|
||||
timer := time.NewTimer(3 * time.Second)
|
||||
select {
|
||||
case <-errCh:
|
||||
case <-timer.C:
|
||||
t.Error("timed out waiting for msgs to be read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMConnectionSend(t *testing.T) {
|
||||
server, client := NetPipe()
|
||||
defer server.Close() // nolint: errcheck
|
||||
|
@@ -25,11 +25,6 @@ func NewPeer() *peer {
|
||||
return p
|
||||
}
|
||||
|
||||
// FlushStop just calls Stop.
|
||||
func (p *peer) FlushStop() {
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
// ID always returns dummy.
|
||||
func (p *peer) ID() p2p.ID {
|
||||
return p2p.ID("dummy")
|
||||
|
@@ -32,10 +32,8 @@ type NetAddress struct {
|
||||
str string
|
||||
}
|
||||
|
||||
// IDAddressString returns id@hostPort. It strips the leading
|
||||
// protocol from protocolHostPort if it exists.
|
||||
func IDAddressString(id ID, protocolHostPort string) string {
|
||||
hostPort := removeProtocolIfDefined(protocolHostPort)
|
||||
// IDAddressString returns id@hostPort.
|
||||
func IDAddressString(id ID, hostPort string) string {
|
||||
return fmt.Sprintf("%s@%s", id, hostPort)
|
||||
}
|
||||
|
||||
|
@@ -216,8 +216,7 @@ OUTER_LOOP:
|
||||
// ListenAddr. Note that the ListenAddr is not authenticated and
|
||||
// may not match that address actually dialed if its an outbound peer.
|
||||
func (info DefaultNodeInfo) NetAddress() *NetAddress {
|
||||
idAddr := IDAddressString(info.ID(), info.ListenAddr)
|
||||
netAddr, err := NewNetAddressString(idAddr)
|
||||
netAddr, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case ErrNetAddressLookup:
|
||||
|
14
p2p/peer.go
14
p2p/peer.go
@@ -8,6 +8,7 @@ import (
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/tendermint/tendermint/config"
|
||||
tmconn "github.com/tendermint/tendermint/p2p/conn"
|
||||
)
|
||||
|
||||
@@ -16,7 +17,6 @@ const metricsTickerDuration = 10 * time.Second
|
||||
// Peer is an interface representing a peer connected on a reactor.
|
||||
type Peer interface {
|
||||
cmn.Service
|
||||
FlushStop()
|
||||
|
||||
ID() ID // peer's cryptographic ID
|
||||
RemoteIP() net.IP // remote IP of the connection
|
||||
@@ -41,6 +41,7 @@ type Peer interface {
|
||||
type peerConn struct {
|
||||
outbound bool
|
||||
persistent bool
|
||||
config *config.P2PConfig
|
||||
conn net.Conn // source connection
|
||||
|
||||
originalAddr *NetAddress // nil for inbound connections
|
||||
@@ -51,6 +52,7 @@ type peerConn struct {
|
||||
|
||||
func newPeerConn(
|
||||
outbound, persistent bool,
|
||||
config *config.P2PConfig,
|
||||
conn net.Conn,
|
||||
originalAddr *NetAddress,
|
||||
) peerConn {
|
||||
@@ -58,6 +60,7 @@ func newPeerConn(
|
||||
return peerConn{
|
||||
outbound: outbound,
|
||||
persistent: persistent,
|
||||
config: config,
|
||||
conn: conn,
|
||||
originalAddr: originalAddr,
|
||||
}
|
||||
@@ -181,15 +184,6 @@ func (p *peer) OnStart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlushStop mimics OnStop but additionally ensures that all successful
|
||||
// .Send() calls will get flushed before closing the connection.
|
||||
// NOTE: it is not safe to call this method more than once.
|
||||
func (p *peer) FlushStop() {
|
||||
p.metricsTicker.Stop()
|
||||
p.BaseService.OnStop()
|
||||
p.mconn.FlushStop() // stop everything and close the conn
|
||||
}
|
||||
|
||||
// OnStop implements BaseService.
|
||||
func (p *peer) OnStop() {
|
||||
p.metricsTicker.Stop()
|
||||
|
@@ -18,7 +18,6 @@ type mockPeer struct {
|
||||
id ID
|
||||
}
|
||||
|
||||
func (mp *mockPeer) FlushStop() { mp.Stop() }
|
||||
func (mp *mockPeer) TrySend(chID byte, msgBytes []byte) bool { return true }
|
||||
func (mp *mockPeer) Send(chID byte, msgBytes []byte) bool { return true }
|
||||
func (mp *mockPeer) NodeInfo() NodeInfo { return DefaultNodeInfo{} }
|
||||
|
@@ -162,10 +162,10 @@ func (a *addrBook) FilePath() string {
|
||||
|
||||
// AddOurAddress one of our addresses.
|
||||
func (a *addrBook) AddOurAddress(addr *p2p.NetAddress) {
|
||||
a.Logger.Info("Add our address to book", "addr", addr)
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
a.Logger.Info("Add our address to book", "addr", addr)
|
||||
a.ourAddrs[addr.String()] = struct{}{}
|
||||
a.mtx.Unlock()
|
||||
}
|
||||
|
||||
// OurAddress returns true if it is our address.
|
||||
@@ -178,10 +178,10 @@ func (a *addrBook) OurAddress(addr *p2p.NetAddress) bool {
|
||||
|
||||
func (a *addrBook) AddPrivateIDs(IDs []string) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
for _, id := range IDs {
|
||||
a.privateIDs[p2p.ID(id)] = struct{}{}
|
||||
}
|
||||
a.mtx.Unlock()
|
||||
}
|
||||
|
||||
// AddAddress implements AddrBook
|
||||
@@ -202,7 +202,7 @@ func (a *addrBook) RemoveAddress(addr *p2p.NetAddress) {
|
||||
if ka == nil {
|
||||
return
|
||||
}
|
||||
a.Logger.Info("Remove address from book", "addr", addr)
|
||||
a.Logger.Info("Remove address from book", "addr", ka.Addr, "ID", ka.ID())
|
||||
a.removeFromAllBuckets(ka)
|
||||
}
|
||||
|
||||
@@ -217,8 +217,8 @@ func (a *addrBook) IsGood(addr *p2p.NetAddress) bool {
|
||||
// HasAddress returns true if the address is in the book.
|
||||
func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
ka := a.addrLookup[addr.ID]
|
||||
a.mtx.Unlock()
|
||||
return ka != nil
|
||||
}
|
||||
|
||||
@@ -461,12 +461,13 @@ ADDRS_LOOP:
|
||||
|
||||
// ListOfKnownAddresses returns the new and old addresses.
|
||||
func (a *addrBook) ListOfKnownAddresses() []*knownAddress {
|
||||
addrs := []*knownAddress{}
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
addrs := []*knownAddress{}
|
||||
for _, addr := range a.addrLookup {
|
||||
addrs = append(addrs, addr.copy())
|
||||
}
|
||||
a.mtx.Unlock()
|
||||
return addrs
|
||||
}
|
||||
|
||||
|
@@ -208,38 +208,22 @@ func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) {
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *pexRequestMessage:
|
||||
// Check we're not receiving too many requests
|
||||
if err := r.receiveRequest(src); err != nil {
|
||||
r.Switch.StopPeerForError(src, err)
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: this is a prime candidate for amplification attacks,
|
||||
// Seeds disconnect after sending a batch of addrs
|
||||
// NOTE: this is a prime candidate for amplification attacks
|
||||
// so it's important we
|
||||
// 1) restrict how frequently peers can request
|
||||
// 2) limit the output size
|
||||
|
||||
// If we're a seed and this is an inbound peer,
|
||||
// respond once and disconnect.
|
||||
if r.config.SeedMode && !src.IsOutbound() {
|
||||
id := string(src.ID())
|
||||
v := r.lastReceivedRequests.Get(id)
|
||||
if v != nil {
|
||||
// FlushStop/StopPeer are already
|
||||
// running in a go-routine.
|
||||
return
|
||||
}
|
||||
r.lastReceivedRequests.Set(id, time.Now())
|
||||
|
||||
// Send addrs and disconnect
|
||||
if r.config.SeedMode {
|
||||
r.SendAddrs(src, r.book.GetSelectionWithBias(biasToSelectNewPeers))
|
||||
go func() {
|
||||
// In a go-routine so it doesn't block .Receive.
|
||||
src.FlushStop()
|
||||
r.Switch.StopPeerGracefully(src)
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second * 3) // TODO Rethink this. Without it, above may not actually send.
|
||||
r.Switch.StopPeerGracefully(src)
|
||||
} else {
|
||||
// Check we're not receiving requests too frequently.
|
||||
if err := r.receiveRequest(src); err != nil {
|
||||
r.Switch.StopPeerForError(src, err)
|
||||
return
|
||||
}
|
||||
r.SendAddrs(src, r.book.GetSelection())
|
||||
}
|
||||
|
||||
|
@@ -387,7 +387,6 @@ func newMockPeer() mockPeer {
|
||||
return mp
|
||||
}
|
||||
|
||||
func (mp mockPeer) FlushStop() { mp.Stop() }
|
||||
func (mp mockPeer) ID() p2p.ID { return mp.addr.ID }
|
||||
func (mp mockPeer) IsOutbound() bool { return mp.outbound }
|
||||
func (mp mockPeer) IsPersistent() bool { return mp.persistent }
|
||||
|
@@ -27,17 +27,6 @@ const (
|
||||
reconnectBackOffBaseSeconds = 3
|
||||
)
|
||||
|
||||
// MConnConfig returns an MConnConfig with fields updated
|
||||
// from the P2PConfig.
|
||||
func MConnConfig(cfg *config.P2PConfig) conn.MConnConfig {
|
||||
mConfig := conn.DefaultMConnConfig()
|
||||
mConfig.FlushThrottle = cfg.FlushThrottleTimeout
|
||||
mConfig.SendRate = cfg.SendRate
|
||||
mConfig.RecvRate = cfg.RecvRate
|
||||
mConfig.MaxPacketMsgPayloadSize = cfg.MaxPacketMsgPayloadSize
|
||||
return mConfig
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// An AddrBook represents an address book from the pex package, which is used
|
||||
@@ -81,6 +70,8 @@ type Switch struct {
|
||||
filterTimeout time.Duration
|
||||
peerFilters []PeerFilterFunc
|
||||
|
||||
mConfig conn.MConnConfig
|
||||
|
||||
rng *cmn.Rand // seed for randomizing dial times and orders
|
||||
|
||||
metrics *Metrics
|
||||
@@ -111,6 +102,14 @@ func NewSwitch(
|
||||
// Ensure we have a completely undeterministic PRNG.
|
||||
sw.rng = cmn.NewRand()
|
||||
|
||||
mConfig := conn.DefaultMConnConfig()
|
||||
mConfig.FlushThrottle = cfg.FlushThrottleTimeout
|
||||
mConfig.SendRate = cfg.SendRate
|
||||
mConfig.RecvRate = cfg.RecvRate
|
||||
mConfig.MaxPacketMsgPayloadSize = cfg.MaxPacketMsgPayloadSize
|
||||
|
||||
sw.mConfig = mConfig
|
||||
|
||||
sw.BaseService = *cmn.NewBaseService(nil, "P2P Switch", sw)
|
||||
|
||||
for _, option := range options {
|
||||
@@ -329,11 +328,6 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
if sw.IsDialingOrExistingAddress(addr) {
|
||||
sw.Logger.Debug("Peer connection has been established or dialed while we waiting next try", "addr", addr)
|
||||
return
|
||||
}
|
||||
|
||||
err := sw.DialPeerWithAddress(addr, true)
|
||||
if err == nil {
|
||||
return // success
|
||||
@@ -421,15 +415,12 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b
|
||||
if addr.Same(ourAddr) {
|
||||
sw.Logger.Debug("Ignore attempt to connect to ourselves", "addr", addr, "ourAddr", ourAddr)
|
||||
return
|
||||
}
|
||||
|
||||
sw.randomSleep(0)
|
||||
|
||||
if sw.IsDialingOrExistingAddress(addr) {
|
||||
} else if sw.IsDialingOrExistingAddress(addr) {
|
||||
sw.Logger.Debug("Ignore attempt to connect to an existing peer", "addr", addr)
|
||||
return
|
||||
}
|
||||
|
||||
sw.randomSleep(0)
|
||||
err := sw.DialPeerWithAddress(addr, persistent)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
@@ -625,7 +616,7 @@ func (sw *Switch) addPeer(p Peer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
p.SetLogger(sw.Logger.With("peer", p.NodeInfo().NetAddress()))
|
||||
p.SetLogger(sw.Logger.With("peer", p.NodeInfo().NetAddress().String))
|
||||
|
||||
// All good. Start peer
|
||||
if sw.IsRunning() {
|
||||
|
@@ -135,7 +135,7 @@ func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
|
||||
|
||||
p := newPeer(
|
||||
pc,
|
||||
MConnConfig(sw.config),
|
||||
sw.mConfig,
|
||||
ni,
|
||||
sw.reactorsByCh,
|
||||
sw.chDescs,
|
||||
@@ -175,7 +175,7 @@ func MakeSwitch(
|
||||
}
|
||||
nodeInfo := testNodeInfo(nodeKey.ID(), fmt.Sprintf("node%d", i))
|
||||
|
||||
t := NewMultiplexTransport(nodeInfo, nodeKey, MConnConfig(cfg))
|
||||
t := NewMultiplexTransport(nodeInfo, nodeKey)
|
||||
|
||||
addr := nodeInfo.NetAddress()
|
||||
if err := t.Listen(*addr); err != nil {
|
||||
@@ -232,6 +232,7 @@ func testPeerConn(
|
||||
|
||||
// Only the information we already have
|
||||
return peerConn{
|
||||
config: cfg,
|
||||
outbound: outbound,
|
||||
persistent: persistent,
|
||||
conn: conn,
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/p2p/conn"
|
||||
)
|
||||
@@ -128,10 +129,11 @@ type MultiplexTransport struct {
|
||||
nodeKey NodeKey
|
||||
resolver IPResolver
|
||||
|
||||
// TODO(xla): This config is still needed as we parameterise peerConn and
|
||||
// TODO(xla): Those configs are still needed as we parameterise peerConn and
|
||||
// peer currently. All relevant configuration should be refactored into options
|
||||
// with sane defaults.
|
||||
mConfig conn.MConnConfig
|
||||
mConfig conn.MConnConfig
|
||||
p2pConfig config.P2PConfig
|
||||
}
|
||||
|
||||
// Test multiplexTransport for interface completeness.
|
||||
@@ -142,7 +144,6 @@ var _ transportLifecycle = (*MultiplexTransport)(nil)
|
||||
func NewMultiplexTransport(
|
||||
nodeInfo NodeInfo,
|
||||
nodeKey NodeKey,
|
||||
mConfig conn.MConnConfig,
|
||||
) *MultiplexTransport {
|
||||
return &MultiplexTransport{
|
||||
acceptc: make(chan accept),
|
||||
@@ -150,7 +151,7 @@ func NewMultiplexTransport(
|
||||
dialTimeout: defaultDialTimeout,
|
||||
filterTimeout: defaultFilterTimeout,
|
||||
handshakeTimeout: defaultHandshakeTimeout,
|
||||
mConfig: mConfig,
|
||||
mConfig: conn.DefaultMConnConfig(),
|
||||
nodeInfo: nodeInfo,
|
||||
nodeKey: nodeKey,
|
||||
conns: NewConnSet(),
|
||||
@@ -404,6 +405,7 @@ func (mt *MultiplexTransport) wrapPeer(
|
||||
peerConn := newPeerConn(
|
||||
cfg.outbound,
|
||||
cfg.persistent,
|
||||
&mt.p2pConfig,
|
||||
c,
|
||||
dialedAddr,
|
||||
)
|
||||
|
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/p2p/conn"
|
||||
)
|
||||
|
||||
var defaultNodeName = "host_peer"
|
||||
@@ -18,20 +17,8 @@ func emptyNodeInfo() NodeInfo {
|
||||
return DefaultNodeInfo{}
|
||||
}
|
||||
|
||||
// newMultiplexTransport returns a tcp connected multiplexed peer
|
||||
// using the default MConnConfig. It's a convenience function used
|
||||
// for testing.
|
||||
func newMultiplexTransport(
|
||||
nodeInfo NodeInfo,
|
||||
nodeKey NodeKey,
|
||||
) *MultiplexTransport {
|
||||
return NewMultiplexTransport(
|
||||
nodeInfo, nodeKey, conn.DefaultMConnConfig(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestTransportMultiplexConnFilter(t *testing.T) {
|
||||
mt := newMultiplexTransport(
|
||||
mt := NewMultiplexTransport(
|
||||
emptyNodeInfo(),
|
||||
NodeKey{
|
||||
PrivKey: ed25519.GenPrivKey(),
|
||||
@@ -88,7 +75,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTransportMultiplexConnFilterTimeout(t *testing.T) {
|
||||
mt := newMultiplexTransport(
|
||||
mt := NewMultiplexTransport(
|
||||
emptyNodeInfo(),
|
||||
NodeKey{
|
||||
PrivKey: ed25519.GenPrivKey(),
|
||||
@@ -153,7 +140,7 @@ func TestTransportMultiplexAcceptMultiple(t *testing.T) {
|
||||
go func() {
|
||||
var (
|
||||
pv = ed25519.GenPrivKey()
|
||||
dialer = newMultiplexTransport(
|
||||
dialer = NewMultiplexTransport(
|
||||
testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName),
|
||||
NodeKey{
|
||||
PrivKey: pv,
|
||||
@@ -274,7 +261,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) {
|
||||
<-slowc
|
||||
|
||||
var (
|
||||
dialer = newMultiplexTransport(
|
||||
dialer = NewMultiplexTransport(
|
||||
fastNodeInfo,
|
||||
NodeKey{
|
||||
PrivKey: fastNodePV,
|
||||
@@ -320,7 +307,7 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) {
|
||||
go func() {
|
||||
var (
|
||||
pv = ed25519.GenPrivKey()
|
||||
dialer = newMultiplexTransport(
|
||||
dialer = NewMultiplexTransport(
|
||||
testNodeInfo(PubKeyToID(pv.PubKey()), ""), // Should not be empty
|
||||
NodeKey{
|
||||
PrivKey: pv,
|
||||
@@ -363,7 +350,7 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) {
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
dialer := newMultiplexTransport(
|
||||
dialer := NewMultiplexTransport(
|
||||
testNodeInfo(
|
||||
PubKeyToID(ed25519.GenPrivKey().PubKey()), "dialer",
|
||||
),
|
||||
@@ -409,7 +396,7 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) {
|
||||
go func() {
|
||||
var (
|
||||
pv = ed25519.GenPrivKey()
|
||||
dialer = newMultiplexTransport(
|
||||
dialer = NewMultiplexTransport(
|
||||
testNodeInfoWithNetwork(PubKeyToID(pv.PubKey()), "dialer", "incompatible-network"),
|
||||
NodeKey{
|
||||
PrivKey: pv,
|
||||
@@ -566,7 +553,7 @@ func TestTransportHandshake(t *testing.T) {
|
||||
func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport {
|
||||
var (
|
||||
pv = ed25519.GenPrivKey()
|
||||
mt = newMultiplexTransport(
|
||||
mt = NewMultiplexTransport(
|
||||
testNodeInfo(
|
||||
PubKeyToID(pv.PubKey()), "transport",
|
||||
),
|
||||
|
@@ -69,18 +69,7 @@ func WaitForOneEvent(c EventsClient, evtTyp string, timeout time.Duration) (type
|
||||
}
|
||||
|
||||
// make sure to unregister after the test is over
|
||||
defer func() {
|
||||
// drain evts to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-evts:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
c.UnsubscribeAll(ctx, subscriber)
|
||||
}()
|
||||
defer c.UnsubscribeAll(ctx, subscriber)
|
||||
|
||||
select {
|
||||
case evt := <-evts:
|
||||
|
@@ -2,7 +2,6 @@ package client_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
"github.com/tendermint/tendermint/rpc/test"
|
||||
rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -33,21 +32,6 @@ func GetClients() []client.Client {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorsEnabled(t *testing.T) {
|
||||
origin := rpctest.GetConfig().RPC.CORSAllowedOrigins[0]
|
||||
remote := strings.Replace(rpctest.GetConfig().RPC.ListenAddress, "tcp", "http", -1)
|
||||
|
||||
req, err := http.NewRequest("GET", remote, nil)
|
||||
require.Nil(t, err, "%+v", err)
|
||||
req.Header.Set("Origin", origin)
|
||||
c := &http.Client{}
|
||||
resp, err := c.Do(req)
|
||||
defer resp.Body.Close()
|
||||
|
||||
require.Nil(t, err, "%+v", err)
|
||||
assert.Equal(t, resp.Header.Get("Access-Control-Allow-Origin"), origin)
|
||||
}
|
||||
|
||||
// Make sure status is correct (we connect properly)
|
||||
func TestStatus(t *testing.T) {
|
||||
for i, c := range GetClients() {
|
||||
|
@@ -193,10 +193,7 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
|
||||
peers := p2pPeers.Peers().List()
|
||||
peerStates := make([]ctypes.PeerStateInfo, len(peers))
|
||||
for i, peer := range peers {
|
||||
peerState, ok := peer.Get(types.PeerStateKey).(*cm.PeerState)
|
||||
if !ok { // peer does not have a state yet
|
||||
continue
|
||||
}
|
||||
peerState := peer.Get(types.PeerStateKey).(*cm.PeerState)
|
||||
peerStateJSON, err := peerState.ToJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -12,10 +12,7 @@ See it here: https://github.com/tendermint/tendermint/tree/master/rpc/lib
|
||||
|
||||
## Configuration
|
||||
|
||||
RPC can be configured by tuning parameters under `[rpc]` table in the `$TMHOME/config/config.toml` file or by using the `--rpc.X` command-line flags.
|
||||
|
||||
Default rpc listen address is `tcp://0.0.0.0:26657`. To set another address, set the `laddr` config parameter to desired value.
|
||||
CORS (Cross-Origin Resource Sharing) can be enabled by setting `cors_allowed_origins`, `cors_allowed_methods`, `cors_allowed_headers` config parameters.
|
||||
Set the `laddr` config parameter under `[rpc]` table in the `$TMHOME/config/config.toml` file or the `--rpc.laddr` command-line flag to the desired protocol://host:port setting. Default: `tcp://0.0.0.0:26657`.
|
||||
|
||||
## Arguments
|
||||
|
||||
|
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -51,7 +51,7 @@ import (
|
||||
func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
err := mempool.CheckTx(tx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error broadcasting transaction: %v", err)
|
||||
}
|
||||
return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
resCh <- res
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error broadcasting transaction: %v", err)
|
||||
}
|
||||
res := <-resCh
|
||||
r := res.GetCheckTx()
|
||||
@@ -106,9 +106,8 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CONTRACT: only returns error if mempool.CheckTx() errs or if we timeout
|
||||
// waiting for tx to commit.
|
||||
//
|
||||
// CONTRACT: only returns error if mempool.BroadcastTx errs (ie. problem with the app)
|
||||
// or if we timeout waiting for tx to commit.
|
||||
// If CheckTx or DeliverTx fail, no error will be returned, but the returned result
|
||||
// will contain a non-OK ABCI code.
|
||||
//
|
||||
@@ -151,31 +150,20 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
// |-----------+------+---------+----------+-----------------|
|
||||
// | tx | Tx | nil | true | The transaction |
|
||||
func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
// Subscribe to tx being committed in block.
|
||||
// subscribe to tx being committed in block
|
||||
ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout)
|
||||
defer cancel()
|
||||
deliverTxResCh := make(chan interface{}, 1)
|
||||
deliverTxResCh := make(chan interface{})
|
||||
q := types.EventQueryTxFor(tx)
|
||||
err := eventBus.Subscribe(ctx, "mempool", q, deliverTxResCh)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "failed to subscribe to tx")
|
||||
logger.Error("Error on broadcast_tx_commit", "err", err)
|
||||
return nil, err
|
||||
logger.Error("Error on broadcastTxCommit", "err", err)
|
||||
return nil, fmt.Errorf("Error on broadcastTxCommit: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
// drain deliverTxResCh to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-deliverTxResCh:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
eventBus.Unsubscribe(context.Background(), "mempool", q)
|
||||
}()
|
||||
defer eventBus.Unsubscribe(context.Background(), "mempool", q)
|
||||
|
||||
// Broadcast tx and wait for CheckTx result
|
||||
// broadcast the tx and register checktx callback
|
||||
checkTxResCh := make(chan *abci.Response, 1)
|
||||
err = mempool.CheckTx(tx, func(res *abci.Response) {
|
||||
checkTxResCh <- res
|
||||
@@ -184,36 +172,40 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
logger.Error("Error on broadcastTxCommit", "err", err)
|
||||
return nil, fmt.Errorf("Error on broadcastTxCommit: %v", err)
|
||||
}
|
||||
checkTxResMsg := <-checkTxResCh
|
||||
checkTxRes := checkTxResMsg.GetCheckTx()
|
||||
if checkTxRes.Code != abci.CodeTypeOK {
|
||||
checkTxRes := <-checkTxResCh
|
||||
checkTxR := checkTxRes.GetCheckTx()
|
||||
if checkTxR.Code != abci.CodeTypeOK {
|
||||
// CheckTx failed!
|
||||
return &ctypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *checkTxRes,
|
||||
CheckTx: *checkTxR,
|
||||
DeliverTx: abci.ResponseDeliverTx{},
|
||||
Hash: tx.Hash(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Wait for the tx to be included in a block or timeout.
|
||||
// Wait for the tx to be included in a block,
|
||||
// timeout after something reasonable.
|
||||
// TODO: configurable?
|
||||
var deliverTxTimeout = rpcserver.WriteTimeout / 2
|
||||
timer := time.NewTimer(60 * 2 * time.Second)
|
||||
select {
|
||||
case deliverTxResMsg := <-deliverTxResCh: // The tx was included in a block.
|
||||
case deliverTxResMsg := <-deliverTxResCh:
|
||||
deliverTxRes := deliverTxResMsg.(types.EventDataTx)
|
||||
// The tx was included in a block.
|
||||
deliverTxR := deliverTxRes.Result
|
||||
logger.Info("DeliverTx passed ", "tx", cmn.HexBytes(tx), "response", deliverTxR)
|
||||
return &ctypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *checkTxRes,
|
||||
DeliverTx: deliverTxRes.Result,
|
||||
CheckTx: *checkTxR,
|
||||
DeliverTx: deliverTxR,
|
||||
Hash: tx.Hash(),
|
||||
Height: deliverTxRes.Height,
|
||||
}, nil
|
||||
case <-time.After(deliverTxTimeout):
|
||||
err = errors.New("Timed out waiting for tx to be included in a block")
|
||||
logger.Error("Error on broadcastTxCommit", "err", err)
|
||||
case <-timer.C:
|
||||
logger.Error("failed to include tx")
|
||||
return &ctypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *checkTxRes,
|
||||
CheckTx: *checkTxR,
|
||||
DeliverTx: abci.ResponseDeliverTx{},
|
||||
Hash: tx.Hash(),
|
||||
}, err
|
||||
}, fmt.Errorf("Timed out waiting for transaction to be included in a block")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/consensus"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
@@ -8,7 +10,6 @@ import (
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -20,7 +21,7 @@ const (
|
||||
maxPerPage = 100
|
||||
)
|
||||
|
||||
var subscribeTimeout = rpcserver.WriteTimeout / 2
|
||||
var subscribeTimeout = 5 * time.Second
|
||||
|
||||
//----------------------------------------------
|
||||
// These interfaces are used by RPC and must be thread safe
|
||||
|
@@ -1,9 +1,12 @@
|
||||
package core_grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/netutil"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
@@ -14,12 +17,28 @@ type Config struct {
|
||||
MaxOpenConnections int
|
||||
}
|
||||
|
||||
// StartGRPCServer starts a new gRPC BroadcastAPIServer using the given net.Listener.
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartGRPCServer(ln net.Listener) error {
|
||||
// StartGRPCServer starts a new gRPC BroadcastAPIServer, listening on
|
||||
// protoAddr, in a goroutine. Returns a listener and an error, if it fails to
|
||||
// parse an address.
|
||||
func StartGRPCServer(protoAddr string, config Config) (net.Listener, error) {
|
||||
parts := strings.SplitN(protoAddr, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid listen address for grpc server (did you forget a tcp:// prefix?) : %s", protoAddr)
|
||||
}
|
||||
proto, addr := parts[0], parts[1]
|
||||
ln, err := net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.MaxOpenConnections > 0 {
|
||||
ln = netutil.LimitListener(ln, config.MaxOpenConnections)
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
RegisterBroadcastAPIServer(grpcServer, &broadcastAPI{})
|
||||
return grpcServer.Serve(ln)
|
||||
go grpcServer.Serve(ln) // nolint: errcheck
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
// StartGRPCClient dials the gRPC server using protoAddr and returns a new
|
||||
|
@@ -70,9 +70,12 @@
|
||||
// wm := rpcserver.NewWebsocketManager(Routes)
|
||||
// mux.HandleFunc("/websocket", wm.WebsocketHandler)
|
||||
// logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
// listener, err := rpc.Listen("0.0.0.0:8080", rpcserver.Config{})
|
||||
// if err != nil { panic(err) }
|
||||
// go rpcserver.StartHTTPServer(listener, mux, logger)
|
||||
// go func() {
|
||||
// _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`)
|
||||
// Now see all available endpoints by sending a GET request to `0.0.0.0:8008`.
|
||||
|
@@ -121,11 +121,12 @@ func setup() {
|
||||
wm := server.NewWebsocketManager(Routes, RoutesCdc, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second))
|
||||
wm.SetLogger(tcpLogger)
|
||||
mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
|
||||
listener1, err := server.Listen(tcpAddr, server.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go server.StartHTTPServer(listener1, mux, tcpLogger)
|
||||
go func() {
|
||||
_, err := server.StartHTTPServer(tcpAddr, mux, tcpLogger, server.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
unixLogger := logger.With("socket", "unix")
|
||||
mux2 := http.NewServeMux()
|
||||
@@ -133,11 +134,12 @@ func setup() {
|
||||
wm = server.NewWebsocketManager(Routes, RoutesCdc)
|
||||
wm.SetLogger(unixLogger)
|
||||
mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
|
||||
listener2, err := server.Listen(unixAddr, server.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go server.StartHTTPServer(listener2, mux2, unixLogger)
|
||||
go func() {
|
||||
_, err := server.StartHTTPServer(unixAddr, mux2, unixLogger, server.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// wait for servers to start
|
||||
time.Sleep(time.Second * 2)
|
||||
|
@@ -27,56 +27,92 @@ const (
|
||||
// maxBodyBytes controls the maximum number of bytes the
|
||||
// server will read parsing the request body.
|
||||
maxBodyBytes = int64(1000000) // 1MB
|
||||
|
||||
// same as the net/http default
|
||||
maxHeaderBytes = 1 << 20
|
||||
|
||||
// Timeouts for reading/writing to the http connection.
|
||||
// Public so handlers can read them -
|
||||
// /broadcast_tx_commit has it's own timeout, which should
|
||||
// be less than the WriteTimeout here.
|
||||
// TODO: use a config instead.
|
||||
ReadTimeout = 3 * time.Second
|
||||
WriteTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
// StartHTTPServer takes a listener and starts an HTTP server with the given handler.
|
||||
// StartHTTPServer starts an HTTP server on listenAddr with the given handler.
|
||||
// It wraps handler with RecoverAndLogHandler.
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger) error {
|
||||
logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr()))
|
||||
s := &http.Server{
|
||||
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
ReadTimeout: ReadTimeout,
|
||||
WriteTimeout: WriteTimeout,
|
||||
MaxHeaderBytes: maxHeaderBytes,
|
||||
func StartHTTPServer(
|
||||
listenAddr string,
|
||||
handler http.Handler,
|
||||
logger log.Logger,
|
||||
config Config,
|
||||
) (listener net.Listener, err error) {
|
||||
var proto, addr string
|
||||
parts := strings.SplitN(listenAddr, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf(
|
||||
"Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)",
|
||||
listenAddr,
|
||||
)
|
||||
}
|
||||
err := s.Serve(listener)
|
||||
logger.Info("RPC HTTP server stopped", "err", err)
|
||||
return err
|
||||
proto, addr = parts[0], parts[1]
|
||||
|
||||
logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listenAddr))
|
||||
listener, err = net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Failed to listen on %v: %v", listenAddr, err)
|
||||
}
|
||||
if config.MaxOpenConnections > 0 {
|
||||
listener = netutil.LimitListener(listener, config.MaxOpenConnections)
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := http.Serve(
|
||||
listener,
|
||||
RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
)
|
||||
logger.Info("RPC HTTP server stopped", "err", err)
|
||||
}()
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
// StartHTTPAndTLSServer takes a listener and starts an HTTPS server with the given handler.
|
||||
// StartHTTPAndTLSServer starts an HTTPS server on listenAddr with the given
|
||||
// handler.
|
||||
// It wraps handler with RecoverAndLogHandler.
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartHTTPAndTLSServer(
|
||||
listener net.Listener,
|
||||
listenAddr string,
|
||||
handler http.Handler,
|
||||
certFile, keyFile string,
|
||||
logger log.Logger,
|
||||
) error {
|
||||
logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)",
|
||||
listener.Addr(), certFile, keyFile))
|
||||
s := &http.Server{
|
||||
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
ReadTimeout: ReadTimeout,
|
||||
WriteTimeout: WriteTimeout,
|
||||
MaxHeaderBytes: maxHeaderBytes,
|
||||
config Config,
|
||||
) (listener net.Listener, err error) {
|
||||
var proto, addr string
|
||||
parts := strings.SplitN(listenAddr, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf(
|
||||
"Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)",
|
||||
listenAddr,
|
||||
)
|
||||
}
|
||||
err := s.ServeTLS(listener, certFile, keyFile)
|
||||
proto, addr = parts[0], parts[1]
|
||||
|
||||
logger.Error("RPC HTTPS server stopped", "err", err)
|
||||
return err
|
||||
logger.Info(
|
||||
fmt.Sprintf(
|
||||
"Starting RPC HTTPS server on %s (cert: %q, key: %q)",
|
||||
listenAddr,
|
||||
certFile,
|
||||
keyFile,
|
||||
),
|
||||
)
|
||||
listener, err = net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Failed to listen on %v: %v", listenAddr, err)
|
||||
}
|
||||
if config.MaxOpenConnections > 0 {
|
||||
listener = netutil.LimitListener(listener, config.MaxOpenConnections)
|
||||
}
|
||||
|
||||
err = http.ServeTLS(
|
||||
listener,
|
||||
RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
certFile,
|
||||
keyFile,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("RPC HTTPS server stopped", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func WriteRPCResponseHTTPError(
|
||||
@@ -115,6 +151,11 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
|
||||
rww := &ResponseWriterWrapper{-1, w}
|
||||
begin := time.Now()
|
||||
|
||||
// Common headers
|
||||
origin := r.Header.Get("Origin")
|
||||
rww.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
rww.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
|
||||
rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
|
||||
|
||||
defer func() {
|
||||
@@ -177,25 +218,3 @@ func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, h.n)
|
||||
h.h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// Listen starts a new net.Listener on the given address.
|
||||
// It returns an error if the address is invalid or the call to Listen() fails.
|
||||
func Listen(addr string, config Config) (listener net.Listener, err error) {
|
||||
parts := strings.SplitN(addr, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf(
|
||||
"Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)",
|
||||
addr,
|
||||
)
|
||||
}
|
||||
proto, addr := parts[0], parts[1]
|
||||
listener, err = net.Listen(proto, addr)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Failed to listen on %v: %v", addr, err)
|
||||
}
|
||||
if config.MaxOpenConnections > 0 {
|
||||
listener = netutil.LimitListener(listener, config.MaxOpenConnections)
|
||||
}
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
@@ -30,10 +30,11 @@ func TestMaxOpenConnections(t *testing.T) {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
fmt.Fprint(w, "some body")
|
||||
})
|
||||
l, err := Listen("tcp://127.0.0.1:0", Config{MaxOpenConnections: max})
|
||||
require.NoError(t, err)
|
||||
l, err := StartHTTPServer("tcp://127.0.0.1:0", mux, log.TestingLogger(), Config{MaxOpenConnections: max})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
go StartHTTPServer(l, mux, log.TestingLogger())
|
||||
|
||||
// Make N GET calls to the server.
|
||||
attempts := max * 2
|
||||
@@ -66,14 +67,11 @@ func TestMaxOpenConnections(t *testing.T) {
|
||||
func TestStartHTTPAndTLSServer(t *testing.T) {
|
||||
// set up fixtures
|
||||
listenerAddr := "tcp://0.0.0.0:0"
|
||||
listener, err := Listen(listenerAddr, Config{MaxOpenConnections: 1})
|
||||
require.NoError(t, err)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
// test failure
|
||||
err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger())
|
||||
gotListener, err := StartHTTPAndTLSServer(listenerAddr, mux, "", "", log.TestingLogger(), Config{MaxOpenConnections: 1})
|
||||
require.Nil(t, gotListener)
|
||||
require.IsType(t, (*os.PathError)(nil), err)
|
||||
|
||||
// TODO: test that starting the server can actually work
|
||||
}
|
||||
|
@@ -28,11 +28,11 @@ func main() {
|
||||
cdc := amino.NewCodec()
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
rpcserver.RegisterRPCFuncs(mux, routes, cdc, logger)
|
||||
listener, err := rpcserver.Listen("0.0.0.0:8008", rpcserver.Config{})
|
||||
_, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger, rpcserver.Config{})
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
go rpcserver.StartHTTPServer(listener, mux, logger)
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
})
|
||||
|
@@ -84,7 +84,6 @@ func GetConfig() *cfg.Config {
|
||||
tm, rpc, grpc := makeAddrs()
|
||||
globalConfig.P2P.ListenAddress = tm
|
||||
globalConfig.RPC.ListenAddress = rpc
|
||||
globalConfig.RPC.CORSAllowedOrigins = []string{"https://tendermint.com/"}
|
||||
globalConfig.RPC.GRPCListenAddress = grpc
|
||||
globalConfig.TxIndex.IndexTags = "app.creator,tx.height" // see kvstore application
|
||||
}
|
||||
|
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# XXX: this script is intended to be run from
|
||||
# a fresh Digital Ocean droplet with Ubuntu
|
||||
|
||||
# upon its completion, you must either reset
|
||||
# your terminal or run `source ~/.profile`
|
||||
|
||||
# as written, this script will install
|
||||
# tendermint core from master branch
|
||||
REPO=github.com/tendermint/tendermint
|
||||
|
||||
# change this to a specific release or branch
|
||||
BRANCH=master
|
||||
|
||||
GO_VERSION=1.11.2
|
||||
|
||||
sudo apt-get update -y
|
||||
|
||||
# get and unpack golang
|
||||
curl -O https://storage.googleapis.com/golang/go$GO_VERSION.linux-armv6l.tar.gz
|
||||
tar -xvf go$GO_VERSION.linux-armv6l.tar.gz
|
||||
|
||||
# move go folder and add go binary to path
|
||||
sudo mv go /usr/local
|
||||
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile
|
||||
|
||||
# create the go directory, set GOPATH, and put it on PATH
|
||||
mkdir go
|
||||
echo "export GOPATH=$HOME/go" >> ~/.profile
|
||||
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile
|
||||
source ~/.profile
|
||||
|
||||
# get the code and move into repo
|
||||
go get $REPO
|
||||
cd "$GOPATH/src/$REPO"
|
||||
|
||||
# build & install
|
||||
git checkout $BRANCH
|
||||
# XXX: uncomment if branch isn't master
|
||||
# git fetch origin $BRANCH
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make install
|
||||
|
||||
# the binary is located in $GOPATH/bin
|
||||
# run `source ~/.profile` or reset your terminal
|
||||
# to persist the changes
|
@@ -14,9 +14,6 @@
|
||||
|
||||
# change this to a specific release or branch
|
||||
set BRANCH=master
|
||||
set REPO=github.com/tendermint/tendermint
|
||||
|
||||
set GO_VERSION=1.11.2
|
||||
|
||||
sudo pkg update
|
||||
|
||||
@@ -24,8 +21,8 @@ sudo pkg install -y gmake
|
||||
sudo pkg install -y git
|
||||
|
||||
# get and unpack golang
|
||||
curl -O https://storage.googleapis.com/golang/go$GO_VERSION.freebsd-amd64.tar.gz
|
||||
tar -xvf go$GO_VERSION.freebsd-amd64.tar.gz
|
||||
curl -O https://storage.googleapis.com/golang/go1.11.freebsd-amd64.tar.gz
|
||||
tar -xvf go1.11.freebsd-amd64.tar.gz
|
||||
|
||||
# move go folder and add go binary to path
|
||||
sudo mv go /usr/local
|
||||
@@ -41,6 +38,7 @@ echo "set path=($path $GOPATH/bin)" >> ~/.tcshrc
|
||||
source ~/.tcshrc
|
||||
|
||||
# get the code and move into repo
|
||||
set REPO=github.com/tendermint/tendermint
|
||||
go get $REPO
|
||||
cd "$GOPATH/src/$REPO"
|
||||
|
||||
|
@@ -13,22 +13,20 @@ REPO=github.com/tendermint/tendermint
|
||||
# change this to a specific release or branch
|
||||
BRANCH=master
|
||||
|
||||
GO_VERSION=1.11.2
|
||||
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y make
|
||||
|
||||
# get and unpack golang
|
||||
curl -O https://storage.googleapis.com/golang/go$GO_VERSION.linux-amd64.tar.gz
|
||||
tar -xvf go$GO_VERSION.linux-amd64.tar.gz
|
||||
curl -O https://storage.googleapis.com/golang/go1.11.linux-amd64.tar.gz
|
||||
tar -xvf go1.11.linux-amd64.tar.gz
|
||||
|
||||
# move go folder and add go binary to path
|
||||
sudo mv go /usr/local
|
||||
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile
|
||||
|
||||
# create the go directory, set GOPATH, and put it on PATH
|
||||
mkdir go
|
||||
echo "export GOPATH=$HOME/go" >> ~/.profile
|
||||
# create the goApps directory, set GOPATH, and put it on PATH
|
||||
mkdir goApps
|
||||
echo "export GOPATH=$HOME/goApps" >> ~/.profile
|
||||
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile
|
||||
source ~/.profile
|
||||
|
||||
|
@@ -2,13 +2,13 @@ package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -108,7 +108,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
|
||||
fail.Fail() // XXX
|
||||
|
||||
// Update the state with the block and responses.
|
||||
state, err = updateState(blockExec.logger, state, blockID, &block.Header, abciResponses)
|
||||
state, err = updateState(state, blockID, &block.Header, abciResponses)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Commit failed for application: %v", err)
|
||||
}
|
||||
@@ -180,8 +180,13 @@ func (blockExec *BlockExecutor) Commit(
|
||||
err = blockExec.mempool.Update(
|
||||
block.Height,
|
||||
block.Txs,
|
||||
TxPreCheck(state),
|
||||
TxPostCheck(state),
|
||||
mempool.PreCheckAminoMaxBytes(
|
||||
types.MaxDataBytesUnknownEvidence(
|
||||
state.ConsensusParams.BlockSize.MaxBytes,
|
||||
state.Validators.Size(),
|
||||
),
|
||||
),
|
||||
mempool.PostCheckMaxGas(state.ConsensusParams.BlockSize.MaxGas),
|
||||
)
|
||||
|
||||
return res.Data, err
|
||||
@@ -255,6 +260,12 @@ func execBlockOnProxyApp(
|
||||
|
||||
logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, "invalidTxs", invalidTxs)
|
||||
|
||||
valUpdates := abciResponses.EndBlock.ValidatorUpdates
|
||||
if len(valUpdates) > 0 {
|
||||
// TODO: cleanup the formatting
|
||||
logger.Info("Updates to validators", "updates", valUpdates)
|
||||
}
|
||||
|
||||
return abciResponses, nil
|
||||
}
|
||||
|
||||
@@ -310,16 +321,16 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS
|
||||
// If more or equal than 1/3 of total voting power changed in one block, then
|
||||
// a light client could never prove the transition externally. See
|
||||
// ./lite/doc.go for details on how a light client tracks validators.
|
||||
func updateValidators(currentSet *types.ValidatorSet, abciUpdates []abci.ValidatorUpdate) ([]*types.Validator, error) {
|
||||
func updateValidators(currentSet *types.ValidatorSet, abciUpdates []abci.ValidatorUpdate) error {
|
||||
updates, err := types.PB2TM.ValidatorUpdates(abciUpdates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// these are tendermint types now
|
||||
for _, valUpdate := range updates {
|
||||
if valUpdate.VotingPower < 0 {
|
||||
return nil, fmt.Errorf("Voting power can't be negative %v", valUpdate)
|
||||
return fmt.Errorf("Voting power can't be negative %v", valUpdate)
|
||||
}
|
||||
|
||||
address := valUpdate.Address
|
||||
@@ -328,28 +339,27 @@ func updateValidators(currentSet *types.ValidatorSet, abciUpdates []abci.Validat
|
||||
// remove val
|
||||
_, removed := currentSet.Remove(address)
|
||||
if !removed {
|
||||
return nil, fmt.Errorf("Failed to remove validator %X", address)
|
||||
return fmt.Errorf("Failed to remove validator %X", address)
|
||||
}
|
||||
} else if val == nil {
|
||||
// add val
|
||||
added := currentSet.Add(valUpdate)
|
||||
if !added {
|
||||
return nil, fmt.Errorf("Failed to add new validator %v", valUpdate)
|
||||
return fmt.Errorf("Failed to add new validator %v", valUpdate)
|
||||
}
|
||||
} else {
|
||||
// update val
|
||||
updated := currentSet.Update(valUpdate)
|
||||
if !updated {
|
||||
return nil, fmt.Errorf("Failed to update validator %X to %v", address, valUpdate)
|
||||
return fmt.Errorf("Failed to update validator %X to %v", address, valUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
return updates, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateState returns a new State updated according to the header and responses.
|
||||
func updateState(
|
||||
logger log.Logger,
|
||||
state State,
|
||||
blockID types.BlockID,
|
||||
header *types.Header,
|
||||
@@ -363,14 +373,12 @@ func updateState(
|
||||
// Update the validator set with the latest abciResponses.
|
||||
lastHeightValsChanged := state.LastHeightValidatorsChanged
|
||||
if len(abciResponses.EndBlock.ValidatorUpdates) > 0 {
|
||||
validatorUpdates, err := updateValidators(nValSet, abciResponses.EndBlock.ValidatorUpdates)
|
||||
err := updateValidators(nValSet, abciResponses.EndBlock.ValidatorUpdates)
|
||||
if err != nil {
|
||||
return state, fmt.Errorf("Error changing validator set: %v", err)
|
||||
}
|
||||
// Change results from this height but only applies to the next next height.
|
||||
lastHeightValsChanged = header.Height + 1 + 1
|
||||
|
||||
logger.Info("Updates to validators", "updates", makeValidatorUpdatesLogString(validatorUpdates))
|
||||
}
|
||||
|
||||
// Update validator accums and set state variables.
|
||||
@@ -464,13 +472,3 @@ func ExecCommitBlock(
|
||||
// ResponseCommit has no error or log, just data
|
||||
return res.Data, nil
|
||||
}
|
||||
|
||||
// Make pretty string for validatorUpdates logging
|
||||
func makeValidatorUpdatesLogString(vals []*types.Validator) string {
|
||||
chunks := make([]string, len(vals))
|
||||
for i, val := range vals {
|
||||
chunks[i] = fmt.Sprintf("%s:%d", val.Address, val.VotingPower)
|
||||
}
|
||||
|
||||
return strings.Join(chunks, ",")
|
||||
}
|
||||
|
@@ -218,7 +218,7 @@ func TestUpdateValidators(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := updateValidators(tc.currentSet, tc.abciUpdates)
|
||||
err := updateValidators(tc.currentSet, tc.abciUpdates)
|
||||
if tc.shouldErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
|
@@ -3,7 +3,6 @@ package state
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -229,7 +228,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
|
||||
power++
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power)
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
state, err = updateState(state, blockID, &header, responses)
|
||||
assert.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
@@ -281,7 +280,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) {
|
||||
|
||||
// Save state etc.
|
||||
var err error
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
state, err = updateState(state, blockID, &header, responses)
|
||||
require.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
@@ -360,7 +359,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
|
||||
cp = params[changeIndex]
|
||||
}
|
||||
header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp)
|
||||
state, err = updateState(log.TestingLogger(), state, blockID, &header, responses)
|
||||
state, err = updateState(state, blockID, &header, responses)
|
||||
|
||||
require.Nil(t, err)
|
||||
nextHeight := state.LastBlockHeight + 1
|
||||
|
@@ -96,7 +96,7 @@ func saveState(db dbm.DB, state State, key []byte) {
|
||||
saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators)
|
||||
// Save next consensus params.
|
||||
saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams)
|
||||
db.SetSync(key, state.Bytes())
|
||||
db.SetSync(stateKey, state.Bytes())
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
@@ -1,22 +1,15 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// TxPreCheck returns a function to filter transactions before processing.
|
||||
// The function limits the size of a transaction to the block's maximum data size.
|
||||
func TxPreCheck(state State) mempl.PreCheckFunc {
|
||||
// TxFilter returns a function to filter transactions. The function limits the
|
||||
// size of a transaction to the maximum block's data size.
|
||||
func TxFilter(state State) func(tx types.Tx) bool {
|
||||
maxDataBytes := types.MaxDataBytesUnknownEvidence(
|
||||
state.ConsensusParams.BlockSize.MaxBytes,
|
||||
state.Validators.Size(),
|
||||
)
|
||||
return mempl.PreCheckAminoMaxBytes(maxDataBytes)
|
||||
}
|
||||
|
||||
// TxPostCheck returns a function to filter transactions after processing.
|
||||
// The function limits the gas wanted by a transaction to the block's maximum total gas.
|
||||
func TxPostCheck(state State) mempl.PostCheckFunc {
|
||||
return mempl.PostCheckMaxGas(state.ConsensusParams.BlockSize.MaxGas)
|
||||
return func(tx types.Tx) bool { return int64(len(tx)) <= maxDataBytes }
|
||||
}
|
||||
|
@@ -18,18 +18,12 @@ func TestTxFilter(t *testing.T) {
|
||||
genDoc := randomGenesisDoc()
|
||||
genDoc.ConsensusParams.BlockSize.MaxBytes = 3000
|
||||
|
||||
// Max size of Txs is much smaller than size of block,
|
||||
// since we need to account for commits and evidence.
|
||||
testCases := []struct {
|
||||
tx types.Tx
|
||||
isErr bool
|
||||
tx types.Tx
|
||||
isTxValid bool
|
||||
}{
|
||||
{types.Tx(cmn.RandBytes(250)), false},
|
||||
{types.Tx(cmn.RandBytes(1809)), false},
|
||||
{types.Tx(cmn.RandBytes(1810)), false},
|
||||
{types.Tx(cmn.RandBytes(1811)), true},
|
||||
{types.Tx(cmn.RandBytes(1812)), true},
|
||||
{types.Tx(cmn.RandBytes(3000)), true},
|
||||
{types.Tx(cmn.RandBytes(250)), true},
|
||||
{types.Tx(cmn.RandBytes(3001)), false},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
@@ -37,12 +31,8 @@ func TestTxFilter(t *testing.T) {
|
||||
state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
require.NoError(t, err)
|
||||
|
||||
f := TxPreCheck(state)
|
||||
if tc.isErr {
|
||||
assert.NotNil(t, f(tc.tx), "#%v", i)
|
||||
} else {
|
||||
assert.Nil(t, f(tc.tx), "#%v", i)
|
||||
}
|
||||
f := TxFilter(state)
|
||||
assert.Equal(t, tc.isTxValid, f(tc.tx), "#%v", i)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -48,13 +48,13 @@ Examples:
|
||||
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
}
|
||||
|
||||
monitor := startMonitor(flag.Arg(0))
|
||||
m := startMonitor(flag.Arg(0))
|
||||
|
||||
listener := startRPC(listenAddr, monitor, logger)
|
||||
startRPC(listenAddr, m, logger)
|
||||
|
||||
var ton *Ton
|
||||
if !noton {
|
||||
ton = NewTon(monitor)
|
||||
ton = NewTon(m)
|
||||
ton.Start()
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ Examples:
|
||||
if !noton {
|
||||
ton.Stop()
|
||||
}
|
||||
monitor.Stop()
|
||||
listener.Close()
|
||||
m.Stop()
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -10,19 +9,16 @@ import (
|
||||
monitor "github.com/tendermint/tendermint/tools/tm-monitor/monitor"
|
||||
)
|
||||
|
||||
func startRPC(listenAddr string, m *monitor.Monitor, logger log.Logger) net.Listener {
|
||||
func startRPC(listenAddr string, m *monitor.Monitor, logger log.Logger) {
|
||||
routes := routes(m)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
wm := rpc.NewWebsocketManager(routes, nil)
|
||||
mux.HandleFunc("/websocket", wm.WebsocketHandler)
|
||||
rpc.RegisterRPCFuncs(mux, routes, cdc, logger)
|
||||
listener, err := rpc.Listen(listenAddr, rpc.Config{})
|
||||
if err != nil {
|
||||
if _, err := rpc.StartHTTPServer(listenAddr, mux, logger, rpc.Config{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go rpc.StartHTTPServer(listener, mux, logger)
|
||||
return listener
|
||||
}
|
||||
|
||||
func routes(m *monitor.Monitor) map[string]*rpc.RPCFunc {
|
||||
|
@@ -21,8 +21,6 @@ const (
|
||||
|
||||
// MaxAminoOverheadForBlock - maximum amino overhead to encode a block (up to
|
||||
// MaxBlockSizeBytes in size) not including it's parts except Data.
|
||||
// This means it also excludes the overhead for individual transactions.
|
||||
// To compute individual transactions' overhead use types.ComputeAminoOverhead(tx types.Tx, fieldNum int).
|
||||
//
|
||||
// Uvarint length of MaxBlockSizeBytes: 4 bytes
|
||||
// 2 fields (2 embedded): 2 bytes
|
||||
|
@@ -250,7 +250,7 @@ func TestMaxHeaderBytes(t *testing.T) {
|
||||
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
|
||||
|
||||
h := Header{
|
||||
Version: version.Consensus{Block: math.MaxInt64, App: math.MaxInt64},
|
||||
Version: version.Consensus{math.MaxInt64, math.MaxInt64},
|
||||
ChainID: maxChainID,
|
||||
Height: math.MaxInt64,
|
||||
Time: timestamp,
|
||||
|
@@ -136,11 +136,11 @@ func (b *EventBus) PublishEventTimeoutWait(data EventDataRoundState) error {
|
||||
return b.Publish(EventTimeoutWait, data)
|
||||
}
|
||||
|
||||
func (b *EventBus) PublishEventNewRound(data EventDataNewRound) error {
|
||||
func (b *EventBus) PublishEventNewRound(data EventDataRoundState) error {
|
||||
return b.Publish(EventNewRound, data)
|
||||
}
|
||||
|
||||
func (b *EventBus) PublishEventCompleteProposal(data EventDataCompleteProposal) error {
|
||||
func (b *EventBus) PublishEventCompleteProposal(data EventDataRoundState) error {
|
||||
return b.Publish(EventCompleteProposal, data)
|
||||
}
|
||||
|
||||
|
@@ -96,9 +96,9 @@ func TestEventBusPublish(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventTimeoutWait(EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventNewRound(EventDataNewRound{})
|
||||
err = eventBus.PublishEventNewRound(EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{})
|
||||
err = eventBus.PublishEventCompleteProposal(EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
err = eventBus.PublishEventPolka(EventDataRoundState{})
|
||||
require.NoError(t, err)
|
||||
|
@@ -43,8 +43,6 @@ func RegisterEventDatas(cdc *amino.Codec) {
|
||||
cdc.RegisterConcrete(EventDataNewBlockHeader{}, "tendermint/event/NewBlockHeader", nil)
|
||||
cdc.RegisterConcrete(EventDataTx{}, "tendermint/event/Tx", nil)
|
||||
cdc.RegisterConcrete(EventDataRoundState{}, "tendermint/event/RoundState", nil)
|
||||
cdc.RegisterConcrete(EventDataNewRound{}, "tendermint/event/NewRound", nil)
|
||||
cdc.RegisterConcrete(EventDataCompleteProposal{}, "tendermint/event/CompleteProposal", nil)
|
||||
cdc.RegisterConcrete(EventDataVote{}, "tendermint/event/Vote", nil)
|
||||
cdc.RegisterConcrete(EventDataProposalHeartbeat{}, "tendermint/event/ProposalHeartbeat", nil)
|
||||
cdc.RegisterConcrete(EventDataValidatorSetUpdates{}, "tendermint/event/ValidatorSetUpdates", nil)
|
||||
@@ -82,27 +80,6 @@ type EventDataRoundState struct {
|
||||
RoundState interface{} `json:"-"`
|
||||
}
|
||||
|
||||
type ValidatorInfo struct {
|
||||
Address Address `json:"address"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
type EventDataNewRound struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Step string `json:"step"`
|
||||
|
||||
Proposer ValidatorInfo `json:"proposer"`
|
||||
}
|
||||
|
||||
type EventDataCompleteProposal struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Step string `json:"step"`
|
||||
|
||||
BlockID BlockID `json:"block_id"`
|
||||
}
|
||||
|
||||
type EventDataVote struct {
|
||||
Vote *Vote
|
||||
}
|
||||
|
@@ -61,7 +61,7 @@ func TestEvidence(t *testing.T) {
|
||||
{vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round
|
||||
{vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step
|
||||
{vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator
|
||||
{vote1, badVote, false}, // signed by wrong key
|
||||
{vote1, badVote, false}, // signed by wrong key
|
||||
}
|
||||
|
||||
pubKey := val.GetPubKey()
|
||||
@@ -121,38 +121,3 @@ func randomDuplicatedVoteEvidence() *DuplicateVoteEvidence {
|
||||
VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2),
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuplicateVoteEvidenceValidation(t *testing.T) {
|
||||
val := NewMockPV()
|
||||
blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash")))
|
||||
blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash")))
|
||||
const chainID = "mychain"
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateEvidence func(*DuplicateVoteEvidence)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false},
|
||||
{"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true},
|
||||
{"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true},
|
||||
{"Nil votes", func(ev *DuplicateVoteEvidence) {
|
||||
ev.VoteA = nil
|
||||
ev.VoteB = nil
|
||||
}, true},
|
||||
{"Invalid vote type", func(ev *DuplicateVoteEvidence) {
|
||||
ev.VoteA = makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0, blockID2)
|
||||
}, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
ev := &DuplicateVoteEvidence{
|
||||
PubKey: secp256k1.GenPrivKey().PubKey(),
|
||||
VoteA: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID),
|
||||
VoteB: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID2),
|
||||
}
|
||||
tc.malleateEvidence(ev)
|
||||
assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -19,9 +19,6 @@ const (
|
||||
|
||||
//------------------------------------------------------------
|
||||
// core types for a genesis definition
|
||||
// NOTE: any changes to the genesis definition should
|
||||
// be reflected in the documentation:
|
||||
// docs/tendermint-core/using-tendermint.md
|
||||
|
||||
// GenesisValidator is an initial validator.
|
||||
type GenesisValidator struct {
|
||||
|
@@ -3,10 +3,8 @@ package types
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
func TestHeartbeatCopy(t *testing.T) {
|
||||
@@ -60,45 +58,3 @@ func TestHeartbeatWriteSignBytes(t *testing.T) {
|
||||
require.Equal(t, string(signBytes), "null")
|
||||
})
|
||||
}
|
||||
|
||||
func TestHeartbeatValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleateHeartBeat func(*Heartbeat)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good HeartBeat", func(hb *Heartbeat) {}, false},
|
||||
{"Invalid address size", func(hb *Heartbeat) {
|
||||
hb.ValidatorAddress = nil
|
||||
}, true},
|
||||
{"Negative validator index", func(hb *Heartbeat) {
|
||||
hb.ValidatorIndex = -1
|
||||
}, true},
|
||||
{"Negative height", func(hb *Heartbeat) {
|
||||
hb.Height = -1
|
||||
}, true},
|
||||
{"Negative round", func(hb *Heartbeat) {
|
||||
hb.Round = -1
|
||||
}, true},
|
||||
{"Negative sequence", func(hb *Heartbeat) {
|
||||
hb.Sequence = -1
|
||||
}, true},
|
||||
{"Missing signature", func(hb *Heartbeat) {
|
||||
hb.Signature = nil
|
||||
}, true},
|
||||
{"Signature too big", func(hb *Heartbeat) {
|
||||
hb.Signature = make([]byte, MaxSignatureSize+1)
|
||||
}, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
hb := &Heartbeat{
|
||||
ValidatorAddress: secp256k1.GenPrivKey().PubKey().Address(),
|
||||
Signature: make([]byte, 4),
|
||||
ValidatorIndex: 1, Height: 10, Round: 1}
|
||||
|
||||
tc.malleateHeartBeat(hb)
|
||||
assert.Equal(t, tc.expectErr, hb.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -83,46 +83,3 @@ func TestWrongProof(t *testing.T) {
|
||||
t.Errorf("Expected to fail adding a part with bad bytes.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPartSetHeaderValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleatePartSetHeader func(*PartSetHeader)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good PartSet", func(psHeader *PartSetHeader) {}, false},
|
||||
{"Negative Total", func(psHeader *PartSetHeader) { psHeader.Total = -2 }, true},
|
||||
{"Invalid Hash", func(psHeader *PartSetHeader) { psHeader.Hash = make([]byte, 1) }, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
data := cmn.RandBytes(testPartSize * 100)
|
||||
ps := NewPartSetFromData(data, testPartSize)
|
||||
psHeader := ps.Header()
|
||||
tc.malleatePartSetHeader(&psHeader)
|
||||
assert.Equal(t, tc.expectErr, psHeader.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPartValidateBasic(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
malleatePart func(*Part)
|
||||
expectErr bool
|
||||
}{
|
||||
{"Good Part", func(pt *Part) {}, false},
|
||||
{"Negative index", func(pt *Part) { pt.Index = -1 }, true},
|
||||
{"Too big part", func(pt *Part) { pt.Bytes = make([]byte, BlockPartSizeBytes+1) }, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
data := cmn.RandBytes(testPartSize * 100)
|
||||
ps := NewPartSetFromData(data, testPartSize)
|
||||
part := ps.GetPart(0)
|
||||
tc.malleatePart(part)
|
||||
assert.Equal(t, tc.expectErr, part.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user